tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

timezone.cpp (56507B)


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