2 *******************************************************************************
3 * Copyright (C) 1996-2015, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 *******************************************************************************
8 #include "utypeinfo.h" // for 'typeid' to work
10 #include "unicode/utypes.h"
12 #if !UCONFIG_NO_FORMATTING
14 #include <stdlib.h> // Apple addition for uacal_getDayPeriod
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"
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
);
45 *ec
= U_MEMORY_ALLOCATION_ERROR
;
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
);
58 U_CAPI UEnumeration
* U_EXPORT2
59 ucal_openTimeZones(UErrorCode
* ec
) {
60 return uenum_openFromStringEnumeration(TimeZone::createEnumeration(), ec
);
63 U_CAPI UEnumeration
* U_EXPORT2
64 ucal_openCountryTimeZones(const char* country
, UErrorCode
* ec
) {
65 return uenum_openFromStringEnumeration(TimeZone::createEnumeration(country
), ec
);
68 U_CAPI
int32_t U_EXPORT2
69 ucal_getDefaultTimeZone(UChar
* result
, int32_t resultCapacity
, UErrorCode
* ec
) {
71 if (ec
!=NULL
&& U_SUCCESS(*ec
)) {
72 TimeZone
* zone
= TimeZone::createDefault();
74 *ec
= U_MEMORY_ALLOCATION_ERROR
;
79 len
= id
.extract(result
, resultCapacity
, *ec
);
86 ucal_setDefaultTimeZone(const UChar
* zoneID
, UErrorCode
* ec
) {
87 TimeZone
* zone
= _createTimeZone(zoneID
, -1, ec
);
89 TimeZone::adoptDefault(zone
);
93 U_CAPI
int32_t U_EXPORT2
94 ucal_getDSTSavings(const UChar
* zoneID
, UErrorCode
* ec
) {
96 TimeZone
* zone
= _createTimeZone(zoneID
, -1, ec
);
98 SimpleTimeZone
* stz
= dynamic_cast<SimpleTimeZone
*>(zone
);
100 result
= stz
->getDSTSavings();
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) {
109 zone
->getOffset(d
, FALSE
, raw
, dst
, *ec
);
110 if (U_FAILURE(*ec
)) {
112 } else if (dst
!= 0) {
123 U_CAPI UDate U_EXPORT2
127 return Calendar::getNow();
130 #define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY)
132 U_CAPI UCalendar
* U_EXPORT2
133 ucal_open( const UChar
* zoneID
,
136 UCalendarType caltype
,
140 if(U_FAILURE(*status
)) return 0;
142 TimeZone
* zone
= (zoneID
==NULL
) ? TimeZone::createDefault()
143 : _createTimeZone(zoneID
, len
, status
);
145 if (U_FAILURE(*status
)) {
149 if ( caltype
== UCAL_GREGORIAN
) {
150 char localeBuf
[ULOC_LOCALE_IDENTIFIER_CAPACITY
];
151 if ( locale
== NULL
) {
152 locale
= uloc_getDefault();
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
)) {
159 return (UCalendar
*)Calendar::createInstance(zone
, Locale(localeBuf
), *status
);
161 return (UCalendar
*)Calendar::createInstance(zone
, Locale(locale
), *status
);
164 U_CAPI
void U_EXPORT2
165 ucal_close(UCalendar
*cal
)
168 delete (Calendar
*) cal
;
171 U_CAPI UCalendar
* U_EXPORT2
172 ucal_clone(const UCalendar
* cal
,
175 if(U_FAILURE(*status
)) return 0;
177 Calendar
* res
= ((Calendar
*)cal
)->clone();
180 *status
= U_MEMORY_ALLOCATION_ERROR
;
184 return (UCalendar
*) res
;
187 U_CAPI
void U_EXPORT2
188 ucal_setTimeZone( UCalendar
* cal
,
194 if(U_FAILURE(*status
))
197 TimeZone
* zone
= (zoneID
==NULL
) ? TimeZone::createDefault()
198 : _createTimeZone(zoneID
, len
, status
);
201 ((Calendar
*)cal
)->adoptTimeZone(zone
);
205 U_CAPI
int32_t U_EXPORT2
206 ucal_getTimeZoneID(const UCalendar
*cal
,
208 int32_t resultLength
,
211 if (U_FAILURE(*status
)) {
214 const TimeZone
& tz
= ((Calendar
*)cal
)->getTimeZone();
217 return id
.extract(result
, resultLength
, *status
);
220 U_CAPI
int32_t U_EXPORT2
221 ucal_getTimeZoneDisplayName(const UCalendar
* cal
,
222 UCalendarDisplayNameType type
,
225 int32_t resultLength
,
229 if(U_FAILURE(*status
)) return -1;
231 const TimeZone
& tz
= ((Calendar
*)cal
)->getTimeZone();
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
);
241 tz
.getDisplayName(FALSE
, TimeZone::LONG
, Locale(locale
), id
);
244 case UCAL_SHORT_STANDARD
:
245 tz
.getDisplayName(FALSE
, TimeZone::SHORT
, Locale(locale
), id
);
249 tz
.getDisplayName(TRUE
, TimeZone::LONG
, Locale(locale
), id
);
253 tz
.getDisplayName(TRUE
, TimeZone::SHORT
, Locale(locale
), id
);
257 return id
.extract(result
, resultLength
, *status
);
260 U_CAPI UBool U_EXPORT2
261 ucal_inDaylightTime( const UCalendar
* cal
,
265 if(U_FAILURE(*status
)) return (UBool
) -1;
266 return ((Calendar
*)cal
)->inDaylightTime(*status
);
269 U_CAPI
void U_EXPORT2
270 ucal_setGregorianChange(UCalendar
*cal
, UDate date
, UErrorCode
*pErrorCode
) {
271 if(U_FAILURE(*pErrorCode
)) {
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
;
285 if(typeid(*cpp_cal
) != typeid(GregorianCalendar
)) {
286 *pErrorCode
= U_UNSUPPORTED_ERROR
;
289 gregocal
->setGregorianChange(date
, *pErrorCode
);
292 U_CAPI UDate U_EXPORT2
293 ucal_getGregorianChange(const UCalendar
*cal
, UErrorCode
*pErrorCode
) {
294 if(U_FAILURE(*pErrorCode
)) {
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
;
307 if(typeid(*cpp_cal
) != typeid(GregorianCalendar
)) {
308 *pErrorCode
= U_UNSUPPORTED_ERROR
;
311 return gregocal
->getGregorianChange();
314 U_CAPI
int32_t U_EXPORT2
315 ucal_getAttribute( const UCalendar
* cal
,
316 UCalendarAttribute attr
)
321 return ((Calendar
*)cal
)->isLenient();
323 case UCAL_FIRST_DAY_OF_WEEK
:
324 return ((Calendar
*)cal
)->getFirstDayOfWeek();
326 case UCAL_MINIMAL_DAYS_IN_FIRST_WEEK
:
327 return ((Calendar
*)cal
)->getMinimalDaysInFirstWeek();
329 case UCAL_REPEATED_WALL_TIME
:
330 return ((Calendar
*)cal
)->getRepeatedWallTimeOption();
332 case UCAL_SKIPPED_WALL_TIME
:
333 return ((Calendar
*)cal
)->getSkippedWallTimeOption();
341 U_CAPI
void U_EXPORT2
342 ucal_setAttribute( UCalendar
* cal
,
343 UCalendarAttribute attr
,
349 ((Calendar
*)cal
)->setLenient((UBool
)newValue
);
352 case UCAL_FIRST_DAY_OF_WEEK
:
353 ((Calendar
*)cal
)->setFirstDayOfWeek((UCalendarDaysOfWeek
)newValue
);
356 case UCAL_MINIMAL_DAYS_IN_FIRST_WEEK
:
357 ((Calendar
*)cal
)->setMinimalDaysInFirstWeek((uint8_t)newValue
);
360 case UCAL_REPEATED_WALL_TIME
:
361 ((Calendar
*)cal
)->setRepeatedWallTimeOption((UCalendarWallTimeOption
)newValue
);
364 case UCAL_SKIPPED_WALL_TIME
:
365 ((Calendar
*)cal
)->setSkippedWallTimeOption((UCalendarWallTimeOption
)newValue
);
370 U_CAPI
const char* U_EXPORT2
371 ucal_getAvailable(int32_t index
)
374 return uloc_getAvailable(index
);
377 U_CAPI
int32_t U_EXPORT2
378 ucal_countAvailable()
381 return uloc_countAvailable();
384 U_CAPI UDate U_EXPORT2
385 ucal_getMillis( const UCalendar
* cal
,
389 if(U_FAILURE(*status
)) return (UDate
) 0;
391 return ((Calendar
*)cal
)->getTime(*status
);
394 U_CAPI
void U_EXPORT2
395 ucal_setMillis( UCalendar
* cal
,
399 if(U_FAILURE(*status
)) return;
401 ((Calendar
*)cal
)->setTime(dateTime
, *status
);
404 // TBD: why does this take an UErrorCode?
405 U_CAPI
void U_EXPORT2
406 ucal_setDate( UCalendar
* cal
,
413 if(U_FAILURE(*status
)) return;
415 ((Calendar
*)cal
)->set(year
, month
, date
);
418 // TBD: why does this take an UErrorCode?
419 U_CAPI
void U_EXPORT2
420 ucal_setDateTime( UCalendar
* cal
,
429 if(U_FAILURE(*status
)) return;
431 ((Calendar
*)cal
)->set(year
, month
, date
, hour
, minute
, second
);
434 U_CAPI UBool U_EXPORT2
435 ucal_equivalentTo( const UCalendar
* cal1
,
436 const UCalendar
* cal2
)
439 return ((Calendar
*)cal1
)->isEquivalentTo(*((Calendar
*)cal2
));
442 U_CAPI
void U_EXPORT2
443 ucal_add( UCalendar
* cal
,
444 UCalendarDateFields field
,
449 if(U_FAILURE(*status
)) return;
451 ((Calendar
*)cal
)->add(field
, amount
, *status
);
454 U_CAPI
void U_EXPORT2
455 ucal_roll( UCalendar
* cal
,
456 UCalendarDateFields field
,
461 if(U_FAILURE(*status
)) return;
463 ((Calendar
*)cal
)->roll(field
, amount
, *status
);
466 U_CAPI
int32_t U_EXPORT2
467 ucal_get( const UCalendar
* cal
,
468 UCalendarDateFields field
,
472 if(U_FAILURE(*status
)) return -1;
474 return ((Calendar
*)cal
)->get(field
, *status
);
477 U_CAPI
void U_EXPORT2
478 ucal_set( UCalendar
* cal
,
479 UCalendarDateFields field
,
483 ((Calendar
*)cal
)->set(field
, value
);
486 U_CAPI UBool U_EXPORT2
487 ucal_isSet( const UCalendar
* cal
,
488 UCalendarDateFields field
)
491 return ((Calendar
*)cal
)->isSet(field
);
494 U_CAPI
void U_EXPORT2
495 ucal_clearField( UCalendar
* cal
,
496 UCalendarDateFields field
)
499 ((Calendar
*)cal
)->clear(field
);
502 U_CAPI
void U_EXPORT2
503 ucal_clear(UCalendar
* calendar
)
506 ((Calendar
*)calendar
)->clear();
509 U_CAPI
int32_t U_EXPORT2
510 ucal_getLimit( const UCalendar
* cal
,
511 UCalendarDateFields field
,
512 UCalendarLimitType type
,
516 if(status
==0 || U_FAILURE(*status
)) {
522 return ((Calendar
*)cal
)->getMinimum(field
);
525 return ((Calendar
*)cal
)->getMaximum(field
);
527 case UCAL_GREATEST_MINIMUM
:
528 return ((Calendar
*)cal
)->getGreatestMinimum(field
);
530 case UCAL_LEAST_MAXIMUM
:
531 return ((Calendar
*)cal
)->getLeastMaximum(field
);
533 case UCAL_ACTUAL_MINIMUM
:
534 return ((Calendar
*)cal
)->getActualMinimum(field
,
537 case UCAL_ACTUAL_MAXIMUM
:
538 return ((Calendar
*)cal
)->getActualMaximum(field
,
547 U_CAPI
const char * U_EXPORT2
548 ucal_getLocaleByType(const UCalendar
*cal
, ULocDataLocaleType type
, UErrorCode
* status
)
551 if (U_SUCCESS(*status
)) {
552 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
556 return ((Calendar
*)cal
)->getLocaleID(type
, *status
);
559 U_CAPI
const char * U_EXPORT2
560 ucal_getTZDataVersion(UErrorCode
* status
)
562 return TimeZone::getTZDataVersion(*status
);
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
)) {
574 if (id
== 0 || len
== 0 || result
== 0 || resultCapacity
<= 0) {
575 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
579 UnicodeString canonical
;
580 UBool systemID
= FALSE
;
581 TimeZone::getCanonicalID(UnicodeString(id
, len
), canonical
, systemID
, *status
);
582 if (U_SUCCESS(*status
)) {
584 *isSystemID
= systemID
;
586 reslen
= canonical
.extract(result
, resultCapacity
, *status
);
591 U_CAPI
const char * U_EXPORT2
592 ucal_getType(const UCalendar
*cal
, UErrorCode
* status
)
594 if (U_FAILURE(*status
)) {
597 return ((Calendar
*)cal
)->getType();
600 U_CAPI UCalendarWeekdayType U_EXPORT2
601 ucal_getDayOfWeekType(const UCalendar
*cal
, UCalendarDaysOfWeek dayOfWeek
, UErrorCode
* status
)
603 if (U_FAILURE(*status
)) {
606 return ((Calendar
*)cal
)->getDayOfWeekType(dayOfWeek
, *status
);
609 U_CAPI
int32_t U_EXPORT2
610 ucal_getWeekendTransition(const UCalendar
*cal
, UCalendarDaysOfWeek dayOfWeek
, UErrorCode
*status
)
612 if (U_FAILURE(*status
)) {
615 return ((Calendar
*)cal
)->getWeekendTransition(dayOfWeek
, *status
);
618 U_CAPI UBool U_EXPORT2
619 ucal_isWeekend(const UCalendar
*cal
, UDate date
, UErrorCode
*status
)
621 if (U_FAILURE(*status
)) {
624 return ((Calendar
*)cal
)->isWeekend(date
, *status
);
627 U_CAPI
int32_t U_EXPORT2
628 ucal_getFieldDifference(UCalendar
* cal
, UDate target
,
629 UCalendarDateFields field
,
632 if (U_FAILURE(*status
)) {
635 return ((Calendar
*)cal
)->fieldDifference(target
, field
, *status
);
639 static const UEnumeration defaultKeywordValues
= {
642 ulist_close_keyword_values_iterator
,
643 ulist_count_keyword_values
,
645 ulist_next_keyword_value
,
646 ulist_reset_keyword_values_iterator
649 static const char * const CAL_TYPES
[] = {
662 "ethiopic-amete-alem",
671 U_CAPI UEnumeration
* U_EXPORT2
672 ucal_getKeywordValuesForLocale(const char * /* key */, const char* locale
, UBool commonlyUsed
, UErrorCode
*status
) {
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
);
681 prefRegionLength
= uloc_getCountry(loc
, prefRegion
, sizeof(prefRegion
), status
);
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
);
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
++) {
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
;
706 u_UCharsToChars(type
, caltype
, len
);
707 *(caltype
+ len
) = 0;
709 ulist_addItemEndList(values
, caltype
, TRUE
, status
);
710 if (U_FAILURE(*status
)) {
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
)) {
726 if (U_FAILURE(*status
)) {
727 ulist_deleteList(values
);
736 if (U_FAILURE(*status
) || values
== NULL
) {
740 // Create string enumeration
741 UEnumeration
*en
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
743 *status
= U_MEMORY_ALLOCATION_ERROR
;
744 ulist_deleteList(values
);
747 ulist_resetList(values
);
748 memcpy(en
, &defaultKeywordValues
, sizeof(UEnumeration
));
749 en
->context
= values
;
753 U_CAPI UBool U_EXPORT2
754 ucal_getTimeZoneTransitionDate(const UCalendar
* cal
, UTimeZoneTransitionType type
,
755 UDate
* transition
, UErrorCode
* status
)
757 if (U_FAILURE(*status
)) {
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
);
770 *transition
= tzt
.getTime();
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
)) {
783 int32_t resultLen
= 0;
784 UnicodeString resultWinID
;
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
);
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
)) {
801 int32_t resultLen
= 0;
802 UnicodeString resultID
;
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
);
813 // Apple-specific function uacal_getDayPeriod and helper functions/data
817 } DayPeriodNameToValue
;
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
},
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
;
841 return UADAYPERIOD_UNKNOWN
;
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;
861 enum { kSetNameMaxLen
= 8, kBoundaryTimeMaxLen
= 6, kDayPeriodEntriesMax
= 12 };
863 U_CAPI UADayPeriod U_EXPORT2
864 uacal_getDayPeriod( const char* locale
,
868 UErrorCode
* status
) {
869 UADayPeriod dayPeriod
= UADAYPERIOD_UNKNOWN
;
870 DayPeriodEntry dpEntries
[kDayPeriodEntriesMax
];
871 int32_t dpEntriesCount
= 0;
873 if (U_FAILURE(*status
)) {
876 if (hour
< 0 || hour
> 23 || minute
< 0 || minute
> 59) {
877 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
880 // get dayPeriods bundle
881 LocalUResourceBundlePointer
rb(ures_openDirect(NULL
, "dayPeriods", status
));
882 if (U_FAILURE(*status
)) {
885 // get locales/locales_selection subbundle
886 LocalUResourceBundlePointer
rbSub(ures_getByKey(rb
.getAlias(), formatStyle
? "locales": "locales_selection", NULL
, status
));
887 if (U_FAILURE(*status
)) {
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
) {
900 uprv_strcpy(lang
, "en"); // should be "root" but the data for root is currently missing
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
));
909 if (U_FAILURE(*status
)) {
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
)) {
919 // get rules subbundle
920 rbSub
.adoptInstead(ures_getByKey(rb
.getAlias(), "rules", rbSub
.orphan(), status
));
921 if (U_FAILURE(*status
)) {
924 // get ruleset from rules subbundle
925 rb
.adoptInstead(ures_getByKey(rbSub
.getAlias(), setName
, rb
.orphan(), status
));
926 if (U_FAILURE(*status
)) {
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
)) {
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
)) {
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) {
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
)) {
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
;
969 if (dpEntriesCount
< kDayPeriodEntriesMax
) {
970 dpEntries
[dpEntriesCount
].startHour
= 24;
971 dpEntries
[dpEntriesCount
].startMin
= 0;
972 dpEntries
[dpEntriesCount
].value
= UADAYPERIOD_UNKNOWN
;
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.
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
};
984 while (dpIndex
< dpEntriesCount
- 1 && CompareDayPeriodEntries(&entryToMatch
, &dpEntries
[dpIndex
+ 1]) >= 0) {
987 if (CompareDayPeriodEntries(&entryToMatch
, &dpEntries
[dpIndex
]) >= 0) {
988 dayPeriod
= dpEntries
[dpIndex
].value
;
996 #endif /* #if !UCONFIG_NO_FORMATTING */