X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/4388f060552cc537e71e957d32f35e9d75a61233..a0b4f637ba1a6c3c5651b61a69303b029bacf7d3:/icuSources/i18n/ucal.cpp?ds=sidebyside diff --git a/icuSources/i18n/ucal.cpp b/icuSources/i18n/ucal.cpp index 2a8db94b..01f8cfab 100644 --- a/icuSources/i18n/ucal.cpp +++ b/icuSources/i18n/ucal.cpp @@ -1,16 +1,18 @@ /* ******************************************************************************* -* Copyright (C) 1996-2012, International Business Machines +* Copyright (C) 1996-2015, International Business Machines * Corporation and others. All Rights Reserved. ******************************************************************************* */ -#include // for 'typeid' to work +#include "utypeinfo.h" // for 'typeid' to work #include "unicode/utypes.h" #if !UCONFIG_NO_FORMATTING +#include // Apple addition for uacal_getDayPeriod + #include "unicode/ucal.h" #include "unicode/uloc.h" #include "unicode/calendar.h" @@ -19,6 +21,7 @@ #include "unicode/simpletz.h" #include "unicode/ustring.h" #include "unicode/strenum.h" +#include "unicode/localpointer.h" #include "cmemory.h" #include "cstring.h" #include "ustrenum.h" @@ -199,6 +202,21 @@ ucal_setTimeZone( UCalendar* cal, } } +U_CAPI int32_t U_EXPORT2 +ucal_getTimeZoneID(const UCalendar *cal, + UChar *result, + int32_t resultLength, + UErrorCode *status) +{ + if (U_FAILURE(*status)) { + return 0; + } + const TimeZone& tz = ((Calendar*)cal)->getTimeZone(); + UnicodeString id; + tz.getID(id); + return id.extract(result, resultLength, *status); +} + U_CAPI int32_t U_EXPORT2 ucal_getTimeZoneDisplayName(const UCalendar* cal, UCalendarDisplayNameType type, @@ -642,6 +660,11 @@ static const char * const CAL_TYPES[] = { "coptic", "ethiopic", "ethiopic-amete-alem", + "iso8601", + "dangi", + "islamic-umalqura", + "islamic-tbla", + "islamic-rgsa", NULL }; @@ -727,4 +750,247 @@ ucal_getKeywordValuesForLocale(const char * /* key */, const char* locale, UBool return en; } +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(&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; +} + + + #endif /* #if !UCONFIG_NO_FORMATTING */