]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/ucal.cpp
ICU-551.51.4.tar.gz
[apple/icu.git] / icuSources / i18n / ucal.cpp
index 351899bdcf2201195600bf532bceabaafc5bfe4c..01f8cfabb7d68517854ee7b96798e9df50ad80bf 100644 (file)
@@ -1,6 +1,6 @@
 /*
 *******************************************************************************
-*   Copyright (C) 1996-2013, International Business Machines
+*   Copyright (C) 1996-2015, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 *******************************************************************************
 */
@@ -11,6 +11,8 @@
 
 #if !UCONFIG_NO_FORMATTING
 
+#include <stdlib.h> // Apple addition for uacal_getDayPeriod
+
 #include "unicode/ucal.h"
 #include "unicode/uloc.h"
 #include "unicode/calendar.h"
@@ -772,7 +774,6 @@ ucal_getTimeZoneTransitionDate(const UCalendar* cal, UTimeZoneTransitionType typ
     return FALSE;
 }
 
-#ifndef U_HIDE_DRAFT_API
 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)) {
@@ -809,6 +810,187 @@ ucal_getTimeZoneIDForWindowsID(const UChar* winid, int32_t len, const char* regi
     return resultLen;
 }
 
-#endif /* U_HIDE_DRAFT_API */
+// 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;
+}
+
+
 
 #endif /* #if !UCONFIG_NO_FORMATTING */