+U_CAPI UBool U_EXPORT2
+ucal_getTimeZoneTransitionDate(const UCalendar* cal, UTimeZoneTransitionType type,
+ UDate* transition, UErrorCode* status)
+{
+ if (U_FAILURE(*status)) {
+ return FALSE;
+ }
+ UDate base = ((Calendar*)cal)->getTime(*status);
+ const TimeZone& tz = ((Calendar*)cal)->getTimeZone();
+ const BasicTimeZone * btz = dynamic_cast<const BasicTimeZone *>(&tz);
+ if (btz != NULL && U_SUCCESS(*status)) {
+ TimeZoneTransition tzt;
+ UBool inclusive = (type == UCAL_TZ_TRANSITION_NEXT_INCLUSIVE || type == UCAL_TZ_TRANSITION_PREVIOUS_INCLUSIVE);
+ UBool result = (type == UCAL_TZ_TRANSITION_NEXT || type == UCAL_TZ_TRANSITION_NEXT_INCLUSIVE)?
+ btz->getNextTransition(base, inclusive, tzt):
+ btz->getPreviousTransition(base, inclusive, tzt);
+ if (result) {
+ *transition = tzt.getTime();
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+U_CAPI int32_t U_EXPORT2
+ucal_getWindowsTimeZoneID(const UChar* id, int32_t len, UChar* winid, int32_t winidCapacity, UErrorCode* status) {
+ if (U_FAILURE(*status)) {
+ return 0;
+ }
+
+ int32_t resultLen = 0;
+ UnicodeString resultWinID;
+
+ TimeZone::getWindowsID(UnicodeString(id, len), resultWinID, *status);
+ if (U_SUCCESS(*status) && resultWinID.length() > 0) {
+ resultLen = resultWinID.length();
+ resultWinID.extract(winid, winidCapacity, *status);
+ }
+
+ return resultLen;
+}
+
+U_CAPI int32_t U_EXPORT2
+ucal_getTimeZoneIDForWindowsID(const UChar* winid, int32_t len, const char* region, UChar* id, int32_t idCapacity, UErrorCode* status) {
+ if (U_FAILURE(*status)) {
+ return 0;
+ }
+
+ int32_t resultLen = 0;
+ UnicodeString resultID;
+
+ TimeZone::getIDForWindowsID(UnicodeString(winid, len), region, resultID, *status);
+ if (U_SUCCESS(*status) && resultID.length() > 0) {
+ resultLen = resultID.length();
+ resultID.extract(id, idCapacity, *status);
+ }
+
+ return resultLen;
+}
+
+// Apple-specific function uacal_getDayPeriod and helper functions/data
+typedef struct {
+ const char* name;
+ UADayPeriod value;
+} DayPeriodNameToValue;
+
+static const DayPeriodNameToValue dpNameToValue[] = {
+ { "afternoon1", UADAYPERIOD_AFTERNOON1 },
+ { "afternoon2", UADAYPERIOD_AFTERNOON2 },
+ { "evening1", UADAYPERIOD_EVENING1 },
+ { "evening2", UADAYPERIOD_EVENING2 },
+ { "midnight", UADAYPERIOD_MIDNIGHT },
+ { "morning1", UADAYPERIOD_MORNING1 },
+ { "morning2", UADAYPERIOD_MORNING2 },
+ { "night1", UADAYPERIOD_NIGHT1 },
+ { "night2", UADAYPERIOD_NIGHT2 },
+ { "noon", UADAYPERIOD_NOON },
+};
+
+static UADayPeriod dayPeriodFromName(const char* name) {
+ const DayPeriodNameToValue * dpNameToValuePtr = dpNameToValue;
+ const DayPeriodNameToValue * dpNameToValueLim = dpNameToValue + UPRV_LENGTHOF(dpNameToValue);
+ // simple linear search, dpNameToValue is small enough
+ for (; dpNameToValuePtr < dpNameToValueLim; dpNameToValuePtr++) {
+ if (uprv_strcmp(name, dpNameToValuePtr->name) == 0) {
+ return dpNameToValuePtr->value;
+ }
+ }
+ return UADAYPERIOD_UNKNOWN;
+}
+
+typedef struct {
+ int32_t startHour;
+ int32_t startMin;
+ UADayPeriod value;
+} DayPeriodEntry;
+
+int CompareDayPeriodEntries(const void* entry1Ptr, const void* entry2Ptr) {
+ const DayPeriodEntry * dpEntry1Ptr = (const DayPeriodEntry *)entry1Ptr;
+ const DayPeriodEntry * dpEntry2Ptr = (const DayPeriodEntry *)entry2Ptr;
+ if (dpEntry1Ptr->startHour < dpEntry2Ptr->startHour) return -1;
+ if (dpEntry1Ptr->startHour > dpEntry2Ptr->startHour) return 1;
+ // here hours are equal
+ if (dpEntry1Ptr->startMin < dpEntry2Ptr->startMin) return -1;
+ if (dpEntry1Ptr->startMin > dpEntry2Ptr->startMin) return 1;
+ return 0;
+}
+
+enum { kSetNameMaxLen = 8, kBoundaryTimeMaxLen = 6, kDayPeriodEntriesMax = 12 };
+
+U_CAPI UADayPeriod U_EXPORT2
+uacal_getDayPeriod( const char* locale,
+ int32_t hour,
+ int32_t minute,
+ UBool formatStyle,
+ UErrorCode* status ) {
+ UADayPeriod dayPeriod = UADAYPERIOD_UNKNOWN;
+ DayPeriodEntry dpEntries[kDayPeriodEntriesMax];
+ int32_t dpEntriesCount = 0;
+
+ if (U_FAILURE(*status)) {
+ return dayPeriod;
+ }
+ if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR;
+ return dayPeriod;
+ }
+ // get dayPeriods bundle
+ LocalUResourceBundlePointer rb(ures_openDirect(NULL, "dayPeriods", status));
+ if (U_FAILURE(*status)) {
+ return dayPeriod;
+ }
+ // get locales/locales_selection subbundle
+ LocalUResourceBundlePointer rbSub(ures_getByKey(rb.getAlias(), formatStyle? "locales": "locales_selection", NULL, status));
+ if (U_FAILURE(*status)) {
+ return dayPeriod;
+ }
+ // get bundle for language (maps to setName)
+ char lang[ULOC_LANG_CAPACITY] = {0};
+ if (locale != NULL) {
+ UErrorCode tempStatus = U_ZERO_ERROR;
+ uloc_getLanguage(locale, lang, ULOC_LANG_CAPACITY, &tempStatus);
+ if (U_FAILURE(*status) || *status == U_STRING_NOT_TERMINATED_WARNING) {
+ lang[0] = 0;
+ }
+ }
+ if (lang[0] == 0) {
+ uprv_strcpy(lang, "en"); // should be "root" but the data for root is currently missing
+ }
+ LocalUResourceBundlePointer rbLang(ures_getByKey(rbSub.getAlias(), lang, NULL, status));
+ if (U_FAILURE(*status)) {
+ // should only happen if lang was not [root] en
+ // fallback should be "root" but the data for root is currently missing, use "en"
+ *status = U_ZERO_ERROR;
+ rbLang.adoptInstead(ures_getByKey(rbSub.getAlias(), "en", rbLang.orphan(), status));
+ }
+ if (U_FAILURE(*status)) {
+ return dayPeriod;
+ }
+ // get setName from language bundle
+ char setName[kSetNameMaxLen] = {0};
+ int32_t setNameLen = kSetNameMaxLen;
+ ures_getUTF8String(rbLang.getAlias(), setName, &setNameLen, TRUE, status);
+ if (U_FAILURE(*status)) {
+ return dayPeriod;
+ }
+ // get rules subbundle
+ rbSub.adoptInstead(ures_getByKey(rb.getAlias(), "rules", rbSub.orphan(), status));
+ if (U_FAILURE(*status)) {
+ return dayPeriod;
+ }
+ // get ruleset from rules subbundle
+ rb.adoptInstead(ures_getByKey(rbSub.getAlias(), setName, rb.orphan(), status));
+ if (U_FAILURE(*status)) {
+ return dayPeriod;
+ }
+ // OK, we should finally have a ruleset (works to here).
+ // Iterate over it to collect entries
+ LocalUResourceBundlePointer rbBound;
+ while (ures_hasNext(rb.getAlias())) {
+ rbSub.adoptInstead(ures_getNextResource(rb.getAlias(), rbSub.orphan(), status));
+ if (U_FAILURE(*status)) {
+ return dayPeriod;
+ }
+ // rbSub now has the bundle for a particular dayPeriod such as morning1, afternoon2, noon
+ UADayPeriod dpForBundle = dayPeriodFromName(ures_getKey(rbSub.getAlias()));
+ while (ures_hasNext(rbSub.getAlias())) {
+ rbBound.adoptInstead(ures_getNextResource(rbSub.getAlias(), rbBound.orphan(), status));
+ if (U_FAILURE(*status)) {
+ return dayPeriod;
+ }
+ // rbBound now has the bundle for a particular time period boundary such as at, from, after.
+ // This is either of type URES_STRING (size=1) or of type URES_ARRAY (size > 1)
+ const char *boundaryType = ures_getKey(rbBound.getAlias());
+ // skip boundaryType "before", it is redundant if we have at, from, after
+ if (uprv_strcmp(boundaryType, "before") == 0) {
+ continue;
+ }
+ int32_t boundaryMinute = (uprv_strcmp(boundaryType, "after") == 0)? 1: 0;
+ int32_t boundaryTimeIndex, boundaryTimeCount = ures_getSize(rbBound.getAlias());
+ for (boundaryTimeIndex = 0; boundaryTimeIndex < boundaryTimeCount; boundaryTimeIndex++) {
+ char boundaryTimeStr[kBoundaryTimeMaxLen];
+ int32_t boundaryTimeStrLen = kBoundaryTimeMaxLen;
+ ures_getUTF8StringByIndex(rbBound.getAlias(), boundaryTimeIndex, boundaryTimeStr, &boundaryTimeStrLen, TRUE, status);
+ if (U_FAILURE(*status)) {
+ return dayPeriod;
+ }
+ if (dpEntriesCount < kDayPeriodEntriesMax) {
+ dpEntries[dpEntriesCount].startHour = atoi(boundaryTimeStr); // can depend on POSIX locale (fortunately no decimal sep here)
+ dpEntries[dpEntriesCount].startMin = boundaryMinute;
+ dpEntries[dpEntriesCount].value = dpForBundle;
+ dpEntriesCount++;
+ }
+ }
+ }
+ }
+ if (dpEntriesCount < kDayPeriodEntriesMax) {
+ dpEntries[dpEntriesCount].startHour = 24;
+ dpEntries[dpEntriesCount].startMin = 0;
+ dpEntries[dpEntriesCount].value = UADAYPERIOD_UNKNOWN;
+ dpEntriesCount++;
+ }
+ // We have collected all of the rule data, now sort by time
+ qsort(dpEntries, dpEntriesCount, sizeof(DayPeriodEntry), CompareDayPeriodEntries);
+ // OK, all of the above is what we would do in an "open" function if we were using an
+ // open/use/close model for this; the object would just have the sorted array above.
+
+ // Now we use the sorted array to find the dayPeriod matching the supplied time.
+ // Only a few entries, linear search OK
+ DayPeriodEntry entryToMatch = { hour, minute, UADAYPERIOD_UNKNOWN };
+ int32_t dpIndex = 0;
+ while (dpIndex < dpEntriesCount - 1 && CompareDayPeriodEntries(&entryToMatch, &dpEntries[dpIndex + 1]) >= 0) {
+ dpIndex++;
+ }
+ if (CompareDayPeriodEntries(&entryToMatch, &dpEntries[dpIndex]) >= 0) {
+ dayPeriod = dpEntries[dpIndex].value;
+ }
+
+ return dayPeriod;
+}
+
+
+