Home | History | Annotate | Download | only in i18n
      1 /*
      2 *******************************************************************************
      3 * Copyright (C) 1997-2012, International Business Machines Corporation and    *
      4 * others. All Rights Reserved.                                                *
      5 *******************************************************************************
      6 *
      7 * File TIMEZONE.CPP
      8 *
      9 * Modification History:
     10 *
     11 *   Date        Name        Description
     12 *   12/05/96    clhuang     Creation.
     13 *   04/21/97    aliu        General clean-up and bug fixing.
     14 *   05/08/97    aliu        Fixed Hashtable code per code review.
     15 *   07/09/97    helena      Changed createInstance to createDefault.
     16 *   07/29/97    aliu        Updated with all-new list of 96 UNIX-derived
     17 *                           TimeZones.  Changed mechanism to load from static
     18 *                           array rather than resource bundle.
     19 *   07/07/1998  srl         Bugfixes from the Java side: UTC GMT CAT NST
     20 *                           Added getDisplayName API
     21 *                           going to add custom parsing.
     22 *
     23 *                           ISSUES:
     24 *                               - should getDisplayName cache something?
     25 *                               - should custom time zones be cached? [probably]
     26 *  08/10/98     stephen     Brought getDisplayName() API in-line w/ conventions
     27 *  08/19/98     stephen     Changed createTimeZone() to never return 0
     28 *  09/02/98     stephen     Added getOffset(monthLen) and hasSameRules()
     29 *  09/15/98     stephen     Added getStaticClassID()
     30 *  02/22/99     stephen     Removed character literals for EBCDIC safety
     31 *  05/04/99     stephen     Changed initDefault() for Mutex issues
     32 *  07/12/99     helena      HPUX 11 CC Port.
     33 *  12/03/99     aliu        Moved data out of static table into icudata.dll.
     34 *                           Substantial rewrite of zone lookup, default zone, and
     35 *                           available IDs code.  Misc. cleanup.
     36 *********************************************************************************/
     37 
     38 #include "utypeinfo.h"  // for 'typeid' to work
     39 
     40 #include "unicode/utypes.h"
     41 #include "unicode/ustring.h"
     42 #include "ustr_imp.h"
     43 
     44 #ifdef U_DEBUG_TZ
     45 # include <stdio.h>
     46 # include "uresimp.h" // for debugging
     47 
     48 static void debug_tz_loc(const char *f, int32_t l)
     49 {
     50   fprintf(stderr, "%s:%d: ", f, l);
     51 }
     52 
     53 static void debug_tz_msg(const char *pat, ...)
     54 {
     55   va_list ap;
     56   va_start(ap, pat);
     57   vfprintf(stderr, pat, ap);
     58   fflush(stderr);
     59 }
     60 static char gStrBuf[256];
     61 #define U_DEBUG_TZ_STR(x) u_austrncpy(gStrBuf,x,sizeof(gStrBuf)-1)
     62 // must use double parens, i.e.:  U_DEBUG_TZ_MSG(("four is: %d",4));
     63 #define U_DEBUG_TZ_MSG(x) {debug_tz_loc(__FILE__,__LINE__);debug_tz_msg x;}
     64 #else
     65 #define U_DEBUG_TZ_MSG(x)
     66 #endif
     67 
     68 #if !UCONFIG_NO_FORMATTING
     69 
     70 #include "unicode/simpletz.h"
     71 #include "unicode/calendar.h"
     72 #include "unicode/gregocal.h"
     73 #include "unicode/ures.h"
     74 #include "unicode/tzfmt.h"
     75 #include "unicode/numfmt.h"
     76 #include "gregoimp.h"
     77 #include "uresimp.h" // struct UResourceBundle
     78 #include "olsontz.h"
     79 #include "mutex.h"
     80 #include "unicode/udata.h"
     81 #include "ucln_in.h"
     82 #include "cstring.h"
     83 #include "cmemory.h"
     84 #include "unicode/strenum.h"
     85 #include "uassert.h"
     86 #include "zonemeta.h"
     87 
     88 #define kZONEINFO "zoneinfo64"
     89 #define kREGIONS  "Regions"
     90 #define kZONES    "Zones"
     91 #define kRULES    "Rules"
     92 #define kNAMES    "Names"
     93 #define kTZVERSION  "TZVersion"
     94 #define kLINKS    "links"
     95 #define kMAX_CUSTOM_HOUR    23
     96 #define kMAX_CUSTOM_MIN     59
     97 #define kMAX_CUSTOM_SEC     59
     98 #define MINUS 0x002D
     99 #define PLUS 0x002B
    100 #define ZERO_DIGIT 0x0030
    101 #define COLON 0x003A
    102 
    103 // Static data and constants
    104 
    105 static const UChar         WORLD[] = {0x30, 0x30, 0x31, 0x00}; /* "001" */
    106 
    107 static const UChar         GMT_ID[] = {0x47, 0x4D, 0x54, 0x00}; /* "GMT" */
    108 static const UChar         UNKNOWN_ZONE_ID[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x6E, 0x6B, 0x6E, 0x6F, 0x77, 0x6E, 0x00}; /* "Etc/Unknown" */
    109 static const int32_t       GMT_ID_LENGTH = 3;
    110 static const int32_t       UNKNOWN_ZONE_ID_LENGTH = 11;
    111 
    112 static UMutex LOCK = U_MUTEX_INITIALIZER;
    113 static UMutex TZSET_LOCK = U_MUTEX_INITIALIZER;
    114 static icu::TimeZone* DEFAULT_ZONE = NULL;
    115 static icu::TimeZone* _GMT = NULL;
    116 static icu::TimeZone* _UNKNOWN_ZONE = NULL;
    117 
    118 static char TZDATA_VERSION[16];
    119 static UBool TZDataVersionInitialized = FALSE;
    120 
    121 static int32_t* MAP_SYSTEM_ZONES = NULL;
    122 static int32_t* MAP_CANONICAL_SYSTEM_ZONES = NULL;
    123 static int32_t* MAP_CANONICAL_SYSTEM_LOCATION_ZONES = NULL;
    124 
    125 static int32_t LEN_SYSTEM_ZONES = 0;
    126 static int32_t LEN_CANONICAL_SYSTEM_ZONES = 0;
    127 static int32_t LEN_CANONICAL_SYSTEM_LOCATION_ZONES = 0;
    128 
    129 U_CDECL_BEGIN
    130 static UBool U_CALLCONV timeZone_cleanup(void)
    131 {
    132     delete DEFAULT_ZONE;
    133     DEFAULT_ZONE = NULL;
    134 
    135     delete _GMT;
    136     _GMT = NULL;
    137 
    138     delete _UNKNOWN_ZONE;
    139     _UNKNOWN_ZONE = NULL;
    140 
    141     uprv_memset(TZDATA_VERSION, 0, sizeof(TZDATA_VERSION));
    142     TZDataVersionInitialized = FALSE;
    143 
    144     LEN_SYSTEM_ZONES = 0;
    145     uprv_free(MAP_SYSTEM_ZONES);
    146     MAP_SYSTEM_ZONES = 0;
    147 
    148     LEN_CANONICAL_SYSTEM_ZONES = 0;
    149     uprv_free(MAP_CANONICAL_SYSTEM_ZONES);
    150     MAP_CANONICAL_SYSTEM_ZONES = 0;
    151 
    152     LEN_CANONICAL_SYSTEM_LOCATION_ZONES = 0;
    153     uprv_free(MAP_CANONICAL_SYSTEM_LOCATION_ZONES);
    154     MAP_CANONICAL_SYSTEM_LOCATION_ZONES = 0;
    155 
    156     return TRUE;
    157 }
    158 U_CDECL_END
    159 
    160 U_NAMESPACE_BEGIN
    161 
    162 static int32_t findInStringArray(UResourceBundle* array, const UnicodeString& id, UErrorCode &status)
    163 {
    164     UnicodeString copy;
    165     const UChar *u;
    166     int32_t len;
    167 
    168     int32_t start = 0;
    169     int32_t limit = ures_getSize(array);
    170     int32_t mid;
    171     int32_t lastMid = INT32_MAX;
    172     if(U_FAILURE(status) || (limit < 1)) {
    173         return -1;
    174     }
    175     U_DEBUG_TZ_MSG(("fisa: Looking for %s, between %d and %d\n", U_DEBUG_TZ_STR(UnicodeString(id).getTerminatedBuffer()), start, limit));
    176 
    177     for (;;) {
    178         mid = (int32_t)((start + limit) / 2);
    179         if (lastMid == mid) {   /* Have we moved? */
    180             break;  /* We haven't moved, and it wasn't found. */
    181         }
    182         lastMid = mid;
    183         u = ures_getStringByIndex(array, mid, &len, &status);
    184         if (U_FAILURE(status)) {
    185             break;
    186         }
    187         U_DEBUG_TZ_MSG(("tz: compare to %s, %d .. [%d] .. %d\n", U_DEBUG_TZ_STR(u), start, mid, limit));
    188         copy.setTo(TRUE, u, len);
    189         int r = id.compare(copy);
    190         if(r==0) {
    191             U_DEBUG_TZ_MSG(("fisa: found at %d\n", mid));
    192             return mid;
    193         } else if(r<0) {
    194             limit = mid;
    195         } else {
    196             start = mid;
    197         }
    198     }
    199     U_DEBUG_TZ_MSG(("fisa: not found\n"));
    200     return -1;
    201 }
    202 
    203 /**
    204  * Fetch a specific zone by name.  Replaces the getByKey call.
    205  * @param top Top timezone resource
    206  * @param id Time zone ID
    207  * @param oldbundle Bundle for reuse (or NULL).   see 'ures_open()'
    208  * @return the zone's bundle if found, or undefined if error.  Reuses oldbundle.
    209  */
    210 static UResourceBundle* getZoneByName(const UResourceBundle* top, const UnicodeString& id, UResourceBundle *oldbundle, UErrorCode& status) {
    211     // load the Rules object
    212     UResourceBundle *tmp = ures_getByKey(top, kNAMES, NULL, &status);
    213 
    214     // search for the string
    215     int32_t idx = findInStringArray(tmp, id, status);
    216 
    217     if((idx == -1) && U_SUCCESS(status)) {
    218         // not found
    219         status = U_MISSING_RESOURCE_ERROR;
    220         //ures_close(oldbundle);
    221         //oldbundle = NULL;
    222     } else {
    223         U_DEBUG_TZ_MSG(("gzbn: oldbundle= size %d, type %d, %s\n", ures_getSize(tmp), ures_getType(tmp), u_errorName(status)));
    224         tmp = ures_getByKey(top, kZONES, tmp, &status); // get Zones object from top
    225         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)));
    226         oldbundle = ures_getByIndex(tmp, idx, oldbundle, &status); // get nth Zone object
    227         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)));
    228     }
    229     ures_close(tmp);
    230     if(U_FAILURE(status)) {
    231         //ures_close(oldbundle);
    232         return NULL;
    233     } else {
    234         return oldbundle;
    235     }
    236 }
    237 
    238 
    239 UResourceBundle* TimeZone::loadRule(const UResourceBundle* top, const UnicodeString& ruleid, UResourceBundle* oldbundle, UErrorCode& status) {
    240     char key[64];
    241     ruleid.extract(0, sizeof(key)-1, key, (int32_t)sizeof(key)-1, US_INV);
    242     U_DEBUG_TZ_MSG(("loadRule(%s)\n", key));
    243     UResourceBundle *r = ures_getByKey(top, kRULES, oldbundle, &status);
    244     U_DEBUG_TZ_MSG(("loadRule(%s) -> kRULES [%s]\n", key, u_errorName(status)));
    245     r = ures_getByKey(r, key, r, &status);
    246     U_DEBUG_TZ_MSG(("loadRule(%s) -> item [%s]\n", key, u_errorName(status)));
    247     return r;
    248 }
    249 
    250 /**
    251  * Given an ID, open the appropriate resource for the given time zone.
    252  * Dereference aliases if necessary.
    253  * @param id zone id
    254  * @param res resource, which must be ready for use (initialized but not open)
    255  * @param ec input-output error code
    256  * @return top-level resource bundle
    257  */
    258 static UResourceBundle* openOlsonResource(const UnicodeString& id,
    259                                           UResourceBundle& res,
    260                                           UErrorCode& ec)
    261 {
    262 #if U_DEBUG_TZ
    263     char buf[128];
    264     id.extract(0, sizeof(buf)-1, buf, sizeof(buf), "");
    265 #endif
    266     UResourceBundle *top = ures_openDirect(0, kZONEINFO, &ec);
    267     U_DEBUG_TZ_MSG(("pre: res sz=%d\n", ures_getSize(&res)));
    268     /* &res = */ getZoneByName(top, id, &res, ec);
    269     // Dereference if this is an alias.  Docs say result should be 1
    270     // but it is 0 in 2.8 (?).
    271     U_DEBUG_TZ_MSG(("Loading zone '%s' (%s, size %d) - %s\n", buf, ures_getKey((UResourceBundle*)&res), ures_getSize(&res), u_errorName(ec)));
    272     if (ures_getType(&res) == URES_INT) {
    273         int32_t deref = ures_getInt(&res, &ec) + 0;
    274         U_DEBUG_TZ_MSG(("getInt: %s - type is %d\n", u_errorName(ec), ures_getType(&res)));
    275         UResourceBundle *ares = ures_getByKey(top, kZONES, NULL, &ec); // dereference Zones section
    276         ures_getByIndex(ares, deref, &res, &ec);
    277         ures_close(ares);
    278         U_DEBUG_TZ_MSG(("alias to #%d (%s) - %s\n", deref, "??", u_errorName(ec)));
    279     } else {
    280         U_DEBUG_TZ_MSG(("not an alias - size %d\n", ures_getSize(&res)));
    281     }
    282     U_DEBUG_TZ_MSG(("%s - final status is %s\n", buf, u_errorName(ec)));
    283     return top;
    284 }
    285 
    286 // -------------------------------------
    287 
    288 namespace {
    289 
    290 void
    291 ensureStaticTimeZones() {
    292     UBool needsInit;
    293     UMTX_CHECK(&LOCK, (_GMT == NULL), needsInit);   /* This is here to prevent race conditions. */
    294 
    295     // Initialize _GMT independently of other static data; it should
    296     // be valid even if we can't load the time zone UDataMemory.
    297     if (needsInit) {
    298         SimpleTimeZone *tmpUnknown =
    299             new SimpleTimeZone(0, UnicodeString(TRUE, UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH));
    300         SimpleTimeZone *tmpGMT = new SimpleTimeZone(0, UnicodeString(TRUE, GMT_ID, GMT_ID_LENGTH));
    301         umtx_lock(&LOCK);
    302         if (_UNKNOWN_ZONE == 0) {
    303             _UNKNOWN_ZONE = tmpUnknown;
    304             tmpUnknown = NULL;
    305         }
    306         if (_GMT == 0) {
    307             _GMT = tmpGMT;
    308             tmpGMT = NULL;
    309         }
    310         umtx_unlock(&LOCK);
    311         ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
    312         delete tmpUnknown;
    313         delete tmpGMT;
    314     }
    315 }
    316 
    317 }  // anonymous namespace
    318 
    319 const TimeZone& U_EXPORT2
    320 TimeZone::getUnknown()
    321 {
    322     ensureStaticTimeZones();
    323     return *_UNKNOWN_ZONE;
    324 }
    325 
    326 const TimeZone* U_EXPORT2
    327 TimeZone::getGMT(void)
    328 {
    329     ensureStaticTimeZones();
    330     return _GMT;
    331 }
    332 
    333 // *****************************************************************************
    334 // class TimeZone
    335 // *****************************************************************************
    336 
    337 UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(TimeZone)
    338 
    339 TimeZone::TimeZone()
    340     :   UObject(), fID()
    341 {
    342 }
    343 
    344 // -------------------------------------
    345 
    346 TimeZone::TimeZone(const UnicodeString &id)
    347     :   UObject(), fID(id)
    348 {
    349 }
    350 
    351 // -------------------------------------
    352 
    353 TimeZone::~TimeZone()
    354 {
    355 }
    356 
    357 // -------------------------------------
    358 
    359 TimeZone::TimeZone(const TimeZone &source)
    360     :   UObject(source), fID(source.fID)
    361 {
    362 }
    363 
    364 // -------------------------------------
    365 
    366 TimeZone &
    367 TimeZone::operator=(const TimeZone &right)
    368 {
    369     if (this != &right) fID = right.fID;
    370     return *this;
    371 }
    372 
    373 // -------------------------------------
    374 
    375 UBool
    376 TimeZone::operator==(const TimeZone& that) const
    377 {
    378     return typeid(*this) == typeid(that) &&
    379         fID == that.fID;
    380 }
    381 
    382 // -------------------------------------
    383 
    384 TimeZone* U_EXPORT2
    385 TimeZone::createTimeZone(const UnicodeString& ID)
    386 {
    387     /* We first try to lookup the zone ID in our system list.  If this
    388      * fails, we try to parse it as a custom string GMT[+-]hh:mm.  If
    389      * all else fails, we return GMT, which is probably not what the
    390      * user wants, but at least is a functioning TimeZone object.
    391      *
    392      * We cannot return NULL, because that would break compatibility
    393      * with the JDK.
    394      */
    395     TimeZone* result = createSystemTimeZone(ID);
    396 
    397     if (result == 0) {
    398         U_DEBUG_TZ_MSG(("failed to load system time zone with id - falling to custom"));
    399         result = createCustomTimeZone(ID);
    400     }
    401     if (result == 0) {
    402         U_DEBUG_TZ_MSG(("failed to load time zone with id - falling to Etc/Unknown(GMT)"));
    403         result = getUnknown().clone();
    404     }
    405     return result;
    406 }
    407 
    408 /**
    409  * Lookup the given name in our system zone table.  If found,
    410  * instantiate a new zone of that name and return it.  If not
    411  * found, return 0.
    412  */
    413 TimeZone*
    414 TimeZone::createSystemTimeZone(const UnicodeString& id) {
    415     UErrorCode ec = U_ZERO_ERROR;
    416     return createSystemTimeZone(id, ec);
    417 }
    418 
    419 TimeZone*
    420 TimeZone::createSystemTimeZone(const UnicodeString& id, UErrorCode& ec) {
    421     if (U_FAILURE(ec)) {
    422         return NULL;
    423     }
    424     TimeZone* z = 0;
    425     UResourceBundle res;
    426     ures_initStackObject(&res);
    427     U_DEBUG_TZ_MSG(("pre-err=%s\n", u_errorName(ec)));
    428     UResourceBundle *top = openOlsonResource(id, res, ec);
    429     U_DEBUG_TZ_MSG(("post-err=%s\n", u_errorName(ec)));
    430     if (U_SUCCESS(ec)) {
    431         z = new OlsonTimeZone(top, &res, id, ec);
    432         if (z == NULL) {
    433           U_DEBUG_TZ_MSG(("cstz: olson time zone failed to initialize - err %s\n", u_errorName(ec)));
    434         }
    435     }
    436     ures_close(&res);
    437     ures_close(top);
    438     if (U_FAILURE(ec)) {
    439         U_DEBUG_TZ_MSG(("cstz: failed to create, err %s\n", u_errorName(ec)));
    440         delete z;
    441         z = 0;
    442     }
    443     return z;
    444 }
    445 
    446 // -------------------------------------
    447 
    448 /**
    449  * Initialize DEFAULT_ZONE from the system default time zone.  The
    450  * caller should confirm that DEFAULT_ZONE is NULL before calling.
    451  * Upon return, DEFAULT_ZONE will not be NULL, unless operator new()
    452  * returns NULL.
    453  *
    454  * Must be called OUTSIDE mutex.
    455  */
    456 void
    457 TimeZone::initDefault()
    458 {
    459     // We access system timezone data through TPlatformUtilities,
    460     // including tzset(), timezone, and tzname[].
    461     int32_t rawOffset = 0;
    462     const char *hostID;
    463 
    464     // First, try to create a system timezone, based
    465     // on the string ID in tzname[0].
    466     {
    467         // NOTE: Local mutex here. TimeZone mutex below
    468         // mutexed to avoid threading issues in the platform functions.
    469         // Some of the locale/timezone OS functions may not be thread safe,
    470         // so the intent is that any setting from anywhere within ICU
    471         // happens while the ICU mutex is held.
    472         // The operating system might actually use ICU to implement timezones.
    473         // So we may have ICU calling ICU here, like on AIX.
    474         // In order to prevent a double lock of a non-reentrant mutex in a
    475         // different part of ICU, we use TZSET_LOCK to allow only one instance
    476         // of ICU to query these thread unsafe OS functions at any given time.
    477         Mutex lock(&TZSET_LOCK);
    478 
    479         ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
    480         uprv_tzset(); // Initialize tz... system data
    481 
    482         // Get the timezone ID from the host.  This function should do
    483         // any required host-specific remapping; e.g., on Windows this
    484         // function maps the Date and Time control panel setting to an
    485         // ICU timezone ID.
    486         hostID = uprv_tzname(0);
    487 
    488         // Invert sign because UNIX semantics are backwards
    489         rawOffset = uprv_timezone() * -U_MILLIS_PER_SECOND;
    490     }
    491 
    492     UBool initialized;
    493     UMTX_CHECK(&LOCK, (DEFAULT_ZONE != NULL), initialized);
    494     if (initialized) {
    495         /* Hrmph? Either a race condition happened, or tzset initialized ICU. */
    496         return;
    497     }
    498 
    499     TimeZone* default_zone = NULL;
    500 
    501     /* Make sure that the string is NULL terminated to prevent BoundsChecker/Purify warnings. */
    502     UnicodeString hostStrID(hostID, -1, US_INV);
    503     hostStrID.append((UChar)0);
    504     hostStrID.truncate(hostStrID.length()-1);
    505     default_zone = createSystemTimeZone(hostStrID);
    506 
    507 #if U_PLATFORM_USES_ONLY_WIN32_API
    508     // hostID points to a heap-allocated location on Windows.
    509     uprv_free(const_cast<char *>(hostID));
    510 #endif
    511 
    512     int32_t hostIDLen = hostStrID.length();
    513     if (default_zone != NULL && rawOffset != default_zone->getRawOffset()
    514         && (3 <= hostIDLen && hostIDLen <= 4))
    515     {
    516         // Uh oh. This probably wasn't a good id.
    517         // It was probably an ambiguous abbreviation
    518         delete default_zone;
    519         default_zone = NULL;
    520     }
    521 
    522     // Construct a fixed standard zone with the host's ID
    523     // and raw offset.
    524     if (default_zone == NULL) {
    525         default_zone = new SimpleTimeZone(rawOffset, hostStrID);
    526     }
    527 
    528     // If we _still_ don't have a time zone, use GMT.
    529     if (default_zone == NULL) {
    530         const TimeZone* temptz = getGMT();
    531         // If we can't use GMT, get out.
    532         if (temptz == NULL) {
    533             return;
    534         }
    535         default_zone = temptz->clone();
    536     }
    537 
    538     // If DEFAULT_ZONE is still NULL, set it up.
    539     umtx_lock(&LOCK);
    540     if (DEFAULT_ZONE == NULL) {
    541         DEFAULT_ZONE = default_zone;
    542         default_zone = NULL;
    543         ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
    544     }
    545     umtx_unlock(&LOCK);
    546 
    547     delete default_zone;
    548 }
    549 
    550 // -------------------------------------
    551 
    552 TimeZone* U_EXPORT2
    553 TimeZone::createDefault()
    554 {
    555     /* This is here to prevent race conditions. */
    556     UBool needsInit;
    557     UMTX_CHECK(&LOCK, (DEFAULT_ZONE == NULL), needsInit);
    558     if (needsInit) {
    559         initDefault();
    560     }
    561 
    562     Mutex lock(&LOCK); // In case adoptDefault is called
    563     return (DEFAULT_ZONE != NULL) ? DEFAULT_ZONE->clone() : NULL;
    564 }
    565 
    566 // -------------------------------------
    567 
    568 void U_EXPORT2
    569 TimeZone::adoptDefault(TimeZone* zone)
    570 {
    571     if (zone != NULL)
    572     {
    573         TimeZone* old = NULL;
    574 
    575         umtx_lock(&LOCK);
    576         old = DEFAULT_ZONE;
    577         DEFAULT_ZONE = zone;
    578         umtx_unlock(&LOCK);
    579 
    580         delete old;
    581         ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
    582     }
    583 }
    584 // -------------------------------------
    585 
    586 void U_EXPORT2
    587 TimeZone::setDefault(const TimeZone& zone)
    588 {
    589     adoptDefault(zone.clone());
    590 }
    591 
    592 //----------------------------------------------------------------------
    593 
    594 /**
    595  * This is the default implementation for subclasses that do not
    596  * override this method.  This implementation calls through to the
    597  * 8-argument getOffset() method after suitable computations, and
    598  * correctly adjusts GMT millis to local millis when necessary.
    599  */
    600 void TimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset,
    601                          int32_t& dstOffset, UErrorCode& ec) const {
    602     if (U_FAILURE(ec)) {
    603         return;
    604     }
    605 
    606     rawOffset = getRawOffset();
    607     if (!local) {
    608         date += rawOffset; // now in local standard millis
    609     }
    610 
    611     // When local == TRUE, date might not be in local standard
    612     // millis.  getOffset taking 7 parameters used here assume
    613     // the given time in day is local standard time.
    614     // At STD->DST transition, there is a range of time which
    615     // does not exist.  When 'date' is in this time range
    616     // (and local == TRUE), this method interprets the specified
    617     // local time as DST.  At DST->STD transition, there is a
    618     // range of time which occurs twice.  In this case, this
    619     // method interprets the specified local time as STD.
    620     // To support the behavior above, we need to call getOffset
    621     // (with 7 args) twice when local == true and DST is
    622     // detected in the initial call.
    623     for (int32_t pass=0; ; ++pass) {
    624         int32_t year, month, dom, dow;
    625         double day = uprv_floor(date / U_MILLIS_PER_DAY);
    626         int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
    627 
    628         Grego::dayToFields(day, year, month, dom, dow);
    629 
    630         dstOffset = getOffset(GregorianCalendar::AD, year, month, dom,
    631                               (uint8_t) dow, millis,
    632                               Grego::monthLength(year, month),
    633                               ec) - rawOffset;
    634 
    635         // Recompute if local==TRUE, dstOffset!=0.
    636         if (pass!=0 || !local || dstOffset == 0) {
    637             break;
    638         }
    639         // adjust to local standard millis
    640         date -= dstOffset;
    641     }
    642 }
    643 
    644 // -------------------------------------
    645 
    646 // New available IDs API as of ICU 2.4.  Uses StringEnumeration API.
    647 
    648 class TZEnumeration : public StringEnumeration {
    649 private:
    650 
    651     // Map into to zones.  Our results are zone[map[i]] for
    652     // i=0..len-1, where zone[i] is the i-th Olson zone.  If map==NULL
    653     // then our results are zone[i] for i=0..len-1.  Len will be zero
    654     // if the zone data could not be loaded.
    655     int32_t* map;
    656     int32_t* localMap;
    657     int32_t  len;
    658     int32_t  pos;
    659 
    660     TZEnumeration(int32_t* mapData, int32_t mapLen, UBool adoptMapData) : pos(0) {
    661         map = mapData;
    662         localMap = adoptMapData ? mapData : NULL;
    663         len = mapLen;
    664     }
    665 
    666     UBool getID(int32_t i) {
    667         UErrorCode ec = U_ZERO_ERROR;
    668         int32_t idLen = 0;
    669         const UChar* id = NULL;
    670         UResourceBundle *top = ures_openDirect(0, kZONEINFO, &ec);
    671         top = ures_getByKey(top, kNAMES, top, &ec); // dereference Zones section
    672         id = ures_getStringByIndex(top, i, &idLen, &ec);
    673         if(U_FAILURE(ec)) {
    674             unistr.truncate(0);
    675         }
    676         else {
    677             unistr.fastCopyFrom(UnicodeString(TRUE, id, idLen));
    678         }
    679         ures_close(top);
    680         return U_SUCCESS(ec);
    681     }
    682 
    683     static int32_t* getMap(USystemTimeZoneType type, int32_t& len, UErrorCode& ec) {
    684         len = 0;
    685         if (U_FAILURE(ec)) {
    686             return NULL;
    687         }
    688         int32_t* m = NULL;
    689         switch (type) {
    690         case UCAL_ZONE_TYPE_ANY:
    691             m = MAP_SYSTEM_ZONES;
    692             len = LEN_SYSTEM_ZONES;
    693             break;
    694         case UCAL_ZONE_TYPE_CANONICAL:
    695             m = MAP_CANONICAL_SYSTEM_ZONES;
    696             len = LEN_CANONICAL_SYSTEM_ZONES;
    697             break;
    698         case UCAL_ZONE_TYPE_CANONICAL_LOCATION:
    699             m = MAP_CANONICAL_SYSTEM_LOCATION_ZONES;
    700             len = LEN_CANONICAL_SYSTEM_LOCATION_ZONES;
    701             break;
    702         }
    703         UBool needsInit = FALSE;
    704         UMTX_CHECK(&LOCK, (len == 0), needsInit);
    705         if (needsInit) {
    706             m = initMap(type, len, ec);
    707         }
    708         return m;
    709     }
    710 
    711     static int32_t* initMap(USystemTimeZoneType type, int32_t& len, UErrorCode& ec) {
    712         len = 0;
    713         if (U_FAILURE(ec)) {
    714             return NULL;
    715         }
    716 
    717         int32_t *result = NULL;
    718 
    719         UResourceBundle *res = ures_openDirect(0, kZONEINFO, &ec);
    720         res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section
    721         if (U_SUCCESS(ec)) {
    722             int32_t size = ures_getSize(res);
    723             int32_t *m = (int32_t *)uprv_malloc(size * sizeof(int32_t));
    724             if (m == NULL) {
    725                 ec = U_MEMORY_ALLOCATION_ERROR;
    726             } else {
    727                 int32_t numEntries = 0;
    728                 for (int32_t i = 0; i < size; i++) {
    729                     UnicodeString id = ures_getUnicodeStringByIndex(res, i, &ec);
    730                     if (U_FAILURE(ec)) {
    731                         break;
    732                     }
    733                     if (0 == id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH)) {
    734                         // exclude Etc/Unknown
    735                         continue;
    736                     }
    737                     if (type == UCAL_ZONE_TYPE_CANONICAL || type == UCAL_ZONE_TYPE_CANONICAL_LOCATION) {
    738                         UnicodeString canonicalID;
    739                         ZoneMeta::getCanonicalCLDRID(id, canonicalID, ec);
    740                         if (U_FAILURE(ec)) {
    741                             break;
    742                         }
    743                         if (canonicalID != id) {
    744                             // exclude aliases
    745                             continue;
    746                         }
    747                     }
    748                     if (type == UCAL_ZONE_TYPE_CANONICAL_LOCATION) {
    749                         const UChar *region = TimeZone::getRegion(id, ec);
    750                         if (U_FAILURE(ec)) {
    751                             break;
    752                         }
    753                         if (u_strcmp(region, WORLD) == 0) {
    754                            // exclude non-location ("001")
    755                             continue;
    756                         }
    757                     }
    758                     m[numEntries++] = i;
    759                 }
    760                 if (U_SUCCESS(ec)) {
    761                     int32_t *tmp = m;
    762                     m = (int32_t *)uprv_realloc(tmp, numEntries * sizeof(int32_t));
    763                     if (m == NULL) {
    764                         // realloc failed.. use the original one even it has unused
    765                         // area at the end
    766                         m = tmp;
    767                     }
    768 
    769                     umtx_lock(&LOCK);
    770                     {
    771                         switch(type) {
    772                         case UCAL_ZONE_TYPE_ANY:
    773                             if (MAP_SYSTEM_ZONES == NULL) {
    774                                 MAP_SYSTEM_ZONES = m;
    775                                 LEN_SYSTEM_ZONES = numEntries;
    776                                 m = NULL;
    777                                 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
    778                             }
    779                             result = MAP_SYSTEM_ZONES;
    780                             len = LEN_SYSTEM_ZONES;
    781                             break;
    782                         case UCAL_ZONE_TYPE_CANONICAL:
    783                             if (MAP_CANONICAL_SYSTEM_ZONES == NULL) {
    784                                 MAP_CANONICAL_SYSTEM_ZONES = m;
    785                                 LEN_CANONICAL_SYSTEM_ZONES = numEntries;
    786                                 m = NULL;
    787                                 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
    788                             }
    789                             result = MAP_CANONICAL_SYSTEM_ZONES;
    790                             len = LEN_CANONICAL_SYSTEM_ZONES;
    791                             break;
    792                         case UCAL_ZONE_TYPE_CANONICAL_LOCATION:
    793                             if (MAP_CANONICAL_SYSTEM_LOCATION_ZONES == NULL) {
    794                                 MAP_CANONICAL_SYSTEM_LOCATION_ZONES = m;
    795                                 LEN_CANONICAL_SYSTEM_LOCATION_ZONES = numEntries;
    796                                 m = NULL;
    797                                 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
    798                             }
    799                             result = MAP_CANONICAL_SYSTEM_LOCATION_ZONES;
    800                             len = LEN_CANONICAL_SYSTEM_LOCATION_ZONES;
    801                             break;
    802                         }
    803                     }
    804                     umtx_unlock(&LOCK);
    805                 }
    806                 uprv_free(m);
    807             }
    808         }
    809 
    810         ures_close(res);
    811         return result;
    812     }
    813 
    814 public:
    815 
    816 #define DEFAULT_FILTERED_MAP_SIZE 8
    817 #define MAP_INCREMENT_SIZE 8
    818 
    819     static TZEnumeration* create(USystemTimeZoneType type, const char* region, const int32_t* rawOffset, UErrorCode& ec) {
    820         if (U_FAILURE(ec)) {
    821             return NULL;
    822         }
    823 
    824         int32_t baseLen;
    825         int32_t *baseMap = getMap(type, baseLen, ec);
    826 
    827         if (U_FAILURE(ec)) {
    828             return NULL;
    829         }
    830 
    831         // If any additional conditions are available,
    832         // create instance local map filtered by the conditions.
    833 
    834         int32_t *filteredMap = NULL;
    835         int32_t numEntries = 0;
    836 
    837         if (region != NULL || rawOffset != NULL) {
    838             int32_t filteredMapSize = DEFAULT_FILTERED_MAP_SIZE;
    839             filteredMap = (int32_t *)uprv_malloc(filteredMapSize * sizeof(int32_t));
    840             if (filteredMap == NULL) {
    841                 ec = U_MEMORY_ALLOCATION_ERROR;
    842                 return NULL;
    843             }
    844 
    845             // Walk through the base map
    846             UResourceBundle *res = ures_openDirect(0, kZONEINFO, &ec);
    847             res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section
    848             for (int32_t i = 0; i < baseLen; i++) {
    849                 int32_t zidx = baseMap[i];
    850                 UnicodeString id = ures_getUnicodeStringByIndex(res, zidx, &ec);
    851                 if (U_FAILURE(ec)) {
    852                     break;
    853                 }
    854                 if (region != NULL) {
    855                     // Filter by region
    856                     char tzregion[4]; // max 3 letters + null term
    857                     TimeZone::getRegion(id, tzregion, sizeof(tzregion), ec);
    858                     if (U_FAILURE(ec)) {
    859                         break;
    860                     }
    861                     if (uprv_stricmp(tzregion, region) != 0) {
    862                         // region does not match
    863                         continue;
    864                     }
    865                 }
    866                 if (rawOffset != NULL) {
    867                     // Filter by raw offset
    868                     // Note: This is VERY inefficient
    869                     TimeZone *z = TimeZone::createSystemTimeZone(id, ec);
    870                     if (U_FAILURE(ec)) {
    871                         break;
    872                     }
    873                     int32_t tzoffset = z->getRawOffset();
    874                     delete z;
    875 
    876                     if (tzoffset != *rawOffset) {
    877                         continue;
    878                     }
    879                 }
    880 
    881                 if (filteredMapSize <= numEntries) {
    882                     filteredMapSize += MAP_INCREMENT_SIZE;
    883                     int32_t *tmp = (int32_t *)uprv_realloc(filteredMap, filteredMapSize * sizeof(int32_t));
    884                     if (tmp == NULL) {
    885                         ec = U_MEMORY_ALLOCATION_ERROR;
    886                         break;
    887                     } else {
    888                         filteredMap = tmp;
    889                     }
    890                 }
    891 
    892                 filteredMap[numEntries++] = zidx;
    893             }
    894 
    895             if (U_FAILURE(ec)) {
    896                 uprv_free(filteredMap);
    897                 filteredMap = NULL;
    898             }
    899 
    900             ures_close(res);
    901         }
    902 
    903         TZEnumeration *result = NULL;
    904         if (U_SUCCESS(ec)) {
    905             // Finally, create a new enumeration instance
    906             if (filteredMap == NULL) {
    907                 result = new TZEnumeration(baseMap, baseLen, FALSE);
    908             } else {
    909                 result = new TZEnumeration(filteredMap, numEntries, TRUE);
    910                 filteredMap = NULL;
    911             }
    912             if (result == NULL) {
    913                 ec = U_MEMORY_ALLOCATION_ERROR;
    914             }
    915         }
    916 
    917         if (filteredMap != NULL) {
    918             uprv_free(filteredMap);
    919         }
    920 
    921         return result;
    922     }
    923 
    924     TZEnumeration(const TZEnumeration &other) : StringEnumeration(), map(NULL), localMap(NULL), len(0), pos(0) {
    925         if (other.localMap != NULL) {
    926             localMap = (int32_t *)uprv_malloc(other.len * sizeof(int32_t));
    927             if (localMap != NULL) {
    928                 len = other.len;
    929                 uprv_memcpy(localMap, other.localMap, len * sizeof(int32_t));
    930                 pos = other.pos;
    931                 map = localMap;
    932             } else {
    933                 len = 0;
    934                 pos = 0;
    935                 map = NULL;
    936             }
    937         } else {
    938             map = other.map;
    939             localMap = NULL;
    940             len = other.len;
    941             pos = other.pos;
    942         }
    943     }
    944 
    945     virtual ~TZEnumeration();
    946 
    947     virtual StringEnumeration *clone() const {
    948         return new TZEnumeration(*this);
    949     }
    950 
    951     virtual int32_t count(UErrorCode& status) const {
    952         return U_FAILURE(status) ? 0 : len;
    953     }
    954 
    955     virtual const UnicodeString* snext(UErrorCode& status) {
    956         if (U_SUCCESS(status) && map != NULL && pos < len) {
    957             getID(map[pos]);
    958             ++pos;
    959             return &unistr;
    960         }
    961         return 0;
    962     }
    963 
    964     virtual void reset(UErrorCode& /*status*/) {
    965         pos = 0;
    966     }
    967 
    968 public:
    969     static UClassID U_EXPORT2 getStaticClassID(void);
    970     virtual UClassID getDynamicClassID(void) const;
    971 };
    972 
    973 TZEnumeration::~TZEnumeration() {
    974     if (localMap != NULL) {
    975         uprv_free(localMap);
    976     }
    977 }
    978 
    979 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TZEnumeration)
    980 
    981 StringEnumeration* U_EXPORT2
    982 TimeZone::createTimeZoneIDEnumeration(
    983             USystemTimeZoneType zoneType,
    984             const char* region,
    985             const int32_t* rawOffset,
    986             UErrorCode& ec) {
    987     return TZEnumeration::create(zoneType, region, rawOffset, ec);
    988 }
    989 
    990 StringEnumeration* U_EXPORT2
    991 TimeZone::createEnumeration() {
    992     UErrorCode ec = U_ZERO_ERROR;
    993     return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, NULL, NULL, ec);
    994 }
    995 
    996 StringEnumeration* U_EXPORT2
    997 TimeZone::createEnumeration(int32_t rawOffset) {
    998     UErrorCode ec = U_ZERO_ERROR;
    999     return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, NULL, &rawOffset, ec);
   1000 }
   1001 
   1002 StringEnumeration* U_EXPORT2
   1003 TimeZone::createEnumeration(const char* country) {
   1004     UErrorCode ec = U_ZERO_ERROR;
   1005     return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, country, NULL, ec);
   1006 }
   1007 
   1008 // ---------------------------------------
   1009 
   1010 int32_t U_EXPORT2
   1011 TimeZone::countEquivalentIDs(const UnicodeString& id) {
   1012     int32_t result = 0;
   1013     UErrorCode ec = U_ZERO_ERROR;
   1014     UResourceBundle res;
   1015     ures_initStackObject(&res);
   1016     U_DEBUG_TZ_MSG(("countEquivalentIDs..\n"));
   1017     UResourceBundle *top = openOlsonResource(id, res, ec);
   1018     if (U_SUCCESS(ec)) {
   1019         UResourceBundle r;
   1020         ures_initStackObject(&r);
   1021         ures_getByKey(&res, kLINKS, &r, &ec);
   1022         ures_getIntVector(&r, &result, &ec);
   1023         ures_close(&r);
   1024     }
   1025     ures_close(&res);
   1026     ures_close(top);
   1027     return result;
   1028 }
   1029 
   1030 // ---------------------------------------
   1031 
   1032 const UnicodeString U_EXPORT2
   1033 TimeZone::getEquivalentID(const UnicodeString& id, int32_t index) {
   1034     U_DEBUG_TZ_MSG(("gEI(%d)\n", index));
   1035     UnicodeString result;
   1036     UErrorCode ec = U_ZERO_ERROR;
   1037     UResourceBundle res;
   1038     ures_initStackObject(&res);
   1039     UResourceBundle *top = openOlsonResource(id, res, ec);
   1040     int32_t zone = -1;
   1041     if (U_SUCCESS(ec)) {
   1042         UResourceBundle r;
   1043         ures_initStackObject(&r);
   1044         int32_t size;
   1045         ures_getByKey(&res, kLINKS, &r, &ec);
   1046         const int32_t* v = ures_getIntVector(&r, &size, &ec);
   1047         if (U_SUCCESS(ec)) {
   1048             if (index >= 0 && index < size) {
   1049                 zone = v[index];
   1050             }
   1051         }
   1052         ures_close(&r);
   1053     }
   1054     ures_close(&res);
   1055     if (zone >= 0) {
   1056         UResourceBundle *ares = ures_getByKey(top, kNAMES, NULL, &ec); // dereference Zones section
   1057         if (U_SUCCESS(ec)) {
   1058             int32_t idLen = 0;
   1059             const UChar* id = ures_getStringByIndex(ares, zone, &idLen, &ec);
   1060             result.fastCopyFrom(UnicodeString(TRUE, id, idLen));
   1061             U_DEBUG_TZ_MSG(("gei(%d) -> %d, len%d, %s\n", index, zone, result.length(), u_errorName(ec)));
   1062         }
   1063         ures_close(ares);
   1064     }
   1065     ures_close(top);
   1066 #if defined(U_DEBUG_TZ)
   1067     if(result.length() ==0) {
   1068       U_DEBUG_TZ_MSG(("equiv [__, #%d] -> 0 (%s)\n", index, u_errorName(ec)));
   1069     }
   1070 #endif
   1071     return result;
   1072 }
   1073 
   1074 // ---------------------------------------
   1075 
   1076 // These methods are used by ZoneMeta class only.
   1077 
   1078 const UChar*
   1079 TimeZone::findID(const UnicodeString& id) {
   1080     const UChar *result = NULL;
   1081     UErrorCode ec = U_ZERO_ERROR;
   1082     UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &ec);
   1083 
   1084     // resolve zone index by name
   1085     UResourceBundle *names = ures_getByKey(rb, kNAMES, NULL, &ec);
   1086     int32_t idx = findInStringArray(names, id, ec);
   1087     result = ures_getStringByIndex(names, idx, NULL, &ec);
   1088     if (U_FAILURE(ec)) {
   1089         result = NULL;
   1090     }
   1091     ures_close(names);
   1092     ures_close(rb);
   1093     return result;
   1094 }
   1095 
   1096 
   1097 const UChar*
   1098 TimeZone::dereferOlsonLink(const UnicodeString& id) {
   1099     const UChar *result = NULL;
   1100     UErrorCode ec = U_ZERO_ERROR;
   1101     UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &ec);
   1102 
   1103     // resolve zone index by name
   1104     UResourceBundle *names = ures_getByKey(rb, kNAMES, NULL, &ec);
   1105     int32_t idx = findInStringArray(names, id, ec);
   1106     result = ures_getStringByIndex(names, idx, NULL, &ec);
   1107 
   1108     // open the zone bundle by index
   1109     ures_getByKey(rb, kZONES, rb, &ec);
   1110     ures_getByIndex(rb, idx, rb, &ec);
   1111 
   1112     if (U_SUCCESS(ec)) {
   1113         if (ures_getType(rb) == URES_INT) {
   1114             // this is a link - dereference the link
   1115             int32_t deref = ures_getInt(rb, &ec);
   1116             const UChar* tmp = ures_getStringByIndex(names, deref, NULL, &ec);
   1117             if (U_SUCCESS(ec)) {
   1118                 result = tmp;
   1119             }
   1120         }
   1121     }
   1122 
   1123     ures_close(names);
   1124     ures_close(rb);
   1125 
   1126     return result;
   1127 }
   1128 
   1129 const UChar*
   1130 TimeZone::getRegion(const UnicodeString& id) {
   1131     UErrorCode status = U_ZERO_ERROR;
   1132     return getRegion(id, status);
   1133 }
   1134 
   1135 const UChar*
   1136 TimeZone::getRegion(const UnicodeString& id, UErrorCode& status) {
   1137     if (U_FAILURE(status)) {
   1138         return NULL;
   1139     }
   1140     const UChar *result = NULL;
   1141     UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &status);
   1142 
   1143     // resolve zone index by name
   1144     UResourceBundle *res = ures_getByKey(rb, kNAMES, NULL, &status);
   1145     int32_t idx = findInStringArray(res, id, status);
   1146 
   1147     // get region mapping
   1148     ures_getByKey(rb, kREGIONS, res, &status);
   1149     const UChar *tmp = ures_getStringByIndex(res, idx, NULL, &status);
   1150     if (U_SUCCESS(status)) {
   1151         result = tmp;
   1152     }
   1153 
   1154     ures_close(res);
   1155     ures_close(rb);
   1156 
   1157     return result;
   1158 }
   1159 
   1160 
   1161 // ---------------------------------------
   1162 int32_t
   1163 TimeZone::getRegion(const UnicodeString& id, char *region, int32_t capacity, UErrorCode& status)
   1164 {
   1165     int32_t resultLen = 0;
   1166     *region = 0;
   1167     if (U_FAILURE(status)) {
   1168         return 0;
   1169     }
   1170 
   1171     const UChar *uregion = NULL;
   1172     // "Etc/Unknown" is not a system zone ID,
   1173     // but in the zone data
   1174     if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) != 0) {
   1175         uregion = getRegion(id);
   1176     }
   1177     if (uregion == NULL) {
   1178         status = U_ILLEGAL_ARGUMENT_ERROR;
   1179         return 0;
   1180     }
   1181     resultLen = u_strlen(uregion);
   1182     // A region code is represented by invariant characters
   1183     u_UCharsToChars(uregion, region, uprv_min(resultLen, capacity));
   1184 
   1185     if (capacity < resultLen) {
   1186         status = U_BUFFER_OVERFLOW_ERROR;
   1187         return resultLen;
   1188     }
   1189 
   1190     return u_terminateChars(region, capacity, resultLen, &status);
   1191 }
   1192 
   1193 // ---------------------------------------
   1194 
   1195 
   1196 UnicodeString&
   1197 TimeZone::getDisplayName(UnicodeString& result) const
   1198 {
   1199     return getDisplayName(FALSE,LONG,Locale::getDefault(), result);
   1200 }
   1201 
   1202 UnicodeString&
   1203 TimeZone::getDisplayName(const Locale& locale, UnicodeString& result) const
   1204 {
   1205     return getDisplayName(FALSE, LONG, locale, result);
   1206 }
   1207 
   1208 UnicodeString&
   1209 TimeZone::getDisplayName(UBool daylight, EDisplayType style, UnicodeString& result)  const
   1210 {
   1211     return getDisplayName(daylight,style, Locale::getDefault(), result);
   1212 }
   1213 //--------------------------------------
   1214 int32_t
   1215 TimeZone::getDSTSavings()const {
   1216     if (useDaylightTime()) {
   1217         return 3600000;
   1218     }
   1219     return 0;
   1220 }
   1221 //---------------------------------------
   1222 UnicodeString&
   1223 TimeZone::getDisplayName(UBool daylight, EDisplayType style, const Locale& locale, UnicodeString& result) const
   1224 {
   1225     UErrorCode status = U_ZERO_ERROR;
   1226     UDate date = Calendar::getNow();
   1227     UTimeZoneFormatTimeType timeType;
   1228     int32_t offset;
   1229 
   1230     if (style == GENERIC_LOCATION || style == LONG_GENERIC || style == SHORT_GENERIC) {
   1231         LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status));
   1232         if (U_FAILURE(status)) {
   1233             result.remove();
   1234             return result;
   1235         }
   1236         // Generic format
   1237         switch (style) {
   1238         case GENERIC_LOCATION:
   1239             tzfmt->format(UTZFMT_STYLE_GENERIC_LOCATION, *this, date, result, &timeType);
   1240             break;
   1241         case LONG_GENERIC:
   1242             tzfmt->format(UTZFMT_STYLE_GENERIC_LONG, *this, date, result, &timeType);
   1243             break;
   1244         case SHORT_GENERIC:
   1245             tzfmt->format(UTZFMT_STYLE_GENERIC_SHORT, *this, date, result, &timeType);
   1246             break;
   1247         default:
   1248             U_ASSERT(FALSE);
   1249         }
   1250         // Generic format many use Localized GMT as the final fallback.
   1251         // When Localized GMT format is used, the result might not be
   1252         // appropriate for the requested daylight value.
   1253         if ((daylight && timeType == UTZFMT_TIME_TYPE_STANDARD) || (!daylight && timeType == UTZFMT_TIME_TYPE_DAYLIGHT)) {
   1254             offset = daylight ? getRawOffset() + getDSTSavings() : getRawOffset();
   1255             tzfmt->formatOffsetLocalizedGMT(offset, result, status);
   1256         }
   1257     } else if (style == LONG_GMT || style == SHORT_GMT) {
   1258         LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status));
   1259         if (U_FAILURE(status)) {
   1260             result.remove();
   1261             return result;
   1262         }
   1263         offset = daylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset();
   1264         switch (style) {
   1265         case LONG_GMT:
   1266             tzfmt->formatOffsetLocalizedGMT(offset, result, status);
   1267             break;
   1268         case SHORT_GMT:
   1269             tzfmt->formatOffsetRFC822(offset, result, status);
   1270             break;
   1271         default:
   1272             U_ASSERT(FALSE);
   1273         }
   1274 
   1275     } else {
   1276         U_ASSERT(style == LONG || style == SHORT || style == SHORT_COMMONLY_USED);
   1277         UTimeZoneNameType nameType = UTZNM_UNKNOWN;
   1278         switch (style) {
   1279         case LONG:
   1280             nameType = daylight ? UTZNM_LONG_DAYLIGHT : UTZNM_LONG_STANDARD;
   1281             break;
   1282         case SHORT:
   1283         case SHORT_COMMONLY_USED:
   1284             nameType = daylight ? UTZNM_SHORT_DAYLIGHT : UTZNM_SHORT_STANDARD;
   1285             break;
   1286         default:
   1287             U_ASSERT(FALSE);
   1288         }
   1289         LocalPointer<TimeZoneNames> tznames(TimeZoneNames::createInstance(locale, status));
   1290         if (U_FAILURE(status)) {
   1291             result.remove();
   1292             return result;
   1293         }
   1294         UnicodeString canonicalID(ZoneMeta::getCanonicalCLDRID(*this));
   1295         tznames->getDisplayName(canonicalID, nameType, date, result);
   1296         if (result.isEmpty()) {
   1297             // Fallback to localized GMT
   1298             LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status));
   1299             offset = daylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset();
   1300             tzfmt->formatOffsetLocalizedGMT(offset, result, status);
   1301         }
   1302     }
   1303     if (U_FAILURE(status)) {
   1304         result.remove();
   1305     }
   1306     return  result;
   1307 }
   1308 
   1309 /**
   1310  * Parse a custom time zone identifier and return a corresponding zone.
   1311  * @param id a string of the form GMT[+-]hh:mm, GMT[+-]hhmm, or
   1312  * GMT[+-]hh.
   1313  * @return a newly created SimpleTimeZone with the given offset and
   1314  * no Daylight Savings Time, or null if the id cannot be parsed.
   1315 */
   1316 TimeZone*
   1317 TimeZone::createCustomTimeZone(const UnicodeString& id)
   1318 {
   1319     int32_t sign, hour, min, sec;
   1320     if (parseCustomID(id, sign, hour, min, sec)) {
   1321         UnicodeString customID;
   1322         formatCustomID(hour, min, sec, (sign < 0), customID);
   1323         int32_t offset = sign * ((hour * 60 + min) * 60 + sec) * 1000;
   1324         return new SimpleTimeZone(offset, customID);
   1325     }
   1326     return NULL;
   1327 }
   1328 
   1329 UnicodeString&
   1330 TimeZone::getCustomID(const UnicodeString& id, UnicodeString& normalized, UErrorCode& status) {
   1331     normalized.remove();
   1332     if (U_FAILURE(status)) {
   1333         return normalized;
   1334     }
   1335     int32_t sign, hour, min, sec;
   1336     if (parseCustomID(id, sign, hour, min, sec)) {
   1337         formatCustomID(hour, min, sec, (sign < 0), normalized);
   1338     }
   1339     return normalized;
   1340 }
   1341 
   1342 UBool
   1343 TimeZone::parseCustomID(const UnicodeString& id, int32_t& sign,
   1344                         int32_t& hour, int32_t& min, int32_t& sec) {
   1345     static const int32_t         kParseFailed = -99999;
   1346 
   1347     NumberFormat* numberFormat = 0;
   1348     UnicodeString idUppercase = id;
   1349     idUppercase.toUpper("");
   1350 
   1351     if (id.length() > GMT_ID_LENGTH &&
   1352         idUppercase.startsWith(GMT_ID, GMT_ID_LENGTH))
   1353     {
   1354         ParsePosition pos(GMT_ID_LENGTH);
   1355         sign = 1;
   1356         hour = 0;
   1357         min = 0;
   1358         sec = 0;
   1359 
   1360         if (id[pos.getIndex()] == MINUS /*'-'*/) {
   1361             sign = -1;
   1362         } else if (id[pos.getIndex()] != PLUS /*'+'*/) {
   1363             return FALSE;
   1364         }
   1365         pos.setIndex(pos.getIndex() + 1);
   1366 
   1367         UErrorCode success = U_ZERO_ERROR;
   1368         numberFormat = NumberFormat::createInstance(success);
   1369         if(U_FAILURE(success)){
   1370             return FALSE;
   1371         }
   1372         numberFormat->setParseIntegerOnly(TRUE);
   1373         //numberFormat->setLenient(TRUE); // TODO: May need to set this, depends on latest timezone parsing
   1374 
   1375         // Look for either hh:mm, hhmm, or hh
   1376         int32_t start = pos.getIndex();
   1377         Formattable n(kParseFailed);
   1378         numberFormat->parse(id, n, pos);
   1379         if (pos.getIndex() == start) {
   1380             delete numberFormat;
   1381             return FALSE;
   1382         }
   1383         hour = n.getLong();
   1384 
   1385         if (pos.getIndex() < id.length()) {
   1386             if (pos.getIndex() - start > 2
   1387                 || id[pos.getIndex()] != COLON) {
   1388                 delete numberFormat;
   1389                 return FALSE;
   1390             }
   1391             // hh:mm
   1392             pos.setIndex(pos.getIndex() + 1);
   1393             int32_t oldPos = pos.getIndex();
   1394             n.setLong(kParseFailed);
   1395             numberFormat->parse(id, n, pos);
   1396             if ((pos.getIndex() - oldPos) != 2) {
   1397                 // must be 2 digits
   1398                 delete numberFormat;
   1399                 return FALSE;
   1400             }
   1401             min = n.getLong();
   1402             if (pos.getIndex() < id.length()) {
   1403                 if (id[pos.getIndex()] != COLON) {
   1404                     delete numberFormat;
   1405                     return FALSE;
   1406                 }
   1407                 // [:ss]
   1408                 pos.setIndex(pos.getIndex() + 1);
   1409                 oldPos = pos.getIndex();
   1410                 n.setLong(kParseFailed);
   1411                 numberFormat->parse(id, n, pos);
   1412                 if (pos.getIndex() != id.length()
   1413                         || (pos.getIndex() - oldPos) != 2) {
   1414                     delete numberFormat;
   1415                     return FALSE;
   1416                 }
   1417                 sec = n.getLong();
   1418             }
   1419         } else {
   1420             // Supported formats are below -
   1421             //
   1422             // HHmmss
   1423             // Hmmss
   1424             // HHmm
   1425             // Hmm
   1426             // HH
   1427             // H
   1428 
   1429             int32_t length = pos.getIndex() - start;
   1430             if (length <= 0 || 6 < length) {
   1431                 // invalid length
   1432                 delete numberFormat;
   1433                 return FALSE;
   1434             }
   1435             switch (length) {
   1436                 case 1:
   1437                 case 2:
   1438                     // already set to hour
   1439                     break;
   1440                 case 3:
   1441                 case 4:
   1442                     min = hour % 100;
   1443                     hour /= 100;
   1444                     break;
   1445                 case 5:
   1446                 case 6:
   1447                     sec = hour % 100;
   1448                     min = (hour/100) % 100;
   1449                     hour /= 10000;
   1450                     break;
   1451             }
   1452         }
   1453 
   1454         delete numberFormat;
   1455 
   1456         if (hour > kMAX_CUSTOM_HOUR || min > kMAX_CUSTOM_MIN || sec > kMAX_CUSTOM_SEC) {
   1457             return FALSE;
   1458         }
   1459         return TRUE;
   1460     }
   1461     return FALSE;
   1462 }
   1463 
   1464 UnicodeString&
   1465 TimeZone::formatCustomID(int32_t hour, int32_t min, int32_t sec,
   1466                          UBool negative, UnicodeString& id) {
   1467     // Create time zone ID - GMT[+|-]hhmm[ss]
   1468     id.setTo(GMT_ID, GMT_ID_LENGTH);
   1469     if (hour | min | sec) {
   1470         if (negative) {
   1471             id += (UChar)MINUS;
   1472         } else {
   1473             id += (UChar)PLUS;
   1474         }
   1475 
   1476         if (hour < 10) {
   1477             id += (UChar)ZERO_DIGIT;
   1478         } else {
   1479             id += (UChar)(ZERO_DIGIT + hour/10);
   1480         }
   1481         id += (UChar)(ZERO_DIGIT + hour%10);
   1482         id += (UChar)COLON;
   1483         if (min < 10) {
   1484             id += (UChar)ZERO_DIGIT;
   1485         } else {
   1486             id += (UChar)(ZERO_DIGIT + min/10);
   1487         }
   1488         id += (UChar)(ZERO_DIGIT + min%10);
   1489 
   1490         if (sec) {
   1491             id += (UChar)COLON;
   1492             if (sec < 10) {
   1493                 id += (UChar)ZERO_DIGIT;
   1494             } else {
   1495                 id += (UChar)(ZERO_DIGIT + sec/10);
   1496             }
   1497             id += (UChar)(ZERO_DIGIT + sec%10);
   1498         }
   1499     }
   1500     return id;
   1501 }
   1502 
   1503 
   1504 UBool
   1505 TimeZone::hasSameRules(const TimeZone& other) const
   1506 {
   1507     return (getRawOffset() == other.getRawOffset() &&
   1508             useDaylightTime() == other.useDaylightTime());
   1509 }
   1510 
   1511 const char*
   1512 TimeZone::getTZDataVersion(UErrorCode& status)
   1513 {
   1514     /* This is here to prevent race conditions. */
   1515     UBool needsInit;
   1516     UMTX_CHECK(&LOCK, !TZDataVersionInitialized, needsInit);
   1517     if (needsInit) {
   1518         int32_t len = 0;
   1519         UResourceBundle *bundle = ures_openDirect(NULL, kZONEINFO, &status);
   1520         const UChar *tzver = ures_getStringByKey(bundle, kTZVERSION,
   1521             &len, &status);
   1522 
   1523         if (U_SUCCESS(status)) {
   1524             if (len >= (int32_t)sizeof(TZDATA_VERSION)) {
   1525                 // Ensure that there is always space for a trailing nul in TZDATA_VERSION
   1526                 len = sizeof(TZDATA_VERSION) - 1;
   1527             }
   1528             umtx_lock(&LOCK);
   1529             if (!TZDataVersionInitialized) {
   1530                 u_UCharsToChars(tzver, TZDATA_VERSION, len);
   1531                 TZDataVersionInitialized = TRUE;
   1532             }
   1533             umtx_unlock(&LOCK);
   1534             ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
   1535         }
   1536 
   1537         ures_close(bundle);
   1538     }
   1539     if (U_FAILURE(status)) {
   1540         return NULL;
   1541     }
   1542     return (const char*)TZDATA_VERSION;
   1543 }
   1544 
   1545 UnicodeString&
   1546 TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UErrorCode& status)
   1547 {
   1548     UBool isSystemID = FALSE;
   1549     return getCanonicalID(id, canonicalID, isSystemID, status);
   1550 }
   1551 
   1552 UnicodeString&
   1553 TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UBool& isSystemID,
   1554                          UErrorCode& status)
   1555 {
   1556     canonicalID.remove();
   1557     isSystemID = FALSE;
   1558     if (U_FAILURE(status)) {
   1559         return canonicalID;
   1560     }
   1561     if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) == 0) {
   1562         // special case - Etc/Unknown is a canonical ID, but not system ID
   1563         canonicalID.fastCopyFrom(id);
   1564         isSystemID = FALSE;
   1565     } else {
   1566         ZoneMeta::getCanonicalCLDRID(id, canonicalID, status);
   1567         if (U_SUCCESS(status)) {
   1568             isSystemID = TRUE;
   1569         } else {
   1570             // Not a system ID
   1571             status = U_ZERO_ERROR;
   1572             getCustomID(id, canonicalID, status);
   1573         }
   1574     }
   1575     return canonicalID;
   1576 }
   1577 
   1578 U_NAMESPACE_END
   1579 
   1580 #endif /* #if !UCONFIG_NO_FORMATTING */
   1581 
   1582 //eof
   1583