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