X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/729e4ab9bc6618bc3d8a898e575df7f4019e29ca..a0b4f637ba1a6c3c5651b61a69303b029bacf7d3:/icuSources/i18n/ucal.cpp?ds=sidebyside diff --git a/icuSources/i18n/ucal.cpp b/icuSources/i18n/ucal.cpp index cb060ab2..01f8cfab 100644 --- a/icuSources/i18n/ucal.cpp +++ b/icuSources/i18n/ucal.cpp @@ -1,16 +1,18 @@ /* ******************************************************************************* -* Copyright (C) 1996-2011, 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" @@ -45,6 +48,13 @@ _createTimeZone(const UChar* zoneID, int32_t len, UErrorCode* ec) { return zone; } +U_CAPI UEnumeration* U_EXPORT2 +ucal_openTimeZoneIDEnumeration(USystemTimeZoneType zoneType, const char* region, + const int32_t* rawOffset, UErrorCode* ec) { + return uenum_openFromStringEnumeration(TimeZone::createTimeZoneIDEnumeration( + zoneType, region, rawOffset, *ec), ec); +} + U_CAPI UEnumeration* U_EXPORT2 ucal_openTimeZones(UErrorCode* ec) { return uenum_openFromStringEnumeration(TimeZone::createEnumeration(), ec); @@ -192,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, @@ -251,6 +276,12 @@ ucal_setGregorianChange(UCalendar *cal, UDate date, UErrorCode *pErrorCode) { // Not if(gregocal == NULL) { // because we really want to work only with a GregorianCalendar, not with // its subclasses like BuddhistCalendar. + if (cpp_cal == NULL) { + // We normally don't check "this" pointers for NULL, but this here avoids + // compiler-generated exception-throwing code in case cal == NULL. + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return; + } if(typeid(*cpp_cal) != typeid(GregorianCalendar)) { *pErrorCode = U_UNSUPPORTED_ERROR; return; @@ -267,6 +298,12 @@ ucal_getGregorianChange(const UCalendar *cal, UErrorCode *pErrorCode) { const GregorianCalendar *gregocal = dynamic_cast(cpp_cal); // Not if(gregocal == NULL) { // see comments in ucal_setGregorianChange(). + if (cpp_cal == NULL) { + // We normally don't check "this" pointers for NULL, but this here avoids + // compiler-generated exception-throwing code in case cal == NULL. + *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + return (UDate)0; + } if(typeid(*cpp_cal) != typeid(GregorianCalendar)) { *pErrorCode = U_UNSUPPORTED_ERROR; return (UDate)0; @@ -289,6 +326,12 @@ ucal_getAttribute( const UCalendar* cal, case UCAL_MINIMAL_DAYS_IN_FIRST_WEEK: return ((Calendar*)cal)->getMinimalDaysInFirstWeek(); + case UCAL_REPEATED_WALL_TIME: + return ((Calendar*)cal)->getRepeatedWallTimeOption(); + + case UCAL_SKIPPED_WALL_TIME: + return ((Calendar*)cal)->getSkippedWallTimeOption(); + default: break; } @@ -313,6 +356,14 @@ ucal_setAttribute( UCalendar* cal, case UCAL_MINIMAL_DAYS_IN_FIRST_WEEK: ((Calendar*)cal)->setMinimalDaysInFirstWeek((uint8_t)newValue); break; + + case UCAL_REPEATED_WALL_TIME: + ((Calendar*)cal)->setRepeatedWallTimeOption((UCalendarWallTimeOption)newValue); + break; + + case UCAL_SKIPPED_WALL_TIME: + ((Calendar*)cal)->setSkippedWallTimeOption((UCalendarWallTimeOption)newValue); + break; } } @@ -609,6 +660,11 @@ static const char * const CAL_TYPES[] = { "coptic", "ethiopic", "ethiopic-amete-alem", + "iso8601", + "dangi", + "islamic-umalqura", + "islamic-tbla", + "islamic-rgsa", NULL }; @@ -620,8 +676,7 @@ ucal_getKeywordValuesForLocale(const char * /* key */, const char* locale, UBool prefRegionLength = uloc_getCountry(locale, prefRegion, sizeof(prefRegion), status); if (prefRegionLength == 0) { char loc[ULOC_FULLNAME_CAPACITY] = ""; - int32_t locLength = 0; - locLength = uloc_addLikelySubtags(locale, loc, sizeof(loc), status); + uloc_addLikelySubtags(locale, loc, sizeof(loc), status); prefRegionLength = uloc_getCountry(loc, prefRegion, sizeof(prefRegion), status); } @@ -695,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 */