]>
Commit | Line | Data |
---|---|---|
1 | // © 2016 and later: Unicode, Inc. and others. | |
2 | // License & terms of use: http://www.unicode.org/copyright.html | |
3 | /* | |
4 | ******************************************************************************* | |
5 | * Copyright (C) 1996-2016, International Business Machines | |
6 | * Corporation and others. All Rights Reserved. | |
7 | ******************************************************************************* | |
8 | */ | |
9 | ||
10 | #include "utypeinfo.h" // for 'typeid' to work | |
11 | ||
12 | #include "unicode/utypes.h" | |
13 | ||
14 | #if !UCONFIG_NO_FORMATTING | |
15 | ||
16 | #include <stdlib.h> // Apple addition for uacal_getDayPeriod | |
17 | ||
18 | #include "unicode/ucal.h" | |
19 | #include "unicode/uloc.h" | |
20 | #include "unicode/calendar.h" | |
21 | #include "unicode/timezone.h" | |
22 | #include "unicode/gregocal.h" | |
23 | #include "unicode/simpletz.h" | |
24 | #include "unicode/ustring.h" | |
25 | #include "unicode/strenum.h" | |
26 | #include "unicode/localpointer.h" | |
27 | #include "cmemory.h" | |
28 | #include "cstring.h" | |
29 | #include "ustrenum.h" | |
30 | #include "uenumimp.h" | |
31 | #include "ulist.h" | |
32 | #include "ulocimp.h" | |
33 | ||
34 | U_NAMESPACE_USE | |
35 | ||
36 | static TimeZone* | |
37 | _createTimeZone(const UChar* zoneID, int32_t len, UErrorCode* ec) { | |
38 | TimeZone* zone = NULL; | |
39 | if (ec != NULL && U_SUCCESS(*ec)) { | |
40 | // Note that if zoneID is invalid, we get back GMT. This odd | |
41 | // behavior is by design and goes back to the JDK. The only | |
42 | // failure we will see is a memory allocation failure. | |
43 | int32_t l = (len<0 ? u_strlen(zoneID) : len); | |
44 | UnicodeString zoneStrID; | |
45 | zoneStrID.setTo((UBool)(len < 0), zoneID, l); /* temporary read-only alias */ | |
46 | zone = TimeZone::createTimeZone(zoneStrID); | |
47 | if (zone == NULL) { | |
48 | *ec = U_MEMORY_ALLOCATION_ERROR; | |
49 | } | |
50 | } | |
51 | return zone; | |
52 | } | |
53 | ||
54 | U_CAPI UEnumeration* U_EXPORT2 | |
55 | ucal_openTimeZoneIDEnumeration(USystemTimeZoneType zoneType, const char* region, | |
56 | const int32_t* rawOffset, UErrorCode* ec) { | |
57 | return uenum_openFromStringEnumeration(TimeZone::createTimeZoneIDEnumeration( | |
58 | zoneType, region, rawOffset, *ec), ec); | |
59 | } | |
60 | ||
61 | U_CAPI UEnumeration* U_EXPORT2 | |
62 | ucal_openTimeZones(UErrorCode* ec) { | |
63 | return uenum_openFromStringEnumeration(TimeZone::createEnumeration(), ec); | |
64 | } | |
65 | ||
66 | U_CAPI UEnumeration* U_EXPORT2 | |
67 | ucal_openCountryTimeZones(const char* country, UErrorCode* ec) { | |
68 | return uenum_openFromStringEnumeration(TimeZone::createEnumeration(country), ec); | |
69 | } | |
70 | ||
71 | U_CAPI int32_t U_EXPORT2 | |
72 | ucal_getDefaultTimeZone(UChar* result, int32_t resultCapacity, UErrorCode* ec) { | |
73 | int32_t len = 0; | |
74 | if (ec != NULL && U_SUCCESS(*ec)) { | |
75 | TimeZone* zone = TimeZone::createDefault(); | |
76 | if (zone == NULL) { | |
77 | *ec = U_MEMORY_ALLOCATION_ERROR; | |
78 | } else { | |
79 | UnicodeString id; | |
80 | zone->getID(id); | |
81 | delete zone; | |
82 | len = id.extract(result, resultCapacity, *ec); | |
83 | } | |
84 | } | |
85 | return len; | |
86 | } | |
87 | ||
88 | U_CAPI void U_EXPORT2 | |
89 | ucal_setDefaultTimeZone(const UChar* zoneID, UErrorCode* ec) { | |
90 | TimeZone* zone = _createTimeZone(zoneID, -1, ec); | |
91 | if (zone != NULL) { | |
92 | TimeZone::adoptDefault(zone); | |
93 | } | |
94 | } | |
95 | ||
96 | U_DRAFT int32_t U_EXPORT2 | |
97 | ucal_getHostTimeZone(UChar* result, int32_t resultCapacity, UErrorCode* ec) { | |
98 | int32_t len = 0; | |
99 | if (ec != NULL && U_SUCCESS(*ec)) { | |
100 | TimeZone *zone = TimeZone::detectHostTimeZone(); | |
101 | if (zone == NULL) { | |
102 | *ec = U_MEMORY_ALLOCATION_ERROR; | |
103 | } else { | |
104 | UnicodeString id; | |
105 | zone->getID(id); | |
106 | delete zone; | |
107 | len = id.extract(result, resultCapacity, *ec); | |
108 | } | |
109 | } | |
110 | return len; | |
111 | } | |
112 | ||
113 | U_CAPI int32_t U_EXPORT2 | |
114 | ucal_getDSTSavings(const UChar* zoneID, UErrorCode* ec) { | |
115 | int32_t result = 0; | |
116 | TimeZone* zone = _createTimeZone(zoneID, -1, ec); | |
117 | if (U_SUCCESS(*ec)) { | |
118 | SimpleTimeZone* stz = dynamic_cast<SimpleTimeZone*>(zone); | |
119 | if (stz != NULL) { | |
120 | result = stz->getDSTSavings(); | |
121 | } else { | |
122 | // Since there is no getDSTSavings on TimeZone, we use a | |
123 | // heuristic: Starting with the current time, march | |
124 | // forwards for one year, looking for DST savings. | |
125 | // Stepping by weeks is sufficient. | |
126 | UDate d = Calendar::getNow(); | |
127 | for (int32_t i=0; i<53; ++i, d+=U_MILLIS_PER_DAY*7.0) { | |
128 | int32_t raw, dst; | |
129 | zone->getOffset(d, FALSE, raw, dst, *ec); | |
130 | if (U_FAILURE(*ec)) { | |
131 | break; | |
132 | } else if (dst != 0) { | |
133 | result = dst; | |
134 | break; | |
135 | } | |
136 | } | |
137 | } | |
138 | } | |
139 | delete zone; | |
140 | return result; | |
141 | } | |
142 | ||
143 | U_CAPI UDate U_EXPORT2 | |
144 | ucal_getNow() | |
145 | { | |
146 | ||
147 | return Calendar::getNow(); | |
148 | } | |
149 | ||
150 | #define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY) | |
151 | ||
152 | U_CAPI UCalendar* U_EXPORT2 | |
153 | ucal_open( const UChar* zoneID, | |
154 | int32_t len, | |
155 | const char* locale, | |
156 | UCalendarType caltype, | |
157 | UErrorCode* status) | |
158 | { | |
159 | ||
160 | if(U_FAILURE(*status)) return 0; | |
161 | ||
162 | LocalPointer<TimeZone> zone( (zoneID==NULL) ? TimeZone::createDefault() | |
163 | : _createTimeZone(zoneID, len, status), *status); | |
164 | ||
165 | if (U_FAILURE(*status)) { | |
166 | return NULL; | |
167 | } | |
168 | ||
169 | if ( caltype == UCAL_GREGORIAN ) { | |
170 | char localeBuf[ULOC_LOCALE_IDENTIFIER_CAPACITY]; | |
171 | if ( locale == NULL ) { | |
172 | locale = uloc_getDefault(); | |
173 | } | |
174 | uprv_strncpy(localeBuf, locale, ULOC_LOCALE_IDENTIFIER_CAPACITY); | |
175 | uloc_setKeywordValue("calendar", "gregorian", localeBuf, ULOC_LOCALE_IDENTIFIER_CAPACITY, status); | |
176 | if (U_FAILURE(*status)) { | |
177 | return NULL; | |
178 | } | |
179 | return (UCalendar*)Calendar::createInstance(zone.orphan(), Locale(localeBuf), *status); | |
180 | } | |
181 | return (UCalendar*)Calendar::createInstance(zone.orphan(), Locale(locale), *status); | |
182 | } | |
183 | ||
184 | U_CAPI void U_EXPORT2 | |
185 | ucal_close(UCalendar *cal) | |
186 | { | |
187 | ||
188 | delete (Calendar*) cal; | |
189 | } | |
190 | ||
191 | U_CAPI UCalendar* U_EXPORT2 | |
192 | ucal_clone(const UCalendar* cal, | |
193 | UErrorCode* status) | |
194 | { | |
195 | if(U_FAILURE(*status)) return 0; | |
196 | ||
197 | Calendar* res = ((Calendar*)cal)->clone(); | |
198 | ||
199 | if(res == 0) { | |
200 | *status = U_MEMORY_ALLOCATION_ERROR; | |
201 | return 0; | |
202 | } | |
203 | ||
204 | return (UCalendar*) res; | |
205 | } | |
206 | ||
207 | U_CAPI void U_EXPORT2 | |
208 | ucal_setTimeZone( UCalendar* cal, | |
209 | const UChar* zoneID, | |
210 | int32_t len, | |
211 | UErrorCode *status) | |
212 | { | |
213 | ||
214 | if(U_FAILURE(*status)) | |
215 | return; | |
216 | ||
217 | TimeZone* zone; | |
218 | if (zoneID==NULL) { | |
219 | zone = TimeZone::createDefault(); | |
220 | } else { | |
221 | UnicodeString zoneStrID, id; | |
222 | zoneStrID.setTo(len < 0, zoneID, len); /* aliasing assignment, avoids copy */ | |
223 | ((Calendar*)cal)->getTimeZone().getID(id); | |
224 | if (id == zoneStrID) { | |
225 | return; | |
226 | } | |
227 | zone = TimeZone::createTimeZone(zoneStrID); | |
228 | } | |
229 | ||
230 | if (zone != NULL) { | |
231 | ((Calendar*)cal)->adoptTimeZone(zone); | |
232 | } else { | |
233 | *status = U_MEMORY_ALLOCATION_ERROR; | |
234 | } | |
235 | } | |
236 | ||
237 | U_CAPI int32_t U_EXPORT2 | |
238 | ucal_getTimeZoneID(const UCalendar *cal, | |
239 | UChar *result, | |
240 | int32_t resultLength, | |
241 | UErrorCode *status) | |
242 | { | |
243 | if (U_FAILURE(*status)) { | |
244 | return 0; | |
245 | } | |
246 | const TimeZone& tz = ((Calendar*)cal)->getTimeZone(); | |
247 | UnicodeString id; | |
248 | tz.getID(id); | |
249 | return id.extract(result, resultLength, *status); | |
250 | } | |
251 | ||
252 | U_CAPI int32_t U_EXPORT2 | |
253 | ucal_getTimeZoneDisplayName(const UCalendar* cal, | |
254 | UCalendarDisplayNameType type, | |
255 | const char *locale, | |
256 | UChar* result, | |
257 | int32_t resultLength, | |
258 | UErrorCode* status) | |
259 | { | |
260 | ||
261 | if(U_FAILURE(*status)) return -1; | |
262 | ||
263 | const TimeZone& tz = ((Calendar*)cal)->getTimeZone(); | |
264 | UnicodeString id; | |
265 | if(!(result==NULL && resultLength==0)) { | |
266 | // NULL destination for pure preflighting: empty dummy string | |
267 | // otherwise, alias the destination buffer | |
268 | id.setTo(result, 0, resultLength); | |
269 | } | |
270 | ||
271 | switch(type) { | |
272 | case UCAL_STANDARD: | |
273 | tz.getDisplayName(FALSE, TimeZone::LONG, Locale(locale), id); | |
274 | break; | |
275 | ||
276 | case UCAL_SHORT_STANDARD: | |
277 | tz.getDisplayName(FALSE, TimeZone::SHORT, Locale(locale), id); | |
278 | break; | |
279 | ||
280 | case UCAL_DST: | |
281 | tz.getDisplayName(TRUE, TimeZone::LONG, Locale(locale), id); | |
282 | break; | |
283 | ||
284 | case UCAL_SHORT_DST: | |
285 | tz.getDisplayName(TRUE, TimeZone::SHORT, Locale(locale), id); | |
286 | break; | |
287 | } | |
288 | ||
289 | return id.extract(result, resultLength, *status); | |
290 | } | |
291 | ||
292 | U_CAPI UBool U_EXPORT2 | |
293 | ucal_inDaylightTime( const UCalendar* cal, | |
294 | UErrorCode* status ) | |
295 | { | |
296 | ||
297 | if(U_FAILURE(*status)) return (UBool) -1; | |
298 | return ((Calendar*)cal)->inDaylightTime(*status); | |
299 | } | |
300 | ||
301 | U_CAPI void U_EXPORT2 | |
302 | ucal_setGregorianChange(UCalendar *cal, UDate date, UErrorCode *pErrorCode) { | |
303 | if(U_FAILURE(*pErrorCode)) { | |
304 | return; | |
305 | } | |
306 | Calendar *cpp_cal = (Calendar *)cal; | |
307 | GregorianCalendar *gregocal = dynamic_cast<GregorianCalendar *>(cpp_cal); | |
308 | // Not if(gregocal == NULL) { | |
309 | // because we really want to work only with a GregorianCalendar, not with | |
310 | // its subclasses like BuddhistCalendar. | |
311 | if (cpp_cal == NULL) { | |
312 | // We normally don't check "this" pointers for NULL, but this here avoids | |
313 | // compiler-generated exception-throwing code in case cal == NULL. | |
314 | *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; | |
315 | return; | |
316 | } | |
317 | if(typeid(*cpp_cal) != typeid(GregorianCalendar)) { | |
318 | *pErrorCode = U_UNSUPPORTED_ERROR; | |
319 | return; | |
320 | } | |
321 | gregocal->setGregorianChange(date, *pErrorCode); | |
322 | } | |
323 | ||
324 | U_CAPI UDate U_EXPORT2 | |
325 | ucal_getGregorianChange(const UCalendar *cal, UErrorCode *pErrorCode) { | |
326 | if(U_FAILURE(*pErrorCode)) { | |
327 | return (UDate)0; | |
328 | } | |
329 | const Calendar *cpp_cal = (const Calendar *)cal; | |
330 | const GregorianCalendar *gregocal = dynamic_cast<const GregorianCalendar *>(cpp_cal); | |
331 | // Not if(gregocal == NULL) { | |
332 | // see comments in ucal_setGregorianChange(). | |
333 | if (cpp_cal == NULL) { | |
334 | // We normally don't check "this" pointers for NULL, but this here avoids | |
335 | // compiler-generated exception-throwing code in case cal == NULL. | |
336 | *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; | |
337 | return (UDate)0; | |
338 | } | |
339 | if(typeid(*cpp_cal) != typeid(GregorianCalendar)) { | |
340 | *pErrorCode = U_UNSUPPORTED_ERROR; | |
341 | return (UDate)0; | |
342 | } | |
343 | return gregocal->getGregorianChange(); | |
344 | } | |
345 | ||
346 | U_CAPI int32_t U_EXPORT2 | |
347 | ucal_getAttribute( const UCalendar* cal, | |
348 | UCalendarAttribute attr) | |
349 | { | |
350 | ||
351 | switch(attr) { | |
352 | case UCAL_LENIENT: | |
353 | return ((Calendar*)cal)->isLenient(); | |
354 | ||
355 | case UCAL_FIRST_DAY_OF_WEEK: | |
356 | return ((Calendar*)cal)->getFirstDayOfWeek(); | |
357 | ||
358 | case UCAL_MINIMAL_DAYS_IN_FIRST_WEEK: | |
359 | return ((Calendar*)cal)->getMinimalDaysInFirstWeek(); | |
360 | ||
361 | case UCAL_REPEATED_WALL_TIME: | |
362 | return ((Calendar*)cal)->getRepeatedWallTimeOption(); | |
363 | ||
364 | case UCAL_SKIPPED_WALL_TIME: | |
365 | return ((Calendar*)cal)->getSkippedWallTimeOption(); | |
366 | ||
367 | default: | |
368 | break; | |
369 | } | |
370 | return -1; | |
371 | } | |
372 | ||
373 | U_CAPI void U_EXPORT2 | |
374 | ucal_setAttribute( UCalendar* cal, | |
375 | UCalendarAttribute attr, | |
376 | int32_t newValue) | |
377 | { | |
378 | ||
379 | switch(attr) { | |
380 | case UCAL_LENIENT: | |
381 | ((Calendar*)cal)->setLenient((UBool)newValue); | |
382 | break; | |
383 | ||
384 | case UCAL_FIRST_DAY_OF_WEEK: | |
385 | ((Calendar*)cal)->setFirstDayOfWeek((UCalendarDaysOfWeek)newValue); | |
386 | break; | |
387 | ||
388 | case UCAL_MINIMAL_DAYS_IN_FIRST_WEEK: | |
389 | ((Calendar*)cal)->setMinimalDaysInFirstWeek((uint8_t)newValue); | |
390 | break; | |
391 | ||
392 | case UCAL_REPEATED_WALL_TIME: | |
393 | ((Calendar*)cal)->setRepeatedWallTimeOption((UCalendarWallTimeOption)newValue); | |
394 | break; | |
395 | ||
396 | case UCAL_SKIPPED_WALL_TIME: | |
397 | ((Calendar*)cal)->setSkippedWallTimeOption((UCalendarWallTimeOption)newValue); | |
398 | break; | |
399 | } | |
400 | } | |
401 | ||
402 | U_CAPI const char* U_EXPORT2 | |
403 | ucal_getAvailable(int32_t index) | |
404 | { | |
405 | ||
406 | return uloc_getAvailable(index); | |
407 | } | |
408 | ||
409 | U_CAPI int32_t U_EXPORT2 | |
410 | ucal_countAvailable() | |
411 | { | |
412 | ||
413 | return uloc_countAvailable(); | |
414 | } | |
415 | ||
416 | U_CAPI UDate U_EXPORT2 | |
417 | ucal_getMillis( const UCalendar* cal, | |
418 | UErrorCode* status) | |
419 | { | |
420 | ||
421 | if(U_FAILURE(*status)) return (UDate) 0; | |
422 | ||
423 | return ((Calendar*)cal)->getTime(*status); | |
424 | } | |
425 | ||
426 | U_CAPI void U_EXPORT2 | |
427 | ucal_setMillis( UCalendar* cal, | |
428 | UDate dateTime, | |
429 | UErrorCode* status ) | |
430 | { | |
431 | if(U_FAILURE(*status)) return; | |
432 | ||
433 | ((Calendar*)cal)->setTime(dateTime, *status); | |
434 | } | |
435 | ||
436 | // TBD: why does this take an UErrorCode? | |
437 | U_CAPI void U_EXPORT2 | |
438 | ucal_setDate( UCalendar* cal, | |
439 | int32_t year, | |
440 | int32_t month, | |
441 | int32_t date, | |
442 | UErrorCode *status) | |
443 | { | |
444 | ||
445 | if(U_FAILURE(*status)) return; | |
446 | ||
447 | ((Calendar*)cal)->set(year, month, date); | |
448 | } | |
449 | ||
450 | // TBD: why does this take an UErrorCode? | |
451 | U_CAPI void U_EXPORT2 | |
452 | ucal_setDateTime( UCalendar* cal, | |
453 | int32_t year, | |
454 | int32_t month, | |
455 | int32_t date, | |
456 | int32_t hour, | |
457 | int32_t minute, | |
458 | int32_t second, | |
459 | UErrorCode *status) | |
460 | { | |
461 | if(U_FAILURE(*status)) return; | |
462 | ||
463 | ((Calendar*)cal)->set(year, month, date, hour, minute, second); | |
464 | } | |
465 | ||
466 | U_CAPI UBool U_EXPORT2 | |
467 | ucal_equivalentTo( const UCalendar* cal1, | |
468 | const UCalendar* cal2) | |
469 | { | |
470 | ||
471 | return ((Calendar*)cal1)->isEquivalentTo(*((Calendar*)cal2)); | |
472 | } | |
473 | ||
474 | U_CAPI void U_EXPORT2 | |
475 | ucal_add( UCalendar* cal, | |
476 | UCalendarDateFields field, | |
477 | int32_t amount, | |
478 | UErrorCode* status) | |
479 | { | |
480 | ||
481 | if(U_FAILURE(*status)) return; | |
482 | ||
483 | ((Calendar*)cal)->add(field, amount, *status); | |
484 | } | |
485 | ||
486 | U_CAPI void U_EXPORT2 | |
487 | ucal_roll( UCalendar* cal, | |
488 | UCalendarDateFields field, | |
489 | int32_t amount, | |
490 | UErrorCode* status) | |
491 | { | |
492 | ||
493 | if(U_FAILURE(*status)) return; | |
494 | ||
495 | ((Calendar*)cal)->roll(field, amount, *status); | |
496 | } | |
497 | ||
498 | U_CAPI int32_t U_EXPORT2 | |
499 | ucal_get( const UCalendar* cal, | |
500 | UCalendarDateFields field, | |
501 | UErrorCode* status ) | |
502 | { | |
503 | ||
504 | if(U_FAILURE(*status)) return -1; | |
505 | ||
506 | return ((Calendar*)cal)->get(field, *status); | |
507 | } | |
508 | ||
509 | U_CAPI void U_EXPORT2 | |
510 | ucal_set( UCalendar* cal, | |
511 | UCalendarDateFields field, | |
512 | int32_t value) | |
513 | { | |
514 | ||
515 | ((Calendar*)cal)->set(field, value); | |
516 | } | |
517 | ||
518 | U_CAPI UBool U_EXPORT2 | |
519 | ucal_isSet( const UCalendar* cal, | |
520 | UCalendarDateFields field) | |
521 | { | |
522 | ||
523 | return ((Calendar*)cal)->isSet(field); | |
524 | } | |
525 | ||
526 | U_CAPI void U_EXPORT2 | |
527 | ucal_clearField( UCalendar* cal, | |
528 | UCalendarDateFields field) | |
529 | { | |
530 | ||
531 | ((Calendar*)cal)->clear(field); | |
532 | } | |
533 | ||
534 | U_CAPI void U_EXPORT2 | |
535 | ucal_clear(UCalendar* calendar) | |
536 | { | |
537 | ||
538 | ((Calendar*)calendar)->clear(); | |
539 | } | |
540 | ||
541 | U_CAPI int32_t U_EXPORT2 | |
542 | ucal_getLimit( const UCalendar* cal, | |
543 | UCalendarDateFields field, | |
544 | UCalendarLimitType type, | |
545 | UErrorCode *status) | |
546 | { | |
547 | ||
548 | if(status==0 || U_FAILURE(*status)) { | |
549 | return -1; | |
550 | } | |
551 | ||
552 | switch(type) { | |
553 | case UCAL_MINIMUM: | |
554 | return ((Calendar*)cal)->getMinimum(field); | |
555 | ||
556 | case UCAL_MAXIMUM: | |
557 | return ((Calendar*)cal)->getMaximum(field); | |
558 | ||
559 | case UCAL_GREATEST_MINIMUM: | |
560 | return ((Calendar*)cal)->getGreatestMinimum(field); | |
561 | ||
562 | case UCAL_LEAST_MAXIMUM: | |
563 | return ((Calendar*)cal)->getLeastMaximum(field); | |
564 | ||
565 | case UCAL_ACTUAL_MINIMUM: | |
566 | return ((Calendar*)cal)->getActualMinimum(field, | |
567 | *status); | |
568 | ||
569 | case UCAL_ACTUAL_MAXIMUM: | |
570 | return ((Calendar*)cal)->getActualMaximum(field, | |
571 | *status); | |
572 | ||
573 | default: | |
574 | break; | |
575 | } | |
576 | return -1; | |
577 | } | |
578 | ||
579 | U_CAPI const char * U_EXPORT2 | |
580 | ucal_getLocaleByType(const UCalendar *cal, ULocDataLocaleType type, UErrorCode* status) | |
581 | { | |
582 | if (cal == NULL) { | |
583 | if (U_SUCCESS(*status)) { | |
584 | *status = U_ILLEGAL_ARGUMENT_ERROR; | |
585 | } | |
586 | return NULL; | |
587 | } | |
588 | return ((Calendar*)cal)->getLocaleID(type, *status); | |
589 | } | |
590 | ||
591 | U_CAPI const char * U_EXPORT2 | |
592 | ucal_getTZDataVersion(UErrorCode* status) | |
593 | { | |
594 | return TimeZone::getTZDataVersion(*status); | |
595 | } | |
596 | ||
597 | U_CAPI int32_t U_EXPORT2 | |
598 | ucal_getCanonicalTimeZoneID(const UChar* id, int32_t len, | |
599 | UChar* result, int32_t resultCapacity, UBool *isSystemID, UErrorCode* status) { | |
600 | if(status == 0 || U_FAILURE(*status)) { | |
601 | return 0; | |
602 | } | |
603 | if (isSystemID) { | |
604 | *isSystemID = FALSE; | |
605 | } | |
606 | if (id == 0 || len == 0 || result == 0 || resultCapacity <= 0) { | |
607 | *status = U_ILLEGAL_ARGUMENT_ERROR; | |
608 | return 0; | |
609 | } | |
610 | int32_t reslen = 0; | |
611 | UnicodeString canonical; | |
612 | UBool systemID = FALSE; | |
613 | TimeZone::getCanonicalID(UnicodeString(id, len), canonical, systemID, *status); | |
614 | if (U_SUCCESS(*status)) { | |
615 | if (isSystemID) { | |
616 | *isSystemID = systemID; | |
617 | } | |
618 | reslen = canonical.extract(result, resultCapacity, *status); | |
619 | } | |
620 | return reslen; | |
621 | } | |
622 | ||
623 | U_CAPI const char * U_EXPORT2 | |
624 | ucal_getType(const UCalendar *cal, UErrorCode* status) | |
625 | { | |
626 | if (U_FAILURE(*status)) { | |
627 | return NULL; | |
628 | } | |
629 | return ((Calendar*)cal)->getType(); | |
630 | } | |
631 | ||
632 | U_CAPI UCalendarWeekdayType U_EXPORT2 | |
633 | ucal_getDayOfWeekType(const UCalendar *cal, UCalendarDaysOfWeek dayOfWeek, UErrorCode* status) | |
634 | { | |
635 | if (U_FAILURE(*status)) { | |
636 | return UCAL_WEEKDAY; | |
637 | } | |
638 | return ((Calendar*)cal)->getDayOfWeekType(dayOfWeek, *status); | |
639 | } | |
640 | ||
641 | U_CAPI int32_t U_EXPORT2 | |
642 | ucal_getWeekendTransition(const UCalendar *cal, UCalendarDaysOfWeek dayOfWeek, UErrorCode *status) | |
643 | { | |
644 | if (U_FAILURE(*status)) { | |
645 | return 0; | |
646 | } | |
647 | return ((Calendar*)cal)->getWeekendTransition(dayOfWeek, *status); | |
648 | } | |
649 | ||
650 | U_CAPI UBool U_EXPORT2 | |
651 | ucal_isWeekend(const UCalendar *cal, UDate date, UErrorCode *status) | |
652 | { | |
653 | if (U_FAILURE(*status)) { | |
654 | return FALSE; | |
655 | } | |
656 | return ((Calendar*)cal)->isWeekend(date, *status); | |
657 | } | |
658 | ||
659 | U_CAPI int32_t U_EXPORT2 | |
660 | ucal_getFieldDifference(UCalendar* cal, UDate target, | |
661 | UCalendarDateFields field, | |
662 | UErrorCode* status ) | |
663 | { | |
664 | if (U_FAILURE(*status)) { | |
665 | return 0; | |
666 | } | |
667 | return ((Calendar*)cal)->fieldDifference(target, field, *status); | |
668 | } | |
669 | ||
670 | ||
671 | static const UEnumeration defaultKeywordValues = { | |
672 | NULL, | |
673 | NULL, | |
674 | ulist_close_keyword_values_iterator, | |
675 | ulist_count_keyword_values, | |
676 | uenum_unextDefault, | |
677 | ulist_next_keyword_value, | |
678 | ulist_reset_keyword_values_iterator | |
679 | }; | |
680 | ||
681 | static const char * const CAL_TYPES[] = { | |
682 | "gregorian", | |
683 | "japanese", | |
684 | "buddhist", | |
685 | "roc", | |
686 | "persian", | |
687 | "islamic-civil", | |
688 | "islamic", | |
689 | "hebrew", | |
690 | "chinese", | |
691 | "indian", | |
692 | "coptic", | |
693 | "ethiopic", | |
694 | "ethiopic-amete-alem", | |
695 | "iso8601", | |
696 | "dangi", | |
697 | "islamic-umalqura", | |
698 | "islamic-tbla", | |
699 | "islamic-rgsa", | |
700 | NULL | |
701 | }; | |
702 | ||
703 | U_CAPI UEnumeration* U_EXPORT2 | |
704 | ucal_getKeywordValuesForLocale(const char * /* key */, const char* locale, UBool commonlyUsed, UErrorCode *status) { | |
705 | // Resolve region | |
706 | char prefRegion[ULOC_COUNTRY_CAPACITY]; | |
707 | (void)ulocimp_getRegionForSupplementalData(locale, TRUE, prefRegion, sizeof(prefRegion), status); | |
708 | ||
709 | // Read preferred calendar values from supplementalData calendarPreference | |
710 | UResourceBundle *rb = ures_openDirect(NULL, "supplementalData", status); | |
711 | ures_getByKey(rb, "calendarPreferenceData", rb, status); | |
712 | UResourceBundle *order = ures_getByKey(rb, prefRegion, NULL, status); | |
713 | if (*status == U_MISSING_RESOURCE_ERROR && rb != NULL) { | |
714 | *status = U_ZERO_ERROR; | |
715 | order = ures_getByKey(rb, "001", NULL, status); | |
716 | } | |
717 | ||
718 | // Create a list of calendar type strings | |
719 | UList *values = NULL; | |
720 | if (U_SUCCESS(*status)) { | |
721 | values = ulist_createEmptyList(status); | |
722 | if (U_SUCCESS(*status)) { | |
723 | for (int i = 0; i < ures_getSize(order); i++) { | |
724 | int32_t len; | |
725 | const UChar *type = ures_getStringByIndex(order, i, &len, status); | |
726 | char *caltype = (char*)uprv_malloc(len + 1); | |
727 | if (caltype == NULL) { | |
728 | *status = U_MEMORY_ALLOCATION_ERROR; | |
729 | break; | |
730 | } | |
731 | u_UCharsToChars(type, caltype, len); | |
732 | *(caltype + len) = 0; | |
733 | ||
734 | ulist_addItemEndList(values, caltype, TRUE, status); | |
735 | if (U_FAILURE(*status)) { | |
736 | break; | |
737 | } | |
738 | } | |
739 | ||
740 | if (U_SUCCESS(*status) && !commonlyUsed) { | |
741 | // If not commonlyUsed, add other available values | |
742 | for (int32_t i = 0; CAL_TYPES[i] != NULL; i++) { | |
743 | if (!ulist_containsString(values, CAL_TYPES[i], (int32_t)uprv_strlen(CAL_TYPES[i]))) { | |
744 | ulist_addItemEndList(values, CAL_TYPES[i], FALSE, status); | |
745 | if (U_FAILURE(*status)) { | |
746 | break; | |
747 | } | |
748 | } | |
749 | } | |
750 | } | |
751 | if (U_FAILURE(*status)) { | |
752 | ulist_deleteList(values); | |
753 | values = NULL; | |
754 | } | |
755 | } | |
756 | } | |
757 | ||
758 | ures_close(order); | |
759 | ures_close(rb); | |
760 | ||
761 | if (U_FAILURE(*status) || values == NULL) { | |
762 | return NULL; | |
763 | } | |
764 | ||
765 | // Create string enumeration | |
766 | UEnumeration *en = (UEnumeration*)uprv_malloc(sizeof(UEnumeration)); | |
767 | if (en == NULL) { | |
768 | *status = U_MEMORY_ALLOCATION_ERROR; | |
769 | ulist_deleteList(values); | |
770 | return NULL; | |
771 | } | |
772 | ulist_resetList(values); | |
773 | memcpy(en, &defaultKeywordValues, sizeof(UEnumeration)); | |
774 | en->context = values; | |
775 | return en; | |
776 | } | |
777 | ||
778 | U_CAPI UBool U_EXPORT2 | |
779 | ucal_getTimeZoneTransitionDate(const UCalendar* cal, UTimeZoneTransitionType type, | |
780 | UDate* transition, UErrorCode* status) | |
781 | { | |
782 | if (U_FAILURE(*status)) { | |
783 | return FALSE; | |
784 | } | |
785 | UDate base = ((Calendar*)cal)->getTime(*status); | |
786 | const TimeZone& tz = ((Calendar*)cal)->getTimeZone(); | |
787 | const BasicTimeZone * btz = dynamic_cast<const BasicTimeZone *>(&tz); | |
788 | if (btz != NULL && U_SUCCESS(*status)) { | |
789 | TimeZoneTransition tzt; | |
790 | UBool inclusive = (type == UCAL_TZ_TRANSITION_NEXT_INCLUSIVE || type == UCAL_TZ_TRANSITION_PREVIOUS_INCLUSIVE); | |
791 | UBool result = (type == UCAL_TZ_TRANSITION_NEXT || type == UCAL_TZ_TRANSITION_NEXT_INCLUSIVE)? | |
792 | btz->getNextTransition(base, inclusive, tzt): | |
793 | btz->getPreviousTransition(base, inclusive, tzt); | |
794 | if (result) { | |
795 | *transition = tzt.getTime(); | |
796 | return TRUE; | |
797 | } | |
798 | } | |
799 | return FALSE; | |
800 | } | |
801 | ||
802 | U_CAPI int32_t U_EXPORT2 | |
803 | ucal_getWindowsTimeZoneID(const UChar* id, int32_t len, UChar* winid, int32_t winidCapacity, UErrorCode* status) { | |
804 | if (U_FAILURE(*status)) { | |
805 | return 0; | |
806 | } | |
807 | ||
808 | int32_t resultLen = 0; | |
809 | UnicodeString resultWinID; | |
810 | ||
811 | TimeZone::getWindowsID(UnicodeString(id, len), resultWinID, *status); | |
812 | if (U_SUCCESS(*status) && resultWinID.length() > 0) { | |
813 | resultLen = resultWinID.length(); | |
814 | resultWinID.extract(winid, winidCapacity, *status); | |
815 | } | |
816 | ||
817 | return resultLen; | |
818 | } | |
819 | ||
820 | U_CAPI int32_t U_EXPORT2 | |
821 | ucal_getTimeZoneIDForWindowsID(const UChar* winid, int32_t len, const char* region, UChar* id, int32_t idCapacity, UErrorCode* status) { | |
822 | if (U_FAILURE(*status)) { | |
823 | return 0; | |
824 | } | |
825 | ||
826 | int32_t resultLen = 0; | |
827 | UnicodeString resultID; | |
828 | ||
829 | TimeZone::getIDForWindowsID(UnicodeString(winid, len), region, resultID, *status); | |
830 | if (U_SUCCESS(*status) && resultID.length() > 0) { | |
831 | resultLen = resultID.length(); | |
832 | resultID.extract(id, idCapacity, *status); | |
833 | } | |
834 | ||
835 | return resultLen; | |
836 | } | |
837 | ||
838 | // Apple-specific function uacal_getDayPeriod and helper functions/data | |
839 | typedef struct { | |
840 | const char* name; | |
841 | UADayPeriod value; | |
842 | } DayPeriodNameToValue; | |
843 | ||
844 | static const DayPeriodNameToValue dpNameToValue[] = { | |
845 | { "afternoon1", UADAYPERIOD_AFTERNOON1 }, | |
846 | { "afternoon2", UADAYPERIOD_AFTERNOON2 }, | |
847 | { "evening1", UADAYPERIOD_EVENING1 }, | |
848 | { "evening2", UADAYPERIOD_EVENING2 }, | |
849 | { "midnight", UADAYPERIOD_MIDNIGHT }, | |
850 | { "morning1", UADAYPERIOD_MORNING1 }, | |
851 | { "morning2", UADAYPERIOD_MORNING2 }, | |
852 | { "night1", UADAYPERIOD_NIGHT1 }, | |
853 | { "night2", UADAYPERIOD_NIGHT2 }, | |
854 | { "noon", UADAYPERIOD_NOON }, | |
855 | }; | |
856 | ||
857 | static UADayPeriod dayPeriodFromName(const char* name) { | |
858 | const DayPeriodNameToValue * dpNameToValuePtr = dpNameToValue; | |
859 | const DayPeriodNameToValue * dpNameToValueLim = dpNameToValue + UPRV_LENGTHOF(dpNameToValue); | |
860 | // simple linear search, dpNameToValue is small enough | |
861 | for (; dpNameToValuePtr < dpNameToValueLim; dpNameToValuePtr++) { | |
862 | if (uprv_strcmp(name, dpNameToValuePtr->name) == 0) { | |
863 | return dpNameToValuePtr->value; | |
864 | } | |
865 | } | |
866 | return UADAYPERIOD_UNKNOWN; | |
867 | } | |
868 | ||
869 | typedef struct { | |
870 | int32_t startHour; | |
871 | int32_t startMin; | |
872 | UADayPeriod value; | |
873 | } DayPeriodEntry; | |
874 | ||
875 | int CompareDayPeriodEntries(const void* entry1Ptr, const void* entry2Ptr) { | |
876 | const DayPeriodEntry * dpEntry1Ptr = (const DayPeriodEntry *)entry1Ptr; | |
877 | const DayPeriodEntry * dpEntry2Ptr = (const DayPeriodEntry *)entry2Ptr; | |
878 | if (dpEntry1Ptr->startHour < dpEntry2Ptr->startHour) return -1; | |
879 | if (dpEntry1Ptr->startHour > dpEntry2Ptr->startHour) return 1; | |
880 | // here hours are equal | |
881 | if (dpEntry1Ptr->startMin < dpEntry2Ptr->startMin) return -1; | |
882 | if (dpEntry1Ptr->startMin > dpEntry2Ptr->startMin) return 1; | |
883 | return 0; | |
884 | } | |
885 | ||
886 | enum { kSetNameMaxLen = 8, kBoundaryTimeMaxLen = 6, kDayPeriodEntriesMax = 12 }; | |
887 | ||
888 | U_CAPI UADayPeriod U_EXPORT2 | |
889 | uacal_getDayPeriod( const char* locale, | |
890 | int32_t hour, | |
891 | int32_t minute, | |
892 | UBool formatStyle, | |
893 | UErrorCode* status ) { | |
894 | UADayPeriod dayPeriod = UADAYPERIOD_UNKNOWN; | |
895 | DayPeriodEntry dpEntries[kDayPeriodEntriesMax]; | |
896 | int32_t dpEntriesCount = 0; | |
897 | ||
898 | if (U_FAILURE(*status)) { | |
899 | return dayPeriod; | |
900 | } | |
901 | if (hour < 0 || hour > 23 || minute < 0 || minute > 59) { | |
902 | *status = U_ILLEGAL_ARGUMENT_ERROR; | |
903 | return dayPeriod; | |
904 | } | |
905 | // get dayPeriods bundle | |
906 | LocalUResourceBundlePointer rb(ures_openDirect(NULL, "dayPeriods", status)); | |
907 | if (U_FAILURE(*status)) { | |
908 | return dayPeriod; | |
909 | } | |
910 | // get locales/locales_selection subbundle | |
911 | LocalUResourceBundlePointer rbSub(ures_getByKey(rb.getAlias(), formatStyle? "locales": "locales_selection", NULL, status)); | |
912 | if (U_FAILURE(*status)) { | |
913 | return dayPeriod; | |
914 | } | |
915 | // get bundle for language (maps to setName) | |
916 | char lang[ULOC_LANG_CAPACITY] = {0}; | |
917 | if (locale != NULL) { | |
918 | UErrorCode tempStatus = U_ZERO_ERROR; | |
919 | uloc_getLanguage(locale, lang, ULOC_LANG_CAPACITY, &tempStatus); | |
920 | if (U_FAILURE(*status) || *status == U_STRING_NOT_TERMINATED_WARNING) { | |
921 | lang[0] = 0; | |
922 | } | |
923 | } | |
924 | if (lang[0] == 0) { | |
925 | uprv_strcpy(lang, "en"); // should be "root" but the data for root was missing | |
926 | } | |
927 | LocalUResourceBundlePointer rbLang(ures_getByKey(rbSub.getAlias(), lang, NULL, status)); | |
928 | if (U_FAILURE(*status)) { | |
929 | // should only happen if lang was not [root] en | |
930 | // fallback should be "root" but the data for root was missing, use "en" | |
931 | *status = U_ZERO_ERROR; | |
932 | rbLang.adoptInstead(ures_getByKey(rbSub.getAlias(), "en", rbLang.orphan(), status)); | |
933 | } | |
934 | if (U_FAILURE(*status)) { | |
935 | return dayPeriod; | |
936 | } | |
937 | // get setName from language bundle | |
938 | char setName[kSetNameMaxLen] = {0}; | |
939 | int32_t setNameLen = kSetNameMaxLen; | |
940 | ures_getUTF8String(rbLang.getAlias(), setName, &setNameLen, TRUE, status); | |
941 | if (U_FAILURE(*status)) { | |
942 | return dayPeriod; | |
943 | } | |
944 | // get rules subbundle | |
945 | rbSub.adoptInstead(ures_getByKey(rb.getAlias(), "rules", rbSub.orphan(), status)); | |
946 | if (U_FAILURE(*status)) { | |
947 | return dayPeriod; | |
948 | } | |
949 | // get ruleset from rules subbundle | |
950 | rb.adoptInstead(ures_getByKey(rbSub.getAlias(), setName, rb.orphan(), status)); | |
951 | if (U_FAILURE(*status)) { | |
952 | return dayPeriod; | |
953 | } | |
954 | // OK, we should finally have a ruleset (works to here). | |
955 | // Iterate over it to collect entries | |
956 | LocalUResourceBundlePointer rbBound; | |
957 | while (ures_hasNext(rb.getAlias())) { | |
958 | rbSub.adoptInstead(ures_getNextResource(rb.getAlias(), rbSub.orphan(), status)); | |
959 | if (U_FAILURE(*status)) { | |
960 | return dayPeriod; | |
961 | } | |
962 | // rbSub now has the bundle for a particular dayPeriod such as morning1, afternoon2, noon | |
963 | UADayPeriod dpForBundle = dayPeriodFromName(ures_getKey(rbSub.getAlias())); | |
964 | int32_t dpLimit = 24; | |
965 | while (ures_hasNext(rbSub.getAlias())) { | |
966 | rbBound.adoptInstead(ures_getNextResource(rbSub.getAlias(), rbBound.orphan(), status)); | |
967 | if (U_FAILURE(*status)) { | |
968 | return dayPeriod; | |
969 | } | |
970 | // rbBound now has the bundle for a particular time period boundary such as at, from, before. | |
971 | // This is either of type URES_STRING (size=1) or of type URES_ARRAY (size > 1) | |
972 | const char *boundaryType = ures_getKey(rbBound.getAlias()); | |
973 | char boundaryTimeStr[kBoundaryTimeMaxLen]; | |
974 | int32_t boundaryTimeStrLen = kBoundaryTimeMaxLen; | |
975 | ures_getUTF8String(rbBound.getAlias(), boundaryTimeStr, &boundaryTimeStrLen, TRUE, status); | |
976 | if (U_FAILURE(*status)) { | |
977 | return dayPeriod; | |
978 | } | |
979 | int32_t startHour = atoi(boundaryTimeStr); // can depend on POSIX locale (fortunately no decimal sep here) | |
980 | if (uprv_strcmp(boundaryType, "before") == 0) { | |
981 | dpLimit = startHour; | |
982 | continue; | |
983 | } | |
984 | int32_t startMinute = 0; | |
985 | if (uprv_strcmp(boundaryType, "from") == 0) { | |
986 | startMinute = 1; | |
987 | if (startHour > dpLimit && dpEntriesCount < kDayPeriodEntriesMax) { | |
988 | dpEntries[dpEntriesCount].startHour = 0; | |
989 | dpEntries[dpEntriesCount].startMin = startMinute; | |
990 | dpEntries[dpEntriesCount].value = dpForBundle; | |
991 | dpEntriesCount++; | |
992 | } | |
993 | } | |
994 | if (dpEntriesCount < kDayPeriodEntriesMax) { | |
995 | dpEntries[dpEntriesCount].startHour = startHour; | |
996 | dpEntries[dpEntriesCount].startMin = startMinute; | |
997 | dpEntries[dpEntriesCount].value = dpForBundle; | |
998 | dpEntriesCount++; | |
999 | } | |
1000 | } | |
1001 | } | |
1002 | if (dpEntriesCount < kDayPeriodEntriesMax) { | |
1003 | dpEntries[dpEntriesCount].startHour = 24; | |
1004 | dpEntries[dpEntriesCount].startMin = 0; | |
1005 | dpEntries[dpEntriesCount].value = UADAYPERIOD_UNKNOWN; | |
1006 | dpEntriesCount++; | |
1007 | } | |
1008 | // We have collected all of the rule data, now sort by time | |
1009 | qsort(dpEntries, dpEntriesCount, sizeof(DayPeriodEntry), CompareDayPeriodEntries); | |
1010 | ||
1011 | // now fix start minute for the non-"at" entries | |
1012 | int32_t dpIndex; | |
1013 | for (dpIndex = 0; dpIndex < dpEntriesCount; dpIndex++) { | |
1014 | if (dpIndex == 0 || (dpEntries[dpIndex-1].value != UADAYPERIOD_MIDNIGHT && dpEntries[dpIndex-1].value != UADAYPERIOD_NOON)) { | |
1015 | dpEntries[dpIndex].startMin = 0; | |
1016 | } | |
1017 | } | |
1018 | ||
1019 | // OK, all of the above is what we would do in an "open" function if we were using an | |
1020 | // open/use/close model for this; the object would just have the sorted array above. | |
1021 | ||
1022 | // Now we use the sorted array to find the dayPeriod matching the supplied time. | |
1023 | // Only a few entries, linear search OK | |
1024 | DayPeriodEntry entryToMatch = { hour, minute, UADAYPERIOD_UNKNOWN }; | |
1025 | dpIndex = 0; | |
1026 | while (dpIndex < dpEntriesCount - 1 && CompareDayPeriodEntries(&entryToMatch, &dpEntries[dpIndex + 1]) >= 0) { | |
1027 | dpIndex++; | |
1028 | } | |
1029 | if (CompareDayPeriodEntries(&entryToMatch, &dpEntries[dpIndex]) >= 0) { | |
1030 | dayPeriod = dpEntries[dpIndex].value; | |
1031 | } | |
1032 | ||
1033 | return dayPeriod; | |
1034 | } | |
1035 | ||
1036 | ||
1037 | ||
1038 | #endif /* #if !UCONFIG_NO_FORMATTING */ |