• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 *******************************************************************************
3 *   Copyright (C) 1996-2011, International Business Machines
4 *   Corporation and others.  All Rights Reserved.
5 *******************************************************************************
6 */
7 
8 #include <typeinfo>  // for 'typeid' to work
9 
10 #include "unicode/utypes.h"
11 
12 #if !UCONFIG_NO_FORMATTING
13 
14 #include "unicode/ucal.h"
15 #include "unicode/uloc.h"
16 #include "unicode/calendar.h"
17 #include "unicode/timezone.h"
18 #include "unicode/gregocal.h"
19 #include "unicode/simpletz.h"
20 #include "unicode/ustring.h"
21 #include "unicode/strenum.h"
22 #include "cmemory.h"
23 #include "cstring.h"
24 #include "ustrenum.h"
25 #include "uenumimp.h"
26 #include "ulist.h"
27 
28 U_NAMESPACE_USE
29 
30 static TimeZone*
_createTimeZone(const UChar * zoneID,int32_t len,UErrorCode * ec)31 _createTimeZone(const UChar* zoneID, int32_t len, UErrorCode* ec) {
32     TimeZone* zone = NULL;
33     if (ec!=NULL && U_SUCCESS(*ec)) {
34         // Note that if zoneID is invalid, we get back GMT. This odd
35         // behavior is by design and goes back to the JDK. The only
36         // failure we will see is a memory allocation failure.
37         int32_t l = (len<0 ? u_strlen(zoneID) : len);
38         UnicodeString zoneStrID;
39         zoneStrID.setTo((UBool)(len < 0), zoneID, l); /* temporary read-only alias */
40         zone = TimeZone::createTimeZone(zoneStrID);
41         if (zone == NULL) {
42             *ec = U_MEMORY_ALLOCATION_ERROR;
43         }
44     }
45     return zone;
46 }
47 
48 U_CAPI UEnumeration* U_EXPORT2
ucal_openTimeZoneIDEnumeration(USystemTimeZoneType zoneType,const char * region,const int32_t * rawOffset,UErrorCode * ec)49 ucal_openTimeZoneIDEnumeration(USystemTimeZoneType zoneType, const char* region,
50                                 const int32_t* rawOffset, UErrorCode* ec) {
51     return uenum_openFromStringEnumeration(TimeZone::createTimeZoneIDEnumeration(
52         zoneType, region, rawOffset, *ec), ec);
53 }
54 
55 U_CAPI UEnumeration* U_EXPORT2
ucal_openTimeZones(UErrorCode * ec)56 ucal_openTimeZones(UErrorCode* ec) {
57     return uenum_openFromStringEnumeration(TimeZone::createEnumeration(), ec);
58 }
59 
60 U_CAPI UEnumeration* U_EXPORT2
ucal_openCountryTimeZones(const char * country,UErrorCode * ec)61 ucal_openCountryTimeZones(const char* country, UErrorCode* ec) {
62     return uenum_openFromStringEnumeration(TimeZone::createEnumeration(country), ec);
63 }
64 
65 U_CAPI int32_t U_EXPORT2
ucal_getDefaultTimeZone(UChar * result,int32_t resultCapacity,UErrorCode * ec)66 ucal_getDefaultTimeZone(UChar* result, int32_t resultCapacity, UErrorCode* ec) {
67     int32_t len = 0;
68     if (ec!=NULL && U_SUCCESS(*ec)) {
69         TimeZone* zone = TimeZone::createDefault();
70         if (zone == NULL) {
71             *ec = U_MEMORY_ALLOCATION_ERROR;
72         } else {
73             UnicodeString id;
74             zone->getID(id);
75             delete zone;
76             len = id.extract(result, resultCapacity, *ec);
77         }
78     }
79     return len;
80 }
81 
82 U_CAPI void U_EXPORT2
ucal_setDefaultTimeZone(const UChar * zoneID,UErrorCode * ec)83 ucal_setDefaultTimeZone(const UChar* zoneID, UErrorCode* ec) {
84     TimeZone* zone = _createTimeZone(zoneID, -1, ec);
85     if (zone != NULL) {
86         TimeZone::adoptDefault(zone);
87     }
88 }
89 
90 U_CAPI int32_t U_EXPORT2
ucal_getDSTSavings(const UChar * zoneID,UErrorCode * ec)91 ucal_getDSTSavings(const UChar* zoneID, UErrorCode* ec) {
92     int32_t result = 0;
93     TimeZone* zone = _createTimeZone(zoneID, -1, ec);
94     if (U_SUCCESS(*ec)) {
95         SimpleTimeZone* stz = dynamic_cast<SimpleTimeZone*>(zone);
96         if (stz != NULL) {
97             result = stz->getDSTSavings();
98         } else {
99             // Since there is no getDSTSavings on TimeZone, we use a
100             // heuristic: Starting with the current time, march
101             // forwards for one year, looking for DST savings.
102             // Stepping by weeks is sufficient.
103             UDate d = Calendar::getNow();
104             for (int32_t i=0; i<53; ++i, d+=U_MILLIS_PER_DAY*7.0) {
105                 int32_t raw, dst;
106                 zone->getOffset(d, FALSE, raw, dst, *ec);
107                 if (U_FAILURE(*ec)) {
108                     break;
109                 } else if (dst != 0) {
110                     result = dst;
111                     break;
112                 }
113             }
114         }
115     }
116     delete zone;
117     return result;
118 }
119 
120 U_CAPI UDate  U_EXPORT2
ucal_getNow()121 ucal_getNow()
122 {
123 
124   return Calendar::getNow();
125 }
126 
127 #define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY)
128 
129 U_CAPI UCalendar*  U_EXPORT2
ucal_open(const UChar * zoneID,int32_t len,const char * locale,UCalendarType caltype,UErrorCode * status)130 ucal_open(  const UChar*  zoneID,
131             int32_t       len,
132             const char*   locale,
133             UCalendarType caltype,
134             UErrorCode*   status)
135 {
136 
137   if(U_FAILURE(*status)) return 0;
138 
139   TimeZone* zone = (zoneID==NULL) ? TimeZone::createDefault()
140       : _createTimeZone(zoneID, len, status);
141 
142   if (U_FAILURE(*status)) {
143       return NULL;
144   }
145 
146   if ( caltype == UCAL_GREGORIAN ) {
147       char  localeBuf[ULOC_LOCALE_IDENTIFIER_CAPACITY];
148       if ( locale == NULL ) {
149           locale = uloc_getDefault();
150       }
151       uprv_strncpy(localeBuf, locale, ULOC_LOCALE_IDENTIFIER_CAPACITY);
152       uloc_setKeywordValue("calendar", "gregorian", localeBuf, ULOC_LOCALE_IDENTIFIER_CAPACITY, status);
153       if (U_FAILURE(*status)) {
154           return NULL;
155       }
156       return (UCalendar*)Calendar::createInstance(zone, Locale(localeBuf), *status);
157   }
158   return (UCalendar*)Calendar::createInstance(zone, Locale(locale), *status);
159 }
160 
161 U_CAPI void U_EXPORT2
ucal_close(UCalendar * cal)162 ucal_close(UCalendar *cal)
163 {
164 
165   delete (Calendar*) cal;
166 }
167 
168 U_CAPI UCalendar* U_EXPORT2
ucal_clone(const UCalendar * cal,UErrorCode * status)169 ucal_clone(const UCalendar* cal,
170            UErrorCode*      status)
171 {
172   if(U_FAILURE(*status)) return 0;
173 
174   Calendar* res = ((Calendar*)cal)->clone();
175 
176   if(res == 0) {
177     *status = U_MEMORY_ALLOCATION_ERROR;
178     return 0;
179   }
180 
181   return (UCalendar*) res;
182 }
183 
184 U_CAPI void  U_EXPORT2
ucal_setTimeZone(UCalendar * cal,const UChar * zoneID,int32_t len,UErrorCode * status)185 ucal_setTimeZone(    UCalendar*      cal,
186             const    UChar*            zoneID,
187             int32_t        len,
188             UErrorCode *status)
189 {
190 
191   if(U_FAILURE(*status))
192     return;
193 
194   TimeZone* zone = (zoneID==NULL) ? TimeZone::createDefault()
195       : _createTimeZone(zoneID, len, status);
196 
197   if (zone != NULL) {
198       ((Calendar*)cal)->adoptTimeZone(zone);
199   }
200 }
201 
202 U_CAPI int32_t U_EXPORT2
ucal_getTimeZoneDisplayName(const UCalendar * cal,UCalendarDisplayNameType type,const char * locale,UChar * result,int32_t resultLength,UErrorCode * status)203 ucal_getTimeZoneDisplayName(const     UCalendar*                 cal,
204                     UCalendarDisplayNameType     type,
205                     const char             *locale,
206                     UChar*                  result,
207                     int32_t                 resultLength,
208                     UErrorCode*             status)
209 {
210 
211     if(U_FAILURE(*status)) return -1;
212 
213     const TimeZone& tz = ((Calendar*)cal)->getTimeZone();
214     UnicodeString id;
215     if(!(result==NULL && resultLength==0)) {
216         // NULL destination for pure preflighting: empty dummy string
217         // otherwise, alias the destination buffer
218         id.setTo(result, 0, resultLength);
219     }
220 
221     switch(type) {
222   case UCAL_STANDARD:
223       tz.getDisplayName(FALSE, TimeZone::LONG, Locale(locale), id);
224       break;
225 
226   case UCAL_SHORT_STANDARD:
227       tz.getDisplayName(FALSE, TimeZone::SHORT, Locale(locale), id);
228       break;
229 
230   case UCAL_DST:
231       tz.getDisplayName(TRUE, TimeZone::LONG, Locale(locale), id);
232       break;
233 
234   case UCAL_SHORT_DST:
235       tz.getDisplayName(TRUE, TimeZone::SHORT, Locale(locale), id);
236       break;
237     }
238 
239     return id.extract(result, resultLength, *status);
240 }
241 
242 U_CAPI UBool  U_EXPORT2
ucal_inDaylightTime(const UCalendar * cal,UErrorCode * status)243 ucal_inDaylightTime(    const    UCalendar*      cal,
244                     UErrorCode*     status )
245 {
246 
247     if(U_FAILURE(*status)) return (UBool) -1;
248     return ((Calendar*)cal)->inDaylightTime(*status);
249 }
250 
251 U_CAPI void U_EXPORT2
ucal_setGregorianChange(UCalendar * cal,UDate date,UErrorCode * pErrorCode)252 ucal_setGregorianChange(UCalendar *cal, UDate date, UErrorCode *pErrorCode) {
253     if(U_FAILURE(*pErrorCode)) {
254         return;
255     }
256     Calendar *cpp_cal = (Calendar *)cal;
257     GregorianCalendar *gregocal = dynamic_cast<GregorianCalendar *>(cpp_cal);
258     // Not if(gregocal == NULL) {
259     // because we really want to work only with a GregorianCalendar, not with
260     // its subclasses like BuddhistCalendar.
261     // BEGIN android-added.
262     // See ICU ticket#9047.
263     if (cpp_cal == NULL) {
264         // We normally don't check "this" pointers for NULL, but this here avoids
265         // compiler-generated exception-throwing code in case cal == NULL.
266         *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR;
267         return;
268     }
269     // END android-added
270     if(typeid(*cpp_cal) != typeid(GregorianCalendar)) {
271         *pErrorCode = U_UNSUPPORTED_ERROR;
272         return;
273     }
274     gregocal->setGregorianChange(date, *pErrorCode);
275 }
276 
277 U_CAPI UDate U_EXPORT2
ucal_getGregorianChange(const UCalendar * cal,UErrorCode * pErrorCode)278 ucal_getGregorianChange(const UCalendar *cal, UErrorCode *pErrorCode) {
279     if(U_FAILURE(*pErrorCode)) {
280         return (UDate)0;
281     }
282     const Calendar *cpp_cal = (const Calendar *)cal;
283     const GregorianCalendar *gregocal = dynamic_cast<const GregorianCalendar *>(cpp_cal);
284     // Not if(gregocal == NULL) {
285     // see comments in ucal_setGregorianChange().
286     // BEGIN android-added.
287     // See ICU ticket#9047.
288     if (cpp_cal == NULL) {
289         // We normally don't check "this" pointers for NULL, but this here avoids
290         // compiler-generated exception-throwing code in case cal == NULL.
291         *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR;
292         return (UDate)0;
293     }
294     // END android-added
295     if(typeid(*cpp_cal) != typeid(GregorianCalendar)) {
296         *pErrorCode = U_UNSUPPORTED_ERROR;
297         return (UDate)0;
298     }
299     return gregocal->getGregorianChange();
300 }
301 
302 U_CAPI int32_t U_EXPORT2
ucal_getAttribute(const UCalendar * cal,UCalendarAttribute attr)303 ucal_getAttribute(    const    UCalendar*              cal,
304                   UCalendarAttribute      attr)
305 {
306 
307     switch(attr) {
308   case UCAL_LENIENT:
309       return ((Calendar*)cal)->isLenient();
310 
311   case UCAL_FIRST_DAY_OF_WEEK:
312       return ((Calendar*)cal)->getFirstDayOfWeek();
313 
314   case UCAL_MINIMAL_DAYS_IN_FIRST_WEEK:
315       return ((Calendar*)cal)->getMinimalDaysInFirstWeek();
316 
317   default:
318       break;
319     }
320     return -1;
321 }
322 
323 U_CAPI void U_EXPORT2
ucal_setAttribute(UCalendar * cal,UCalendarAttribute attr,int32_t newValue)324 ucal_setAttribute(      UCalendar*              cal,
325                   UCalendarAttribute      attr,
326                   int32_t                 newValue)
327 {
328 
329     switch(attr) {
330   case UCAL_LENIENT:
331       ((Calendar*)cal)->setLenient((UBool)newValue);
332       break;
333 
334   case UCAL_FIRST_DAY_OF_WEEK:
335       ((Calendar*)cal)->setFirstDayOfWeek((UCalendarDaysOfWeek)newValue);
336       break;
337 
338   case UCAL_MINIMAL_DAYS_IN_FIRST_WEEK:
339       ((Calendar*)cal)->setMinimalDaysInFirstWeek((uint8_t)newValue);
340       break;
341     }
342 }
343 
344 U_CAPI const char* U_EXPORT2
ucal_getAvailable(int32_t index)345 ucal_getAvailable(int32_t index)
346 {
347 
348     return uloc_getAvailable(index);
349 }
350 
351 U_CAPI int32_t U_EXPORT2
ucal_countAvailable()352 ucal_countAvailable()
353 {
354 
355     return uloc_countAvailable();
356 }
357 
358 U_CAPI UDate  U_EXPORT2
ucal_getMillis(const UCalendar * cal,UErrorCode * status)359 ucal_getMillis(    const    UCalendar*      cal,
360                UErrorCode*     status)
361 {
362 
363     if(U_FAILURE(*status)) return (UDate) 0;
364 
365     return ((Calendar*)cal)->getTime(*status);
366 }
367 
368 U_CAPI void  U_EXPORT2
ucal_setMillis(UCalendar * cal,UDate dateTime,UErrorCode * status)369 ucal_setMillis(        UCalendar*      cal,
370                UDate           dateTime,
371                UErrorCode*     status )
372 {
373     if(U_FAILURE(*status)) return;
374 
375     ((Calendar*)cal)->setTime(dateTime, *status);
376 }
377 
378 // TBD: why does this take an UErrorCode?
379 U_CAPI void  U_EXPORT2
ucal_setDate(UCalendar * cal,int32_t year,int32_t month,int32_t date,UErrorCode * status)380 ucal_setDate(        UCalendar*        cal,
381              int32_t            year,
382              int32_t            month,
383              int32_t            date,
384              UErrorCode        *status)
385 {
386 
387     if(U_FAILURE(*status)) return;
388 
389     ((Calendar*)cal)->set(year, month, date);
390 }
391 
392 // TBD: why does this take an UErrorCode?
393 U_CAPI void  U_EXPORT2
ucal_setDateTime(UCalendar * cal,int32_t year,int32_t month,int32_t date,int32_t hour,int32_t minute,int32_t second,UErrorCode * status)394 ucal_setDateTime(    UCalendar*        cal,
395                  int32_t            year,
396                  int32_t            month,
397                  int32_t            date,
398                  int32_t            hour,
399                  int32_t            minute,
400                  int32_t            second,
401                  UErrorCode        *status)
402 {
403     if(U_FAILURE(*status)) return;
404 
405     ((Calendar*)cal)->set(year, month, date, hour, minute, second);
406 }
407 
408 U_CAPI UBool  U_EXPORT2
ucal_equivalentTo(const UCalendar * cal1,const UCalendar * cal2)409 ucal_equivalentTo(    const UCalendar*      cal1,
410                   const UCalendar*      cal2)
411 {
412 
413     return ((Calendar*)cal1)->isEquivalentTo(*((Calendar*)cal2));
414 }
415 
416 U_CAPI void  U_EXPORT2
ucal_add(UCalendar * cal,UCalendarDateFields field,int32_t amount,UErrorCode * status)417 ucal_add(    UCalendar*                cal,
418          UCalendarDateFields        field,
419          int32_t                    amount,
420          UErrorCode*                status)
421 {
422 
423     if(U_FAILURE(*status)) return;
424 
425     ((Calendar*)cal)->add(field, amount, *status);
426 }
427 
428 U_CAPI void  U_EXPORT2
ucal_roll(UCalendar * cal,UCalendarDateFields field,int32_t amount,UErrorCode * status)429 ucal_roll(        UCalendar*            cal,
430           UCalendarDateFields field,
431           int32_t                amount,
432           UErrorCode*            status)
433 {
434 
435     if(U_FAILURE(*status)) return;
436 
437     ((Calendar*)cal)->roll(field, amount, *status);
438 }
439 
440 U_CAPI int32_t  U_EXPORT2
ucal_get(const UCalendar * cal,UCalendarDateFields field,UErrorCode * status)441 ucal_get(    const    UCalendar*                cal,
442          UCalendarDateFields        field,
443          UErrorCode*                status )
444 {
445 
446     if(U_FAILURE(*status)) return -1;
447 
448     return ((Calendar*)cal)->get(field, *status);
449 }
450 
451 U_CAPI void  U_EXPORT2
ucal_set(UCalendar * cal,UCalendarDateFields field,int32_t value)452 ucal_set(    UCalendar*                cal,
453          UCalendarDateFields        field,
454          int32_t                    value)
455 {
456 
457     ((Calendar*)cal)->set(field, value);
458 }
459 
460 U_CAPI UBool  U_EXPORT2
ucal_isSet(const UCalendar * cal,UCalendarDateFields field)461 ucal_isSet(    const    UCalendar*                cal,
462            UCalendarDateFields        field)
463 {
464 
465     return ((Calendar*)cal)->isSet(field);
466 }
467 
468 U_CAPI void  U_EXPORT2
ucal_clearField(UCalendar * cal,UCalendarDateFields field)469 ucal_clearField(    UCalendar*            cal,
470                 UCalendarDateFields field)
471 {
472 
473     ((Calendar*)cal)->clear(field);
474 }
475 
476 U_CAPI void  U_EXPORT2
ucal_clear(UCalendar * calendar)477 ucal_clear(UCalendar* calendar)
478 {
479 
480     ((Calendar*)calendar)->clear();
481 }
482 
483 U_CAPI int32_t  U_EXPORT2
ucal_getLimit(const UCalendar * cal,UCalendarDateFields field,UCalendarLimitType type,UErrorCode * status)484 ucal_getLimit(    const    UCalendar*              cal,
485               UCalendarDateFields     field,
486               UCalendarLimitType      type,
487               UErrorCode        *status)
488 {
489 
490     if(status==0 || U_FAILURE(*status)) {
491         return -1;
492     }
493 
494     switch(type) {
495   case UCAL_MINIMUM:
496       return ((Calendar*)cal)->getMinimum(field);
497 
498   case UCAL_MAXIMUM:
499       return ((Calendar*)cal)->getMaximum(field);
500 
501   case UCAL_GREATEST_MINIMUM:
502       return ((Calendar*)cal)->getGreatestMinimum(field);
503 
504   case UCAL_LEAST_MAXIMUM:
505       return ((Calendar*)cal)->getLeastMaximum(field);
506 
507   case UCAL_ACTUAL_MINIMUM:
508       return ((Calendar*)cal)->getActualMinimum(field,
509           *status);
510 
511   case UCAL_ACTUAL_MAXIMUM:
512       return ((Calendar*)cal)->getActualMaximum(field,
513           *status);
514 
515   default:
516       break;
517     }
518     return -1;
519 }
520 
521 U_CAPI const char * U_EXPORT2
ucal_getLocaleByType(const UCalendar * cal,ULocDataLocaleType type,UErrorCode * status)522 ucal_getLocaleByType(const UCalendar *cal, ULocDataLocaleType type, UErrorCode* status)
523 {
524     if (cal == NULL) {
525         if (U_SUCCESS(*status)) {
526             *status = U_ILLEGAL_ARGUMENT_ERROR;
527         }
528         return NULL;
529     }
530     return ((Calendar*)cal)->getLocaleID(type, *status);
531 }
532 
533 U_CAPI const char * U_EXPORT2
ucal_getTZDataVersion(UErrorCode * status)534 ucal_getTZDataVersion(UErrorCode* status)
535 {
536     return TimeZone::getTZDataVersion(*status);
537 }
538 
539 U_CAPI int32_t U_EXPORT2
ucal_getCanonicalTimeZoneID(const UChar * id,int32_t len,UChar * result,int32_t resultCapacity,UBool * isSystemID,UErrorCode * status)540 ucal_getCanonicalTimeZoneID(const UChar* id, int32_t len,
541                             UChar* result, int32_t resultCapacity, UBool *isSystemID, UErrorCode* status) {
542     if(status == 0 || U_FAILURE(*status)) {
543         return 0;
544     }
545     if (isSystemID) {
546         *isSystemID = FALSE;
547     }
548     if (id == 0 || len == 0 || result == 0 || resultCapacity <= 0) {
549         *status = U_ILLEGAL_ARGUMENT_ERROR;
550         return 0;
551     }
552     int32_t reslen = 0;
553     UnicodeString canonical;
554     UBool systemID = FALSE;
555     TimeZone::getCanonicalID(UnicodeString(id, len), canonical, systemID, *status);
556     if (U_SUCCESS(*status)) {
557         if (isSystemID) {
558             *isSystemID = systemID;
559         }
560         reslen = canonical.extract(result, resultCapacity, *status);
561     }
562     return reslen;
563 }
564 
565 U_CAPI const char * U_EXPORT2
ucal_getType(const UCalendar * cal,UErrorCode * status)566 ucal_getType(const UCalendar *cal, UErrorCode* status)
567 {
568     if (U_FAILURE(*status)) {
569         return NULL;
570     }
571     return ((Calendar*)cal)->getType();
572 }
573 
574 U_CAPI UCalendarWeekdayType U_EXPORT2
ucal_getDayOfWeekType(const UCalendar * cal,UCalendarDaysOfWeek dayOfWeek,UErrorCode * status)575 ucal_getDayOfWeekType(const UCalendar *cal, UCalendarDaysOfWeek dayOfWeek, UErrorCode* status)
576 {
577     if (U_FAILURE(*status)) {
578         return UCAL_WEEKDAY;
579     }
580     return ((Calendar*)cal)->getDayOfWeekType(dayOfWeek, *status);
581 }
582 
583 U_CAPI int32_t U_EXPORT2
ucal_getWeekendTransition(const UCalendar * cal,UCalendarDaysOfWeek dayOfWeek,UErrorCode * status)584 ucal_getWeekendTransition(const UCalendar *cal, UCalendarDaysOfWeek dayOfWeek, UErrorCode *status)
585 {
586     if (U_FAILURE(*status)) {
587         return 0;
588     }
589     return ((Calendar*)cal)->getWeekendTransition(dayOfWeek, *status);
590 }
591 
592 U_CAPI UBool U_EXPORT2
ucal_isWeekend(const UCalendar * cal,UDate date,UErrorCode * status)593 ucal_isWeekend(const UCalendar *cal, UDate date, UErrorCode *status)
594 {
595     if (U_FAILURE(*status)) {
596         return FALSE;
597     }
598     return ((Calendar*)cal)->isWeekend(date, *status);
599 }
600 
601 U_CAPI int32_t  U_EXPORT2
ucal_getFieldDifference(UCalendar * cal,UDate target,UCalendarDateFields field,UErrorCode * status)602 ucal_getFieldDifference(UCalendar* cal, UDate target,
603                         UCalendarDateFields field,
604                         UErrorCode* status )
605 {
606     if (U_FAILURE(*status)) {
607         return 0;
608     }
609     return ((Calendar*)cal)->fieldDifference(target, field, *status);
610 }
611 
612 
613 static const UEnumeration defaultKeywordValues = {
614     NULL,
615     NULL,
616     ulist_close_keyword_values_iterator,
617     ulist_count_keyword_values,
618     uenum_unextDefault,
619     ulist_next_keyword_value,
620     ulist_reset_keyword_values_iterator
621 };
622 
623 static const char * const CAL_TYPES[] = {
624         "gregorian",
625         "japanese",
626         "buddhist",
627         "roc",
628         "persian",
629         "islamic-civil",
630         "islamic",
631         "hebrew",
632         "chinese",
633         "indian",
634         "coptic",
635         "ethiopic",
636         "ethiopic-amete-alem",
637         NULL
638 };
639 
640 U_CAPI UEnumeration* U_EXPORT2
ucal_getKeywordValuesForLocale(const char *,const char * locale,UBool commonlyUsed,UErrorCode * status)641 ucal_getKeywordValuesForLocale(const char * /* key */, const char* locale, UBool commonlyUsed, UErrorCode *status) {
642     // Resolve region
643     char prefRegion[ULOC_FULLNAME_CAPACITY] = "";
644     int32_t prefRegionLength = 0;
645     prefRegionLength = uloc_getCountry(locale, prefRegion, sizeof(prefRegion), status);
646     if (prefRegionLength == 0) {
647         char loc[ULOC_FULLNAME_CAPACITY] = "";
648         int32_t locLength = 0;
649         locLength = uloc_addLikelySubtags(locale, loc, sizeof(loc), status);
650 
651         prefRegionLength = uloc_getCountry(loc, prefRegion, sizeof(prefRegion), status);
652     }
653 
654     // Read preferred calendar values from supplementalData calendarPreference
655     UResourceBundle *rb = ures_openDirect(NULL, "supplementalData", status);
656     ures_getByKey(rb, "calendarPreferenceData", rb, status);
657     UResourceBundle *order = ures_getByKey(rb, prefRegion, NULL, status);
658     if (*status == U_MISSING_RESOURCE_ERROR && rb != NULL) {
659         *status = U_ZERO_ERROR;
660         order = ures_getByKey(rb, "001", NULL, status);
661     }
662 
663     // Create a list of calendar type strings
664     UList *values = NULL;
665     if (U_SUCCESS(*status)) {
666         values = ulist_createEmptyList(status);
667         if (U_SUCCESS(*status)) {
668             for (int i = 0; i < ures_getSize(order); i++) {
669                 int32_t len;
670                 const UChar *type = ures_getStringByIndex(order, i, &len, status);
671                 char *caltype = (char*)uprv_malloc(len + 1);
672                 if (caltype == NULL) {
673                     *status = U_MEMORY_ALLOCATION_ERROR;
674                     break;
675                 }
676                 u_UCharsToChars(type, caltype, len);
677                 *(caltype + len) = 0;
678 
679                 ulist_addItemEndList(values, caltype, TRUE, status);
680                 if (U_FAILURE(*status)) {
681                     break;
682                 }
683             }
684 
685             if (U_SUCCESS(*status) && !commonlyUsed) {
686                 // If not commonlyUsed, add other available values
687                 for (int32_t i = 0; CAL_TYPES[i] != NULL; i++) {
688                     if (!ulist_containsString(values, CAL_TYPES[i], (int32_t)uprv_strlen(CAL_TYPES[i]))) {
689                         ulist_addItemEndList(values, CAL_TYPES[i], FALSE, status);
690                         if (U_FAILURE(*status)) {
691                             break;
692                         }
693                     }
694                 }
695             }
696             if (U_FAILURE(*status)) {
697                 ulist_deleteList(values);
698                 values = NULL;
699             }
700         }
701     }
702 
703     ures_close(order);
704     ures_close(rb);
705 
706     if (U_FAILURE(*status) || values == NULL) {
707         return NULL;
708     }
709 
710     // Create string enumeration
711     UEnumeration *en = (UEnumeration*)uprv_malloc(sizeof(UEnumeration));
712     if (en == NULL) {
713         *status = U_MEMORY_ALLOCATION_ERROR;
714         ulist_deleteList(values);
715         return NULL;
716     }
717     ulist_resetList(values);
718     memcpy(en, &defaultKeywordValues, sizeof(UEnumeration));
719     en->context = values;
720     return en;
721 }
722 
723 #endif /* #if !UCONFIG_NO_FORMATTING */
724