| /* |
| ******************************************************************************* |
| * Copyright (C) 1997-2015, International Business Machines Corporation and |
| * others. All Rights Reserved. |
| ******************************************************************************* |
| * |
| * File TIMEZONE.CPP |
| * |
| * Modification History: |
| * |
| * Date Name Description |
| * 12/05/96 clhuang Creation. |
| * 04/21/97 aliu General clean-up and bug fixing. |
| * 05/08/97 aliu Fixed Hashtable code per code review. |
| * 07/09/97 helena Changed createInstance to createDefault. |
| * 07/29/97 aliu Updated with all-new list of 96 UNIX-derived |
| * TimeZones. Changed mechanism to load from static |
| * array rather than resource bundle. |
| * 07/07/1998 srl Bugfixes from the Java side: UTC GMT CAT NST |
| * Added getDisplayName API |
| * going to add custom parsing. |
| * |
| * ISSUES: |
| * - should getDisplayName cache something? |
| * - should custom time zones be cached? [probably] |
| * 08/10/98 stephen Brought getDisplayName() API in-line w/ conventions |
| * 08/19/98 stephen Changed createTimeZone() to never return 0 |
| * 09/02/98 stephen Added getOffset(monthLen) and hasSameRules() |
| * 09/15/98 stephen Added getStaticClassID() |
| * 02/22/99 stephen Removed character literals for EBCDIC safety |
| * 05/04/99 stephen Changed initDefault() for Mutex issues |
| * 07/12/99 helena HPUX 11 CC Port. |
| * 12/03/99 aliu Moved data out of static table into icudata.dll. |
| * Substantial rewrite of zone lookup, default zone, and |
| * available IDs code. Misc. cleanup. |
| *********************************************************************************/ |
| |
| #include "starboard/client_porting/poem/assert_poem.h" |
| #include "utypeinfo.h" // for 'typeid' to work |
| |
| #include "unicode/utypes.h" |
| #include "unicode/ustring.h" |
| #include "uassert.h" |
| #include "ustr_imp.h" |
| |
| #ifdef U_DEBUG_TZ |
| # include <stdio.h> |
| # include "uresimp.h" // for debugging |
| |
| static void debug_tz_loc(const char *f, int32_t l) |
| { |
| fprintf(stderr, "%s:%d: ", f, l); |
| } |
| |
| static void debug_tz_msg(const char *pat, ...) |
| { |
| va_list ap; |
| va_start(ap, pat); |
| vfprintf(stderr, pat, ap); |
| fflush(stderr); |
| } |
| static char gStrBuf[256]; |
| #define U_DEBUG_TZ_STR(x) u_austrncpy(gStrBuf,x,sizeof(gStrBuf)-1) |
| // must use double parens, i.e.: U_DEBUG_TZ_MSG(("four is: %d",4)); |
| #define U_DEBUG_TZ_MSG(x) {debug_tz_loc(__FILE__,__LINE__);debug_tz_msg x;} |
| #else |
| #define U_DEBUG_TZ_MSG(x) |
| #endif |
| |
| #if !UCONFIG_NO_FORMATTING |
| |
| #include "unicode/simpletz.h" |
| #include "unicode/calendar.h" |
| #include "unicode/gregocal.h" |
| #include "unicode/ures.h" |
| #include "unicode/tzfmt.h" |
| #include "unicode/numfmt.h" |
| #include "gregoimp.h" |
| #include "uresimp.h" // struct UResourceBundle |
| #include "olsontz.h" |
| #include "mutex.h" |
| #include "unicode/udata.h" |
| #include "ucln_in.h" |
| #include "cstring.h" |
| #include "cmemory.h" |
| #include "unicode/strenum.h" |
| #include "uassert.h" |
| #include "zonemeta.h" |
| |
| #define kZONEINFO "zoneinfo64" |
| #define kREGIONS "Regions" |
| #define kZONES "Zones" |
| #define kRULES "Rules" |
| #define kNAMES "Names" |
| #define kTZVERSION "TZVersion" |
| #define kLINKS "links" |
| #define kMAX_CUSTOM_HOUR 23 |
| #define kMAX_CUSTOM_MIN 59 |
| #define kMAX_CUSTOM_SEC 59 |
| #define MINUS 0x002D |
| #define PLUS 0x002B |
| #define ZERO_DIGIT 0x0030 |
| #define COLON 0x003A |
| |
| // Static data and constants |
| |
| static const UChar WORLD[] = {0x30, 0x30, 0x31, 0x00}; /* "001" */ |
| |
| static const UChar GMT_ID[] = {0x47, 0x4D, 0x54, 0x00}; /* "GMT" */ |
| static const UChar UNKNOWN_ZONE_ID[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x6E, 0x6B, 0x6E, 0x6F, 0x77, 0x6E, 0x00}; /* "Etc/Unknown" */ |
| static const int32_t GMT_ID_LENGTH = 3; |
| static const int32_t UNKNOWN_ZONE_ID_LENGTH = 11; |
| |
| static icu::TimeZone* DEFAULT_ZONE = NULL; |
| static icu::UInitOnce gDefaultZoneInitOnce = U_INITONCE_INITIALIZER; |
| |
| static icu::TimeZone* _GMT = NULL; |
| static icu::TimeZone* _UNKNOWN_ZONE = NULL; |
| static icu::UInitOnce gStaticZonesInitOnce = U_INITONCE_INITIALIZER; |
| |
| static char TZDATA_VERSION[16]; |
| static icu::UInitOnce gTZDataVersionInitOnce = U_INITONCE_INITIALIZER; |
| |
| static int32_t* MAP_SYSTEM_ZONES = NULL; |
| static int32_t* MAP_CANONICAL_SYSTEM_ZONES = NULL; |
| static int32_t* MAP_CANONICAL_SYSTEM_LOCATION_ZONES = NULL; |
| |
| static int32_t LEN_SYSTEM_ZONES = 0; |
| static int32_t LEN_CANONICAL_SYSTEM_ZONES = 0; |
| static int32_t LEN_CANONICAL_SYSTEM_LOCATION_ZONES = 0; |
| |
| static icu::UInitOnce gSystemZonesInitOnce = U_INITONCE_INITIALIZER; |
| static icu::UInitOnce gCanonicalZonesInitOnce = U_INITONCE_INITIALIZER; |
| static icu::UInitOnce gCanonicalLocationZonesInitOnce = U_INITONCE_INITIALIZER; |
| |
| U_CDECL_BEGIN |
| static UBool U_CALLCONV timeZone_cleanup(void) |
| { |
| U_NAMESPACE_USE |
| delete DEFAULT_ZONE; |
| DEFAULT_ZONE = NULL; |
| gDefaultZoneInitOnce.reset(); |
| |
| delete _GMT; |
| _GMT = NULL; |
| delete _UNKNOWN_ZONE; |
| _UNKNOWN_ZONE = NULL; |
| gStaticZonesInitOnce.reset(); |
| |
| uprv_memset(TZDATA_VERSION, 0, sizeof(TZDATA_VERSION)); |
| gTZDataVersionInitOnce.reset(); |
| |
| LEN_SYSTEM_ZONES = 0; |
| uprv_free(MAP_SYSTEM_ZONES); |
| MAP_SYSTEM_ZONES = 0; |
| gSystemZonesInitOnce.reset(); |
| |
| LEN_CANONICAL_SYSTEM_ZONES = 0; |
| uprv_free(MAP_CANONICAL_SYSTEM_ZONES); |
| MAP_CANONICAL_SYSTEM_ZONES = 0; |
| gCanonicalZonesInitOnce.reset(); |
| |
| LEN_CANONICAL_SYSTEM_LOCATION_ZONES = 0; |
| uprv_free(MAP_CANONICAL_SYSTEM_LOCATION_ZONES); |
| MAP_CANONICAL_SYSTEM_LOCATION_ZONES = 0; |
| gCanonicalLocationZonesInitOnce.reset(); |
| |
| return TRUE; |
| } |
| U_CDECL_END |
| |
| U_NAMESPACE_BEGIN |
| |
| static int32_t findInStringArray(UResourceBundle* array, const UnicodeString& id, UErrorCode &status) |
| { |
| UnicodeString copy; |
| const UChar *u; |
| int32_t len; |
| |
| int32_t start = 0; |
| int32_t limit = ures_getSize(array); |
| int32_t mid; |
| int32_t lastMid = INT32_MAX; |
| if(U_FAILURE(status) || (limit < 1)) { |
| return -1; |
| } |
| U_DEBUG_TZ_MSG(("fisa: Looking for %s, between %d and %d\n", U_DEBUG_TZ_STR(UnicodeString(id).getTerminatedBuffer()), start, limit)); |
| |
| for (;;) { |
| mid = (int32_t)((start + limit) / 2); |
| if (lastMid == mid) { /* Have we moved? */ |
| break; /* We haven't moved, and it wasn't found. */ |
| } |
| lastMid = mid; |
| u = ures_getStringByIndex(array, mid, &len, &status); |
| if (U_FAILURE(status)) { |
| break; |
| } |
| U_DEBUG_TZ_MSG(("tz: compare to %s, %d .. [%d] .. %d\n", U_DEBUG_TZ_STR(u), start, mid, limit)); |
| copy.setTo(TRUE, u, len); |
| int r = id.compare(copy); |
| if(r==0) { |
| U_DEBUG_TZ_MSG(("fisa: found at %d\n", mid)); |
| return mid; |
| } else if(r<0) { |
| limit = mid; |
| } else { |
| start = mid; |
| } |
| } |
| U_DEBUG_TZ_MSG(("fisa: not found\n")); |
| return -1; |
| } |
| |
| /** |
| * Fetch a specific zone by name. Replaces the getByKey call. |
| * @param top Top timezone resource |
| * @param id Time zone ID |
| * @param oldbundle Bundle for reuse (or NULL). see 'ures_open()' |
| * @return the zone's bundle if found, or undefined if error. Reuses oldbundle. |
| */ |
| static UResourceBundle* getZoneByName(const UResourceBundle* top, const UnicodeString& id, UResourceBundle *oldbundle, UErrorCode& status) { |
| // load the Rules object |
| UResourceBundle *tmp = ures_getByKey(top, kNAMES, NULL, &status); |
| |
| // search for the string |
| int32_t idx = findInStringArray(tmp, id, status); |
| |
| if((idx == -1) && U_SUCCESS(status)) { |
| // not found |
| status = U_MISSING_RESOURCE_ERROR; |
| //ures_close(oldbundle); |
| //oldbundle = NULL; |
| } else { |
| U_DEBUG_TZ_MSG(("gzbn: oldbundle= size %d, type %d, %s\n", ures_getSize(tmp), ures_getType(tmp), u_errorName(status))); |
| tmp = ures_getByKey(top, kZONES, tmp, &status); // get Zones object from top |
| U_DEBUG_TZ_MSG(("gzbn: loaded ZONES, size %d, type %d, path %s %s\n", ures_getSize(tmp), ures_getType(tmp), ures_getPath(tmp), u_errorName(status))); |
| oldbundle = ures_getByIndex(tmp, idx, oldbundle, &status); // get nth Zone object |
| U_DEBUG_TZ_MSG(("gzbn: loaded z#%d, size %d, type %d, path %s, %s\n", idx, ures_getSize(oldbundle), ures_getType(oldbundle), ures_getPath(oldbundle), u_errorName(status))); |
| } |
| ures_close(tmp); |
| if(U_FAILURE(status)) { |
| //ures_close(oldbundle); |
| return NULL; |
| } else { |
| return oldbundle; |
| } |
| } |
| |
| |
| UResourceBundle* TimeZone::loadRule(const UResourceBundle* top, const UnicodeString& ruleid, UResourceBundle* oldbundle, UErrorCode& status) { |
| char key[64]; |
| ruleid.extract(0, sizeof(key)-1, key, (int32_t)sizeof(key)-1, US_INV); |
| U_DEBUG_TZ_MSG(("loadRule(%s)\n", key)); |
| UResourceBundle *r = ures_getByKey(top, kRULES, oldbundle, &status); |
| U_DEBUG_TZ_MSG(("loadRule(%s) -> kRULES [%s]\n", key, u_errorName(status))); |
| r = ures_getByKey(r, key, r, &status); |
| U_DEBUG_TZ_MSG(("loadRule(%s) -> item [%s]\n", key, u_errorName(status))); |
| return r; |
| } |
| |
| /** |
| * Given an ID, open the appropriate resource for the given time zone. |
| * Dereference aliases if necessary. |
| * @param id zone id |
| * @param res resource, which must be ready for use (initialized but not open) |
| * @param ec input-output error code |
| * @return top-level resource bundle |
| */ |
| static UResourceBundle* openOlsonResource(const UnicodeString& id, |
| UResourceBundle& res, |
| UErrorCode& ec) |
| { |
| #if U_DEBUG_TZ |
| char buf[128]; |
| id.extract(0, sizeof(buf)-1, buf, sizeof(buf), ""); |
| #endif |
| UResourceBundle *top = ures_openDirect(0, kZONEINFO, &ec); |
| U_DEBUG_TZ_MSG(("pre: res sz=%d\n", ures_getSize(&res))); |
| /* &res = */ getZoneByName(top, id, &res, ec); |
| // Dereference if this is an alias. Docs say result should be 1 |
| // but it is 0 in 2.8 (?). |
| U_DEBUG_TZ_MSG(("Loading zone '%s' (%s, size %d) - %s\n", buf, ures_getKey((UResourceBundle*)&res), ures_getSize(&res), u_errorName(ec))); |
| if (ures_getType(&res) == URES_INT) { |
| int32_t deref = ures_getInt(&res, &ec) + 0; |
| U_DEBUG_TZ_MSG(("getInt: %s - type is %d\n", u_errorName(ec), ures_getType(&res))); |
| UResourceBundle *ares = ures_getByKey(top, kZONES, NULL, &ec); // dereference Zones section |
| ures_getByIndex(ares, deref, &res, &ec); |
| ures_close(ares); |
| U_DEBUG_TZ_MSG(("alias to #%d (%s) - %s\n", deref, "??", u_errorName(ec))); |
| } else { |
| U_DEBUG_TZ_MSG(("not an alias - size %d\n", ures_getSize(&res))); |
| } |
| U_DEBUG_TZ_MSG(("%s - final status is %s\n", buf, u_errorName(ec))); |
| return top; |
| } |
| |
| // ------------------------------------- |
| |
| namespace { |
| |
| void U_CALLCONV initStaticTimeZones() { |
| // Initialize _GMT independently of other static data; it should |
| // be valid even if we can't load the time zone UDataMemory. |
| ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup); |
| _UNKNOWN_ZONE = new SimpleTimeZone(0, UnicodeString(TRUE, UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH)); |
| _GMT = new SimpleTimeZone(0, UnicodeString(TRUE, GMT_ID, GMT_ID_LENGTH)); |
| } |
| |
| } // anonymous namespace |
| |
| const TimeZone& U_EXPORT2 |
| TimeZone::getUnknown() |
| { |
| umtx_initOnce(gStaticZonesInitOnce, &initStaticTimeZones); |
| return *_UNKNOWN_ZONE; |
| } |
| |
| const TimeZone* U_EXPORT2 |
| TimeZone::getGMT(void) |
| { |
| umtx_initOnce(gStaticZonesInitOnce, &initStaticTimeZones); |
| return _GMT; |
| } |
| |
| // ***************************************************************************** |
| // class TimeZone |
| // ***************************************************************************** |
| |
| UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(TimeZone) |
| |
| TimeZone::TimeZone() |
| : UObject(), fID() |
| { |
| } |
| |
| // ------------------------------------- |
| |
| TimeZone::TimeZone(const UnicodeString &id) |
| : UObject(), fID(id) |
| { |
| } |
| |
| // ------------------------------------- |
| |
| TimeZone::~TimeZone() |
| { |
| } |
| |
| // ------------------------------------- |
| |
| TimeZone::TimeZone(const TimeZone &source) |
| : UObject(source), fID(source.fID) |
| { |
| } |
| |
| // ------------------------------------- |
| |
| TimeZone & |
| TimeZone::operator=(const TimeZone &right) |
| { |
| if (this != &right) fID = right.fID; |
| return *this; |
| } |
| |
| // ------------------------------------- |
| |
| UBool |
| TimeZone::operator==(const TimeZone& that) const |
| { |
| return typeid(*this) == typeid(that) && |
| fID == that.fID; |
| } |
| |
| // ------------------------------------- |
| |
| namespace { |
| TimeZone* |
| createSystemTimeZone(const UnicodeString& id, UErrorCode& ec) { |
| if (U_FAILURE(ec)) { |
| return NULL; |
| } |
| TimeZone* z = 0; |
| UResourceBundle res; |
| ures_initStackObject(&res); |
| U_DEBUG_TZ_MSG(("pre-err=%s\n", u_errorName(ec))); |
| UResourceBundle *top = openOlsonResource(id, res, ec); |
| U_DEBUG_TZ_MSG(("post-err=%s\n", u_errorName(ec))); |
| if (U_SUCCESS(ec)) { |
| z = new OlsonTimeZone(top, &res, id, ec); |
| if (z == NULL) { |
| U_DEBUG_TZ_MSG(("cstz: olson time zone failed to initialize - err %s\n", u_errorName(ec))); |
| } |
| } |
| ures_close(&res); |
| ures_close(top); |
| if (U_FAILURE(ec)) { |
| U_DEBUG_TZ_MSG(("cstz: failed to create, err %s\n", u_errorName(ec))); |
| delete z; |
| z = 0; |
| } |
| return z; |
| } |
| |
| /** |
| * Lookup the given name in our system zone table. If found, |
| * instantiate a new zone of that name and return it. If not |
| * found, return 0. |
| */ |
| TimeZone* |
| createSystemTimeZone(const UnicodeString& id) { |
| UErrorCode ec = U_ZERO_ERROR; |
| return createSystemTimeZone(id, ec); |
| } |
| |
| // winIdOut is only modified if the creation of TimeZone was successful. This |
| // allows the input variable to also be the output variable. |
| TimeZone* CreateTimeZoneFromWindowsID(const UnicodeString& id, |
| UnicodeString* winIdOut) { |
| UErrorCode status = U_ZERO_ERROR; |
| UnicodeString idOut; |
| TimeZone::getIDForWindowsID(id, NULL, idOut, status); |
| |
| if ((idOut.length() > 0) && (status == U_ZERO_ERROR)) { |
| idOut.append((UChar)0); |
| idOut.truncate(idOut.length()-1); |
| |
| TimeZone* time_zone = createSystemTimeZone(idOut); |
| if (time_zone) { |
| // SUCCESS!! |
| // Update variables. |
| if (winIdOut) { |
| *winIdOut = idOut; |
| } |
| return time_zone; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| } // namespace anonymous. |
| |
| TimeZone* U_EXPORT2 |
| TimeZone::createTimeZone(const UnicodeString& ID) |
| { |
| /* We first try to lookup the zone ID in our system list. If this |
| * fails, we try to parse it as a custom string GMT[+-]hh:mm. If |
| * all else fails, we return GMT, which is probably not what the |
| * user wants, but at least is a functioning TimeZone object. |
| * |
| * We cannot return NULL, because that would break compatibility |
| * with the JDK. |
| */ |
| TimeZone* result = createSystemTimeZone(ID); |
| |
| if (result == NULL) { |
| U_DEBUG_TZ_MSG(("failed to load system time zone with id - falling to custom")); |
| result = createCustomTimeZone(ID); |
| } |
| if (result == NULL) { |
| U_DEBUG_TZ_MSG(("failed to load time zone with id - falling to Etc/Unknown(GMT)")); |
| const TimeZone& unknown = getUnknown(); |
| if (_UNKNOWN_ZONE == NULL) { // Cannot test (&unknown == NULL) because the |
| U_DEBUG_TZ_MSG(("failed to getUnknown()")); // behavior of NULL references is undefined. |
| } else { |
| result = unknown.clone(); |
| } |
| } |
| return result; |
| } |
| |
| // ------------------------------------- |
| |
| TimeZone* U_EXPORT2 |
| TimeZone::detectHostTimeZone() |
| { |
| // We access system timezone data through TPlatformUtilities, |
| // including tzset(), timezone, and tzname[]. |
| int32_t rawOffset = 0; |
| const char *hostID; |
| |
| // First, try to create a system timezone, based |
| // on the string ID in tzname[0]. |
| |
| uprv_tzset(); // Initialize tz... system data |
| |
| uprv_tzname_clear_cache(); |
| |
| // Get the timezone ID from the host. This function should do |
| // any required host-specific remapping; e.g., on Windows this |
| // function maps the Date and Time control panel setting to an |
| // ICU timezone ID. |
| hostID = uprv_tzname(0); |
| |
| // Invert sign because UNIX semantics are backwards |
| rawOffset = uprv_timezone() * -U_MILLIS_PER_SECOND; |
| |
| TimeZone* hostZone = NULL; |
| |
| /* Make sure that the string is NULL terminated to prevent BoundsChecker/Purify warnings. */ |
| UnicodeString hostStrID(hostID, -1, US_INV); |
| hostStrID.append((UChar)0); |
| hostStrID.truncate(hostStrID.length()-1); |
| hostZone = createSystemTimeZone(hostStrID); |
| |
| #ifdef STARBOARD |
| // Certain platforms may return an id that is un-abbreviated, such as |
| // "Pacific Standard Time". Therefore attempt to create a TimeZone for |
| // these platforms. |
| if (!hostZone) { |
| // hostStrID will be mutated iff hostZone was successfully created. |
| hostZone = CreateTimeZoneFromWindowsID(hostStrID, &hostStrID); |
| } |
| #endif |
| |
| #if U_PLATFORM_USES_ONLY_WIN32_API || defined(__LB_XB1__) |
| // hostID points to a heap-allocated location on Windows. |
| uprv_free(const_cast<char *>(hostID)); |
| #endif |
| |
| int32_t hostIDLen = hostStrID.length(); |
| if (hostZone != NULL && rawOffset != hostZone->getRawOffset() |
| && (3 <= hostIDLen && hostIDLen <= 4)) |
| { |
| // Uh oh. This probably wasn't a good id. |
| // It was probably an ambiguous abbreviation |
| delete hostZone; |
| hostZone = NULL; |
| } |
| |
| // Construct a fixed standard zone with the host's ID |
| // and raw offset. |
| if (hostZone == NULL) { |
| hostZone = new SimpleTimeZone(rawOffset, hostStrID); |
| } |
| |
| // If we _still_ don't have a time zone, use GMT. |
| // |
| // Note: This is extremely unlikely situation. If |
| // new SimpleTimeZone(...) above fails, the following |
| // code may also fail. |
| if (hostZone == NULL) { |
| const TimeZone* temptz = TimeZone::getGMT(); |
| // If we can't use GMT, get out. |
| if (temptz == NULL) { |
| return NULL; |
| } |
| hostZone = temptz->clone(); |
| } |
| |
| return hostZone; |
| } |
| |
| // ------------------------------------- |
| |
| /** |
| * Initialize DEFAULT_ZONE from the system default time zone. |
| * Upon return, DEFAULT_ZONE will not be NULL, unless operator new() |
| * returns NULL. |
| */ |
| static void U_CALLCONV initDefault() |
| { |
| ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup); |
| |
| // If setDefault() has already been called we can skip getting the |
| // default zone information from the system. |
| if (DEFAULT_ZONE != NULL) { |
| return; |
| } |
| |
| // NOTE: this code is safely single threaded, being only |
| // run via umtx_initOnce(). |
| // |
| // Some of the locale/timezone OS functions may not be thread safe, |
| // |
| // The operating system might actually use ICU to implement timezones. |
| // So we may have ICU calling ICU here, like on AIX. |
| // There shouldn't be a problem with this; initOnce does not hold a mutex |
| // while the init function is being run. |
| |
| // The code detecting the host time zone was separated from this |
| // and implemented as TimeZone::detectHostTimeZone() |
| |
| TimeZone *default_zone = TimeZone::detectHostTimeZone(); |
| |
| // The only way for DEFAULT_ZONE to be non-null at this point is if the user |
| // made a thread-unsafe call to setDefault() or adoptDefault() in another |
| // thread while this thread was doing something that required getting the default. |
| U_ASSERT(DEFAULT_ZONE == NULL); |
| |
| DEFAULT_ZONE = default_zone; |
| } |
| |
| // ------------------------------------- |
| |
| TimeZone* U_EXPORT2 |
| TimeZone::createDefault() |
| { |
| umtx_initOnce(gDefaultZoneInitOnce, initDefault); |
| return (DEFAULT_ZONE != NULL) ? DEFAULT_ZONE->clone() : NULL; |
| } |
| |
| // ------------------------------------- |
| |
| void U_EXPORT2 |
| TimeZone::adoptDefault(TimeZone* zone) |
| { |
| if (zone != NULL) |
| { |
| TimeZone *old = DEFAULT_ZONE; |
| DEFAULT_ZONE = zone; |
| delete old; |
| ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup); |
| } |
| } |
| // ------------------------------------- |
| |
| void U_EXPORT2 |
| TimeZone::setDefault(const TimeZone& zone) |
| { |
| adoptDefault(zone.clone()); |
| } |
| |
| //---------------------------------------------------------------------- |
| |
| |
| static void U_CALLCONV initMap(USystemTimeZoneType type, UErrorCode& ec) { |
| ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup); |
| |
| UResourceBundle *res = ures_openDirect(0, kZONEINFO, &ec); |
| res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section |
| if (U_SUCCESS(ec)) { |
| int32_t size = ures_getSize(res); |
| int32_t *m = (int32_t *)uprv_malloc(size * sizeof(int32_t)); |
| if (m == NULL) { |
| ec = U_MEMORY_ALLOCATION_ERROR; |
| } else { |
| int32_t numEntries = 0; |
| for (int32_t i = 0; i < size; i++) { |
| UnicodeString id = ures_getUnicodeStringByIndex(res, i, &ec); |
| if (U_FAILURE(ec)) { |
| break; |
| } |
| if (0 == id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH)) { |
| // exclude Etc/Unknown |
| continue; |
| } |
| if (type == UCAL_ZONE_TYPE_CANONICAL || type == UCAL_ZONE_TYPE_CANONICAL_LOCATION) { |
| UnicodeString canonicalID; |
| ZoneMeta::getCanonicalCLDRID(id, canonicalID, ec); |
| if (U_FAILURE(ec)) { |
| break; |
| } |
| if (canonicalID != id) { |
| // exclude aliases |
| continue; |
| } |
| } |
| if (type == UCAL_ZONE_TYPE_CANONICAL_LOCATION) { |
| const UChar *region = TimeZone::getRegion(id, ec); |
| if (U_FAILURE(ec)) { |
| break; |
| } |
| if (u_strcmp(region, WORLD) == 0) { |
| // exclude non-location ("001") |
| continue; |
| } |
| } |
| m[numEntries++] = i; |
| } |
| if (U_SUCCESS(ec)) { |
| int32_t *tmp = m; |
| m = (int32_t *)uprv_realloc(tmp, numEntries * sizeof(int32_t)); |
| if (m == NULL) { |
| // realloc failed.. use the original one even it has unused |
| // area at the end |
| m = tmp; |
| } |
| |
| switch(type) { |
| case UCAL_ZONE_TYPE_ANY: |
| U_ASSERT(MAP_SYSTEM_ZONES == NULL); |
| MAP_SYSTEM_ZONES = m; |
| LEN_SYSTEM_ZONES = numEntries; |
| break; |
| case UCAL_ZONE_TYPE_CANONICAL: |
| U_ASSERT(MAP_CANONICAL_SYSTEM_ZONES == NULL); |
| MAP_CANONICAL_SYSTEM_ZONES = m; |
| LEN_CANONICAL_SYSTEM_ZONES = numEntries; |
| break; |
| case UCAL_ZONE_TYPE_CANONICAL_LOCATION: |
| U_ASSERT(MAP_CANONICAL_SYSTEM_LOCATION_ZONES == NULL); |
| MAP_CANONICAL_SYSTEM_LOCATION_ZONES = m; |
| LEN_CANONICAL_SYSTEM_LOCATION_ZONES = numEntries; |
| break; |
| } |
| } |
| } |
| } |
| ures_close(res); |
| } |
| |
| |
| /** |
| * This is the default implementation for subclasses that do not |
| * override this method. This implementation calls through to the |
| * 8-argument getOffset() method after suitable computations, and |
| * correctly adjusts GMT millis to local millis when necessary. |
| */ |
| void TimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset, |
| int32_t& dstOffset, UErrorCode& ec) const { |
| if (U_FAILURE(ec)) { |
| return; |
| } |
| |
| rawOffset = getRawOffset(); |
| if (!local) { |
| date += rawOffset; // now in local standard millis |
| } |
| |
| // When local == TRUE, date might not be in local standard |
| // millis. getOffset taking 7 parameters used here assume |
| // the given time in day is local standard time. |
| // At STD->DST transition, there is a range of time which |
| // does not exist. When 'date' is in this time range |
| // (and local == TRUE), this method interprets the specified |
| // local time as DST. At DST->STD transition, there is a |
| // range of time which occurs twice. In this case, this |
| // method interprets the specified local time as STD. |
| // To support the behavior above, we need to call getOffset |
| // (with 7 args) twice when local == true and DST is |
| // detected in the initial call. |
| for (int32_t pass=0; ; ++pass) { |
| int32_t year, month, dom, dow; |
| double day = uprv_floor(date / U_MILLIS_PER_DAY); |
| int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY); |
| |
| Grego::dayToFields(day, year, month, dom, dow); |
| |
| dstOffset = getOffset(GregorianCalendar::AD, year, month, dom, |
| (uint8_t) dow, millis, |
| Grego::monthLength(year, month), |
| ec) - rawOffset; |
| |
| // Recompute if local==TRUE, dstOffset!=0. |
| if (pass!=0 || !local || dstOffset == 0) { |
| break; |
| } |
| // adjust to local standard millis |
| date -= dstOffset; |
| } |
| } |
| |
| // ------------------------------------- |
| |
| // New available IDs API as of ICU 2.4. Uses StringEnumeration API. |
| |
| class TZEnumeration : public StringEnumeration { |
| private: |
| |
| // Map into to zones. Our results are zone[map[i]] for |
| // i=0..len-1, where zone[i] is the i-th Olson zone. If map==NULL |
| // then our results are zone[i] for i=0..len-1. Len will be zero |
| // if the zone data could not be loaded. |
| int32_t* map; |
| int32_t* localMap; |
| int32_t len; |
| int32_t pos; |
| |
| TZEnumeration(int32_t* mapData, int32_t mapLen, UBool adoptMapData) : pos(0) { |
| map = mapData; |
| localMap = adoptMapData ? mapData : NULL; |
| len = mapLen; |
| } |
| |
| UBool getID(int32_t i) { |
| UErrorCode ec = U_ZERO_ERROR; |
| int32_t idLen = 0; |
| const UChar* id = NULL; |
| UResourceBundle *top = ures_openDirect(0, kZONEINFO, &ec); |
| top = ures_getByKey(top, kNAMES, top, &ec); // dereference Zones section |
| id = ures_getStringByIndex(top, i, &idLen, &ec); |
| if(U_FAILURE(ec)) { |
| unistr.truncate(0); |
| } |
| else { |
| unistr.fastCopyFrom(UnicodeString(TRUE, id, idLen)); |
| } |
| ures_close(top); |
| return U_SUCCESS(ec); |
| } |
| |
| static int32_t* getMap(USystemTimeZoneType type, int32_t& len, UErrorCode& ec) { |
| len = 0; |
| if (U_FAILURE(ec)) { |
| return NULL; |
| } |
| int32_t* m = NULL; |
| switch (type) { |
| case UCAL_ZONE_TYPE_ANY: |
| umtx_initOnce(gSystemZonesInitOnce, &initMap, type, ec); |
| m = MAP_SYSTEM_ZONES; |
| len = LEN_SYSTEM_ZONES; |
| break; |
| case UCAL_ZONE_TYPE_CANONICAL: |
| umtx_initOnce(gCanonicalZonesInitOnce, &initMap, type, ec); |
| m = MAP_CANONICAL_SYSTEM_ZONES; |
| len = LEN_CANONICAL_SYSTEM_ZONES; |
| break; |
| case UCAL_ZONE_TYPE_CANONICAL_LOCATION: |
| umtx_initOnce(gCanonicalLocationZonesInitOnce, &initMap, type, ec); |
| m = MAP_CANONICAL_SYSTEM_LOCATION_ZONES; |
| len = LEN_CANONICAL_SYSTEM_LOCATION_ZONES; |
| break; |
| default: |
| ec = U_ILLEGAL_ARGUMENT_ERROR; |
| m = NULL; |
| len = 0; |
| break; |
| } |
| return m; |
| } |
| |
| public: |
| |
| #define DEFAULT_FILTERED_MAP_SIZE 8 |
| #define MAP_INCREMENT_SIZE 8 |
| |
| static TZEnumeration* create(USystemTimeZoneType type, const char* region, const int32_t* rawOffset, UErrorCode& ec) { |
| if (U_FAILURE(ec)) { |
| return NULL; |
| } |
| |
| int32_t baseLen; |
| int32_t *baseMap = getMap(type, baseLen, ec); |
| |
| if (U_FAILURE(ec)) { |
| return NULL; |
| } |
| |
| // If any additional conditions are available, |
| // create instance local map filtered by the conditions. |
| |
| int32_t *filteredMap = NULL; |
| int32_t numEntries = 0; |
| |
| if (region != NULL || rawOffset != NULL) { |
| int32_t filteredMapSize = DEFAULT_FILTERED_MAP_SIZE; |
| filteredMap = (int32_t *)uprv_malloc(filteredMapSize * sizeof(int32_t)); |
| if (filteredMap == NULL) { |
| ec = U_MEMORY_ALLOCATION_ERROR; |
| return NULL; |
| } |
| |
| // Walk through the base map |
| UResourceBundle *res = ures_openDirect(0, kZONEINFO, &ec); |
| res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section |
| for (int32_t i = 0; i < baseLen; i++) { |
| int32_t zidx = baseMap[i]; |
| UnicodeString id = ures_getUnicodeStringByIndex(res, zidx, &ec); |
| if (U_FAILURE(ec)) { |
| break; |
| } |
| if (region != NULL) { |
| // Filter by region |
| char tzregion[4]; // max 3 letters + null term |
| TimeZone::getRegion(id, tzregion, sizeof(tzregion), ec); |
| if (U_FAILURE(ec)) { |
| break; |
| } |
| if (uprv_stricmp(tzregion, region) != 0) { |
| // region does not match |
| continue; |
| } |
| } |
| if (rawOffset != NULL) { |
| // Filter by raw offset |
| // Note: This is VERY inefficient |
| TimeZone *z = createSystemTimeZone(id, ec); |
| if (U_FAILURE(ec)) { |
| break; |
| } |
| int32_t tzoffset = z->getRawOffset(); |
| delete z; |
| |
| if (tzoffset != *rawOffset) { |
| continue; |
| } |
| } |
| |
| if (filteredMapSize <= numEntries) { |
| filteredMapSize += MAP_INCREMENT_SIZE; |
| int32_t *tmp = (int32_t *)uprv_realloc(filteredMap, filteredMapSize * sizeof(int32_t)); |
| if (tmp == NULL) { |
| ec = U_MEMORY_ALLOCATION_ERROR; |
| break; |
| } else { |
| filteredMap = tmp; |
| } |
| } |
| |
| filteredMap[numEntries++] = zidx; |
| } |
| |
| if (U_FAILURE(ec)) { |
| uprv_free(filteredMap); |
| filteredMap = NULL; |
| } |
| |
| ures_close(res); |
| } |
| |
| TZEnumeration *result = NULL; |
| if (U_SUCCESS(ec)) { |
| // Finally, create a new enumeration instance |
| if (filteredMap == NULL) { |
| result = new TZEnumeration(baseMap, baseLen, FALSE); |
| } else { |
| result = new TZEnumeration(filteredMap, numEntries, TRUE); |
| filteredMap = NULL; |
| } |
| if (result == NULL) { |
| ec = U_MEMORY_ALLOCATION_ERROR; |
| } |
| } |
| |
| if (filteredMap != NULL) { |
| uprv_free(filteredMap); |
| } |
| |
| return result; |
| } |
| |
| TZEnumeration(const TZEnumeration &other) : StringEnumeration(), map(NULL), localMap(NULL), len(0), pos(0) { |
| if (other.localMap != NULL) { |
| localMap = (int32_t *)uprv_malloc(other.len * sizeof(int32_t)); |
| if (localMap != NULL) { |
| len = other.len; |
| uprv_memcpy(localMap, other.localMap, len * sizeof(int32_t)); |
| pos = other.pos; |
| map = localMap; |
| } else { |
| len = 0; |
| pos = 0; |
| map = NULL; |
| } |
| } else { |
| map = other.map; |
| localMap = NULL; |
| len = other.len; |
| pos = other.pos; |
| } |
| } |
| |
| virtual ~TZEnumeration(); |
| |
| virtual StringEnumeration *clone() const { |
| return new TZEnumeration(*this); |
| } |
| |
| virtual int32_t count(UErrorCode& status) const { |
| return U_FAILURE(status) ? 0 : len; |
| } |
| |
| virtual const UnicodeString* snext(UErrorCode& status) { |
| if (U_SUCCESS(status) && map != NULL && pos < len) { |
| getID(map[pos]); |
| ++pos; |
| return &unistr; |
| } |
| return 0; |
| } |
| |
| virtual void reset(UErrorCode& /*status*/) { |
| pos = 0; |
| } |
| |
| public: |
| static UClassID U_EXPORT2 getStaticClassID(void); |
| virtual UClassID getDynamicClassID(void) const; |
| }; |
| |
| TZEnumeration::~TZEnumeration() { |
| if (localMap != NULL) { |
| uprv_free(localMap); |
| } |
| } |
| |
| UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TZEnumeration) |
| |
| StringEnumeration* U_EXPORT2 |
| TimeZone::createTimeZoneIDEnumeration( |
| USystemTimeZoneType zoneType, |
| const char* region, |
| const int32_t* rawOffset, |
| UErrorCode& ec) { |
| return TZEnumeration::create(zoneType, region, rawOffset, ec); |
| } |
| |
| StringEnumeration* U_EXPORT2 |
| TimeZone::createEnumeration() { |
| UErrorCode ec = U_ZERO_ERROR; |
| return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, NULL, NULL, ec); |
| } |
| |
| StringEnumeration* U_EXPORT2 |
| TimeZone::createEnumeration(int32_t rawOffset) { |
| UErrorCode ec = U_ZERO_ERROR; |
| return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, NULL, &rawOffset, ec); |
| } |
| |
| StringEnumeration* U_EXPORT2 |
| TimeZone::createEnumeration(const char* country) { |
| UErrorCode ec = U_ZERO_ERROR; |
| return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, country, NULL, ec); |
| } |
| |
| // --------------------------------------- |
| |
| int32_t U_EXPORT2 |
| TimeZone::countEquivalentIDs(const UnicodeString& id) { |
| int32_t result = 0; |
| UErrorCode ec = U_ZERO_ERROR; |
| UResourceBundle res; |
| ures_initStackObject(&res); |
| U_DEBUG_TZ_MSG(("countEquivalentIDs..\n")); |
| UResourceBundle *top = openOlsonResource(id, res, ec); |
| if (U_SUCCESS(ec)) { |
| UResourceBundle r; |
| ures_initStackObject(&r); |
| ures_getByKey(&res, kLINKS, &r, &ec); |
| ures_getIntVector(&r, &result, &ec); |
| ures_close(&r); |
| } |
| ures_close(&res); |
| ures_close(top); |
| return result; |
| } |
| |
| // --------------------------------------- |
| |
| const UnicodeString U_EXPORT2 |
| TimeZone::getEquivalentID(const UnicodeString& id, int32_t index) { |
| U_DEBUG_TZ_MSG(("gEI(%d)\n", index)); |
| UnicodeString result; |
| UErrorCode ec = U_ZERO_ERROR; |
| UResourceBundle res; |
| ures_initStackObject(&res); |
| UResourceBundle *top = openOlsonResource(id, res, ec); |
| int32_t zone = -1; |
| if (U_SUCCESS(ec)) { |
| UResourceBundle r; |
| ures_initStackObject(&r); |
| int32_t size; |
| ures_getByKey(&res, kLINKS, &r, &ec); |
| const int32_t* v = ures_getIntVector(&r, &size, &ec); |
| if (U_SUCCESS(ec)) { |
| if (index >= 0 && index < size) { |
| zone = v[index]; |
| } |
| } |
| ures_close(&r); |
| } |
| ures_close(&res); |
| if (zone >= 0) { |
| UResourceBundle *ares = ures_getByKey(top, kNAMES, NULL, &ec); // dereference Zones section |
| if (U_SUCCESS(ec)) { |
| int32_t idLen = 0; |
| const UChar* id = ures_getStringByIndex(ares, zone, &idLen, &ec); |
| result.fastCopyFrom(UnicodeString(TRUE, id, idLen)); |
| U_DEBUG_TZ_MSG(("gei(%d) -> %d, len%d, %s\n", index, zone, result.length(), u_errorName(ec))); |
| } |
| ures_close(ares); |
| } |
| ures_close(top); |
| #if defined(U_DEBUG_TZ) |
| if(result.length() ==0) { |
| U_DEBUG_TZ_MSG(("equiv [__, #%d] -> 0 (%s)\n", index, u_errorName(ec))); |
| } |
| #endif |
| return result; |
| } |
| |
| // --------------------------------------- |
| |
| // These methods are used by ZoneMeta class only. |
| |
| const UChar* |
| TimeZone::findID(const UnicodeString& id) { |
| const UChar *result = NULL; |
| UErrorCode ec = U_ZERO_ERROR; |
| UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &ec); |
| |
| // resolve zone index by name |
| UResourceBundle *names = ures_getByKey(rb, kNAMES, NULL, &ec); |
| int32_t idx = findInStringArray(names, id, ec); |
| result = ures_getStringByIndex(names, idx, NULL, &ec); |
| if (U_FAILURE(ec)) { |
| result = NULL; |
| } |
| ures_close(names); |
| ures_close(rb); |
| return result; |
| } |
| |
| |
| const UChar* |
| TimeZone::dereferOlsonLink(const UnicodeString& id) { |
| const UChar *result = NULL; |
| UErrorCode ec = U_ZERO_ERROR; |
| UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &ec); |
| |
| // resolve zone index by name |
| UResourceBundle *names = ures_getByKey(rb, kNAMES, NULL, &ec); |
| int32_t idx = findInStringArray(names, id, ec); |
| result = ures_getStringByIndex(names, idx, NULL, &ec); |
| |
| // open the zone bundle by index |
| ures_getByKey(rb, kZONES, rb, &ec); |
| ures_getByIndex(rb, idx, rb, &ec); |
| |
| if (U_SUCCESS(ec)) { |
| if (ures_getType(rb) == URES_INT) { |
| // this is a link - dereference the link |
| int32_t deref = ures_getInt(rb, &ec); |
| const UChar* tmp = ures_getStringByIndex(names, deref, NULL, &ec); |
| if (U_SUCCESS(ec)) { |
| result = tmp; |
| } |
| } |
| } |
| |
| ures_close(names); |
| ures_close(rb); |
| |
| return result; |
| } |
| |
| const UChar* |
| TimeZone::getRegion(const UnicodeString& id) { |
| UErrorCode status = U_ZERO_ERROR; |
| return getRegion(id, status); |
| } |
| |
| const UChar* |
| TimeZone::getRegion(const UnicodeString& id, UErrorCode& status) { |
| if (U_FAILURE(status)) { |
| return NULL; |
| } |
| const UChar *result = NULL; |
| UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &status); |
| |
| // resolve zone index by name |
| UResourceBundle *res = ures_getByKey(rb, kNAMES, NULL, &status); |
| int32_t idx = findInStringArray(res, id, status); |
| |
| // get region mapping |
| ures_getByKey(rb, kREGIONS, res, &status); |
| const UChar *tmp = ures_getStringByIndex(res, idx, NULL, &status); |
| if (U_SUCCESS(status)) { |
| result = tmp; |
| } |
| |
| ures_close(res); |
| ures_close(rb); |
| |
| return result; |
| } |
| |
| |
| // --------------------------------------- |
| int32_t |
| TimeZone::getRegion(const UnicodeString& id, char *region, int32_t capacity, UErrorCode& status) |
| { |
| int32_t resultLen = 0; |
| *region = 0; |
| if (U_FAILURE(status)) { |
| return 0; |
| } |
| |
| const UChar *uregion = NULL; |
| // "Etc/Unknown" is not a system zone ID, |
| // but in the zone data |
| if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) != 0) { |
| uregion = getRegion(id); |
| } |
| if (uregion == NULL) { |
| status = U_ILLEGAL_ARGUMENT_ERROR; |
| return 0; |
| } |
| resultLen = u_strlen(uregion); |
| // A region code is represented by invariant characters |
| u_UCharsToChars(uregion, region, uprv_min(resultLen, capacity)); |
| |
| if (capacity < resultLen) { |
| status = U_BUFFER_OVERFLOW_ERROR; |
| return resultLen; |
| } |
| |
| return u_terminateChars(region, capacity, resultLen, &status); |
| } |
| |
| // --------------------------------------- |
| |
| |
| UnicodeString& |
| TimeZone::getDisplayName(UnicodeString& result) const |
| { |
| return getDisplayName(FALSE,LONG,Locale::getDefault(), result); |
| } |
| |
| UnicodeString& |
| TimeZone::getDisplayName(const Locale& locale, UnicodeString& result) const |
| { |
| return getDisplayName(FALSE, LONG, locale, result); |
| } |
| |
| UnicodeString& |
| TimeZone::getDisplayName(UBool daylight, EDisplayType style, UnicodeString& result) const |
| { |
| return getDisplayName(daylight,style, Locale::getDefault(), result); |
| } |
| //-------------------------------------- |
| int32_t |
| TimeZone::getDSTSavings()const { |
| if (useDaylightTime()) { |
| return 3600000; |
| } |
| return 0; |
| } |
| //--------------------------------------- |
| UnicodeString& |
| TimeZone::getDisplayName(UBool daylight, EDisplayType style, const Locale& locale, UnicodeString& result) const |
| { |
| UErrorCode status = U_ZERO_ERROR; |
| UDate date = Calendar::getNow(); |
| UTimeZoneFormatTimeType timeType; |
| int32_t offset; |
| |
| if (style == GENERIC_LOCATION || style == LONG_GENERIC || style == SHORT_GENERIC) { |
| LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status)); |
| if (U_FAILURE(status)) { |
| result.remove(); |
| return result; |
| } |
| // Generic format |
| switch (style) { |
| case GENERIC_LOCATION: |
| tzfmt->format(UTZFMT_STYLE_GENERIC_LOCATION, *this, date, result, &timeType); |
| break; |
| case LONG_GENERIC: |
| tzfmt->format(UTZFMT_STYLE_GENERIC_LONG, *this, date, result, &timeType); |
| break; |
| case SHORT_GENERIC: |
| tzfmt->format(UTZFMT_STYLE_GENERIC_SHORT, *this, date, result, &timeType); |
| break; |
| default: |
| U_ASSERT(FALSE); |
| } |
| // Generic format many use Localized GMT as the final fallback. |
| // When Localized GMT format is used, the result might not be |
| // appropriate for the requested daylight value. |
| if ((daylight && timeType == UTZFMT_TIME_TYPE_STANDARD) || (!daylight && timeType == UTZFMT_TIME_TYPE_DAYLIGHT)) { |
| offset = daylight ? getRawOffset() + getDSTSavings() : getRawOffset(); |
| if (style == SHORT_GENERIC) { |
| tzfmt->formatOffsetShortLocalizedGMT(offset, result, status); |
| } else { |
| tzfmt->formatOffsetLocalizedGMT(offset, result, status); |
| } |
| } |
| } else if (style == LONG_GMT || style == SHORT_GMT) { |
| LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status)); |
| if (U_FAILURE(status)) { |
| result.remove(); |
| return result; |
| } |
| offset = daylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset(); |
| switch (style) { |
| case LONG_GMT: |
| tzfmt->formatOffsetLocalizedGMT(offset, result, status); |
| break; |
| case SHORT_GMT: |
| tzfmt->formatOffsetISO8601Basic(offset, FALSE, FALSE, FALSE, result, status); |
| break; |
| default: |
| U_ASSERT(FALSE); |
| } |
| |
| } else { |
| U_ASSERT(style == LONG || style == SHORT || style == SHORT_COMMONLY_USED); |
| UTimeZoneNameType nameType = UTZNM_UNKNOWN; |
| switch (style) { |
| case LONG: |
| nameType = daylight ? UTZNM_LONG_DAYLIGHT : UTZNM_LONG_STANDARD; |
| break; |
| case SHORT: |
| case SHORT_COMMONLY_USED: |
| nameType = daylight ? UTZNM_SHORT_DAYLIGHT : UTZNM_SHORT_STANDARD; |
| break; |
| default: |
| U_ASSERT(FALSE); |
| } |
| LocalPointer<TimeZoneNames> tznames(TimeZoneNames::createInstance(locale, status)); |
| if (U_FAILURE(status)) { |
| result.remove(); |
| return result; |
| } |
| UnicodeString canonicalID(ZoneMeta::getCanonicalCLDRID(*this)); |
| tznames->getDisplayName(canonicalID, nameType, date, result); |
| if (result.isEmpty()) { |
| // Fallback to localized GMT |
| LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status)); |
| offset = daylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset(); |
| if (style == LONG) { |
| tzfmt->formatOffsetLocalizedGMT(offset, result, status); |
| } else { |
| tzfmt->formatOffsetShortLocalizedGMT(offset, result, status); |
| } |
| } |
| } |
| if (U_FAILURE(status)) { |
| result.remove(); |
| } |
| return result; |
| } |
| |
| /** |
| * Parse a custom time zone identifier and return a corresponding zone. |
| * @param id a string of the form GMT[+-]hh:mm, GMT[+-]hhmm, or |
| * GMT[+-]hh. |
| * @return a newly created SimpleTimeZone with the given offset and |
| * no Daylight Savings Time, or null if the id cannot be parsed. |
| */ |
| TimeZone* |
| TimeZone::createCustomTimeZone(const UnicodeString& id) |
| { |
| int32_t sign, hour, min, sec; |
| if (parseCustomID(id, sign, hour, min, sec)) { |
| UnicodeString customID; |
| formatCustomID(hour, min, sec, (sign < 0), customID); |
| int32_t offset = sign * ((hour * 60 + min) * 60 + sec) * 1000; |
| return new SimpleTimeZone(offset, customID); |
| } |
| return NULL; |
| } |
| |
| UnicodeString& |
| TimeZone::getCustomID(const UnicodeString& id, UnicodeString& normalized, UErrorCode& status) { |
| normalized.remove(); |
| if (U_FAILURE(status)) { |
| return normalized; |
| } |
| int32_t sign, hour, min, sec; |
| if (parseCustomID(id, sign, hour, min, sec)) { |
| formatCustomID(hour, min, sec, (sign < 0), normalized); |
| } else { |
| status = U_ILLEGAL_ARGUMENT_ERROR; |
| } |
| return normalized; |
| } |
| |
| UBool |
| TimeZone::parseCustomID(const UnicodeString& id, int32_t& sign, |
| int32_t& hour, int32_t& min, int32_t& sec) { |
| static const int32_t kParseFailed = -99999; |
| |
| NumberFormat* numberFormat = 0; |
| UnicodeString idUppercase = id; |
| idUppercase.toUpper(""); |
| |
| if (id.length() > GMT_ID_LENGTH && |
| idUppercase.startsWith(GMT_ID, GMT_ID_LENGTH)) |
| { |
| ParsePosition pos(GMT_ID_LENGTH); |
| sign = 1; |
| hour = 0; |
| min = 0; |
| sec = 0; |
| |
| if (id[pos.getIndex()] == MINUS /*'-'*/) { |
| sign = -1; |
| } else if (id[pos.getIndex()] != PLUS /*'+'*/) { |
| return FALSE; |
| } |
| pos.setIndex(pos.getIndex() + 1); |
| |
| UErrorCode success = U_ZERO_ERROR; |
| numberFormat = NumberFormat::createInstance(success); |
| if(U_FAILURE(success)){ |
| return FALSE; |
| } |
| numberFormat->setParseIntegerOnly(TRUE); |
| //numberFormat->setLenient(TRUE); // TODO: May need to set this, depends on latest timezone parsing |
| |
| // Look for either hh:mm, hhmm, or hh |
| int32_t start = pos.getIndex(); |
| Formattable n(kParseFailed); |
| numberFormat->parse(id, n, pos); |
| if (pos.getIndex() == start) { |
| delete numberFormat; |
| return FALSE; |
| } |
| hour = n.getLong(); |
| |
| if (pos.getIndex() < id.length()) { |
| if (pos.getIndex() - start > 2 |
| || id[pos.getIndex()] != COLON) { |
| delete numberFormat; |
| return FALSE; |
| } |
| // hh:mm |
| pos.setIndex(pos.getIndex() + 1); |
| int32_t oldPos = pos.getIndex(); |
| n.setLong(kParseFailed); |
| numberFormat->parse(id, n, pos); |
| if ((pos.getIndex() - oldPos) != 2) { |
| // must be 2 digits |
| delete numberFormat; |
| return FALSE; |
| } |
| min = n.getLong(); |
| if (pos.getIndex() < id.length()) { |
| if (id[pos.getIndex()] != COLON) { |
| delete numberFormat; |
| return FALSE; |
| } |
| // [:ss] |
| pos.setIndex(pos.getIndex() + 1); |
| oldPos = pos.getIndex(); |
| n.setLong(kParseFailed); |
| numberFormat->parse(id, n, pos); |
| if (pos.getIndex() != id.length() |
| || (pos.getIndex() - oldPos) != 2) { |
| delete numberFormat; |
| return FALSE; |
| } |
| sec = n.getLong(); |
| } |
| } else { |
| // Supported formats are below - |
| // |
| // HHmmss |
| // Hmmss |
| // HHmm |
| // Hmm |
| // HH |
| // H |
| |
| int32_t length = pos.getIndex() - start; |
| if (length <= 0 || 6 < length) { |
| // invalid length |
| delete numberFormat; |
| return FALSE; |
| } |
| switch (length) { |
| case 1: |
| case 2: |
| // already set to hour |
| break; |
| case 3: |
| case 4: |
| min = hour % 100; |
| hour /= 100; |
| break; |
| case 5: |
| case 6: |
| sec = hour % 100; |
| min = (hour/100) % 100; |
| hour /= 10000; |
| break; |
| } |
| } |
| |
| delete numberFormat; |
| |
| if (hour > kMAX_CUSTOM_HOUR || min > kMAX_CUSTOM_MIN || sec > kMAX_CUSTOM_SEC) { |
| return FALSE; |
| } |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| UnicodeString& |
| TimeZone::formatCustomID(int32_t hour, int32_t min, int32_t sec, |
| UBool negative, UnicodeString& id) { |
| // Create time zone ID - GMT[+|-]hhmm[ss] |
| id.setTo(GMT_ID, GMT_ID_LENGTH); |
| if (hour | min | sec) { |
| if (negative) { |
| id += (UChar)MINUS; |
| } else { |
| id += (UChar)PLUS; |
| } |
| |
| if (hour < 10) { |
| id += (UChar)ZERO_DIGIT; |
| } else { |
| id += (UChar)(ZERO_DIGIT + hour/10); |
| } |
| id += (UChar)(ZERO_DIGIT + hour%10); |
| id += (UChar)COLON; |
| if (min < 10) { |
| id += (UChar)ZERO_DIGIT; |
| } else { |
| id += (UChar)(ZERO_DIGIT + min/10); |
| } |
| id += (UChar)(ZERO_DIGIT + min%10); |
| |
| if (sec) { |
| id += (UChar)COLON; |
| if (sec < 10) { |
| id += (UChar)ZERO_DIGIT; |
| } else { |
| id += (UChar)(ZERO_DIGIT + sec/10); |
| } |
| id += (UChar)(ZERO_DIGIT + sec%10); |
| } |
| } |
| return id; |
| } |
| |
| |
| UBool |
| TimeZone::hasSameRules(const TimeZone& other) const |
| { |
| return (getRawOffset() == other.getRawOffset() && |
| useDaylightTime() == other.useDaylightTime()); |
| } |
| |
| static void U_CALLCONV initTZDataVersion(UErrorCode &status) { |
| ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup); |
| int32_t len = 0; |
| UResourceBundle *bundle = ures_openDirect(NULL, kZONEINFO, &status); |
| const UChar *tzver = ures_getStringByKey(bundle, kTZVERSION, &len, &status); |
| |
| if (U_SUCCESS(status)) { |
| if (len >= (int32_t)sizeof(TZDATA_VERSION)) { |
| // Ensure that there is always space for a trailing nul in TZDATA_VERSION |
| len = sizeof(TZDATA_VERSION) - 1; |
| } |
| u_UCharsToChars(tzver, TZDATA_VERSION, len); |
| } |
| ures_close(bundle); |
| |
| } |
| |
| const char* |
| TimeZone::getTZDataVersion(UErrorCode& status) |
| { |
| umtx_initOnce(gTZDataVersionInitOnce, &initTZDataVersion, status); |
| return (const char*)TZDATA_VERSION; |
| } |
| |
| UnicodeString& |
| TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UErrorCode& status) |
| { |
| UBool isSystemID = FALSE; |
| return getCanonicalID(id, canonicalID, isSystemID, status); |
| } |
| |
| UnicodeString& |
| TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UBool& isSystemID, |
| UErrorCode& status) |
| { |
| canonicalID.remove(); |
| isSystemID = FALSE; |
| if (U_FAILURE(status)) { |
| return canonicalID; |
| } |
| if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) == 0) { |
| // special case - Etc/Unknown is a canonical ID, but not system ID |
| canonicalID.fastCopyFrom(id); |
| isSystemID = FALSE; |
| } else { |
| ZoneMeta::getCanonicalCLDRID(id, canonicalID, status); |
| if (U_SUCCESS(status)) { |
| isSystemID = TRUE; |
| } else { |
| // Not a system ID |
| status = U_ZERO_ERROR; |
| getCustomID(id, canonicalID, status); |
| } |
| } |
| return canonicalID; |
| } |
| |
| UnicodeString& |
| TimeZone::getWindowsID(const UnicodeString& id, UnicodeString& winid, UErrorCode& status) { |
| winid.remove(); |
| if (U_FAILURE(status)) { |
| return winid; |
| } |
| |
| // canonicalize the input ID |
| UnicodeString canonicalID; |
| UBool isSystemID = FALSE; |
| |
| getCanonicalID(id, canonicalID, isSystemID, status); |
| if (U_FAILURE(status) || !isSystemID) { |
| // mapping data is only applicable to tz database IDs |
| if (status == U_ILLEGAL_ARGUMENT_ERROR) { |
| // getWindowsID() sets an empty string where |
| // getCanonicalID() sets a U_ILLEGAL_ARGUMENT_ERROR. |
| status = U_ZERO_ERROR; |
| } |
| return winid; |
| } |
| |
| UResourceBundle *mapTimezones = ures_openDirect(NULL, "windowsZones", &status); |
| ures_getByKey(mapTimezones, "mapTimezones", mapTimezones, &status); |
| |
| if (U_FAILURE(status)) { |
| return winid; |
| } |
| |
| UResourceBundle *winzone = NULL; |
| UBool found = FALSE; |
| while (ures_hasNext(mapTimezones) && !found) { |
| winzone = ures_getNextResource(mapTimezones, winzone, &status); |
| if (U_FAILURE(status)) { |
| break; |
| } |
| if (ures_getType(winzone) != URES_TABLE) { |
| continue; |
| } |
| UResourceBundle *regionalData = NULL; |
| while (ures_hasNext(winzone) && !found) { |
| regionalData = ures_getNextResource(winzone, regionalData, &status); |
| if (U_FAILURE(status)) { |
| break; |
| } |
| if (ures_getType(regionalData) != URES_STRING) { |
| continue; |
| } |
| int32_t len; |
| const UChar *tzids = ures_getString(regionalData, &len, &status); |
| if (U_FAILURE(status)) { |
| break; |
| } |
| |
| const UChar *start = tzids; |
| UBool hasNext = TRUE; |
| while (hasNext) { |
| const UChar *end = u_strchr(start, (UChar)0x20); |
| if (end == NULL) { |
| end = tzids + len; |
| hasNext = FALSE; |
| } |
| if (canonicalID.compare(start, end - start) == 0) { |
| winid = UnicodeString(ures_getKey(winzone), -1 , US_INV); |
| found = TRUE; |
| break; |
| } |
| start = end + 1; |
| } |
| } |
| ures_close(regionalData); |
| } |
| ures_close(winzone); |
| ures_close(mapTimezones); |
| |
| return winid; |
| } |
| |
| #define MAX_WINDOWS_ID_SIZE 128 |
| |
| UnicodeString& |
| TimeZone::getIDForWindowsID(const UnicodeString& winid, const char* region, UnicodeString& id, UErrorCode& status) { |
| id.remove(); |
| if (U_FAILURE(status)) { |
| return id; |
| } |
| |
| UResourceBundle *zones = ures_openDirect(NULL, "windowsZones", &status); |
| ures_getByKey(zones, "mapTimezones", zones, &status); |
| if (U_FAILURE(status)) { |
| ures_close(zones); |
| return id; |
| } |
| |
| UErrorCode tmperr = U_ZERO_ERROR; |
| char winidKey[MAX_WINDOWS_ID_SIZE]; |
| int32_t winKeyLen = winid.extract(0, winid.length(), winidKey, sizeof(winidKey) - 1, US_INV); |
| |
| if (winKeyLen == 0 || winKeyLen >= (int32_t)sizeof(winidKey)) { |
| ures_close(zones); |
| return id; |
| } |
| winidKey[winKeyLen] = 0; |
| |
| ures_getByKey(zones, winidKey, zones, &tmperr); // use tmperr, because windows mapping might not |
| // be avaiable by design |
| if (U_FAILURE(tmperr)) { |
| ures_close(zones); |
| return id; |
| } |
| |
| const UChar *tzid = NULL; |
| int32_t len = 0; |
| UBool gotID = FALSE; |
| if (region) { |
| const UChar *tzids = ures_getStringByKey(zones, region, &len, &tmperr); // use tmperr, because |
| // regional mapping is optional |
| if (U_SUCCESS(tmperr)) { |
| // first ID delimited by space is the defasult one |
| const UChar *end = u_strchr(tzids, (UChar)0x20); |
| if (end == NULL) { |
| id.setTo(tzids, -1); |
| } else { |
| id.setTo(tzids, end - tzids); |
| } |
| gotID = TRUE; |
| } |
| } |
| |
| if (!gotID) { |
| tzid = ures_getStringByKey(zones, "001", &len, &status); // using status, because "001" must be |
| // available at this point |
| if (U_SUCCESS(status)) { |
| id.setTo(tzid, len); |
| } |
| } |
| |
| ures_close(zones); |
| return id; |
| } |
| |
| |
| U_NAMESPACE_END |
| |
| #endif /* #if !UCONFIG_NO_FORMATTING */ |
| |
| //eof |