tor-browser

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

tzgnames.cpp (44655B)


      1 // © 2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 /*
      4 *******************************************************************************
      5 * Copyright (C) 2011-2016, International Business Machines Corporation and
      6 * others. All Rights Reserved.
      7 *******************************************************************************
      8 */
      9 
     10 #include "unicode/utypes.h"
     11 
     12 #if !UCONFIG_NO_FORMATTING
     13 
     14 #include "tzgnames.h"
     15 
     16 #include "unicode/basictz.h"
     17 #include "unicode/locdspnm.h"
     18 #include "unicode/rbtz.h"
     19 #include "unicode/simpleformatter.h"
     20 #include "unicode/simpletz.h"
     21 #include "unicode/strenum.h"
     22 #include "unicode/vtzone.h"
     23 
     24 #include "charstr.h"
     25 #include "cmemory.h"
     26 #include "cstring.h"
     27 #include "fixedstring.h"
     28 #include "mutex.h"
     29 #include "uhash.h"
     30 #include "uassert.h"
     31 #include "umutex.h"
     32 #include "ulocimp.h"
     33 #include "uresimp.h"
     34 #include "ureslocs.h"
     35 #include "zonemeta.h"
     36 #include "tznames_impl.h"
     37 #include "olsontz.h"
     38 #include "ucln_in.h"
     39 
     40 U_NAMESPACE_BEGIN
     41 
     42 #define ZID_KEY_MAX  128
     43 
     44 static const char gZoneStrings[]                = "zoneStrings";
     45 
     46 static const char gRegionFormatTag[]            = "regionFormat";
     47 static const char gFallbackFormatTag[]          = "fallbackFormat";
     48 
     49 static const char16_t gEmpty[]                     = {0x00};
     50 
     51 static const char16_t gDefRegionPattern[]          = {0x7B, 0x30, 0x7D, 0x00}; // "{0}"
     52 static const char16_t gDefFallbackPattern[]        = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})"
     53 
     54 static const double kDstCheckRange = static_cast<double>(184) * U_MILLIS_PER_DAY;
     55 
     56 
     57 
     58 U_CDECL_BEGIN
     59 
     60 typedef struct PartialLocationKey {
     61    const char16_t* tzID;
     62    const char16_t* mzID;
     63    UBool isLong;
     64 } PartialLocationKey;
     65 
     66 /**
     67 * Hash function for partial location name hash key
     68 */
     69 static int32_t U_CALLCONV
     70 hashPartialLocationKey(const UHashTok key) {
     71    // <tzID>&<mzID>#[L|S]
     72    PartialLocationKey *p = (PartialLocationKey *)key.pointer;
     73    UnicodeString str(p->tzID);
     74    str.append((char16_t)0x26)
     75        .append(p->mzID, -1)
     76        .append((char16_t)0x23)
     77        .append((char16_t)(p->isLong ? 0x4C : 0x53));
     78    return str.hashCode();
     79 }
     80 
     81 /**
     82 * Comparer for partial location name hash key
     83 */
     84 static UBool U_CALLCONV
     85 comparePartialLocationKey(const UHashTok key1, const UHashTok key2) {
     86    PartialLocationKey *p1 = (PartialLocationKey *)key1.pointer;
     87    PartialLocationKey *p2 = (PartialLocationKey *)key2.pointer;
     88 
     89    if (p1 == p2) {
     90        return true;
     91    }
     92    if (p1 == nullptr || p2 == nullptr) {
     93        return false;
     94    }
     95    // We just check identity of tzID/mzID
     96    return (p1->tzID == p2->tzID && p1->mzID == p2->mzID && p1->isLong == p2->isLong);
     97 }
     98 
     99 /**
    100 * Deleter for GNameInfo
    101 */
    102 static void U_CALLCONV
    103 deleteGNameInfo(void *obj) {
    104    uprv_free(obj);
    105 }
    106 
    107 /**
    108 * GNameInfo stores zone name information in the local trie
    109 */
    110 typedef struct GNameInfo {
    111    UTimeZoneGenericNameType    type;
    112    const char16_t*                tzID;
    113 } ZNameInfo;
    114 
    115 /**
    116 * GMatchInfo stores zone name match information used by find method
    117 */
    118 typedef struct GMatchInfo {
    119    const GNameInfo*    gnameInfo;
    120    int32_t             matchLength;
    121    UTimeZoneFormatTimeType   timeType;
    122 } ZMatchInfo;
    123 
    124 U_CDECL_END
    125 
    126 // ---------------------------------------------------
    127 // The class stores time zone generic name match information
    128 // ---------------------------------------------------
    129 class TimeZoneGenericNameMatchInfo : public UMemory {
    130 public:
    131    TimeZoneGenericNameMatchInfo(UVector* matches);
    132    ~TimeZoneGenericNameMatchInfo();
    133 
    134    int32_t size() const;
    135    UTimeZoneGenericNameType getGenericNameType(int32_t index) const;
    136    int32_t getMatchLength(int32_t index) const;
    137    UnicodeString& getTimeZoneID(int32_t index, UnicodeString& tzID) const;
    138 
    139 private:
    140    UVector* fMatches;  // vector of MatchEntry
    141 };
    142 
    143 TimeZoneGenericNameMatchInfo::TimeZoneGenericNameMatchInfo(UVector* matches)
    144 : fMatches(matches) {
    145 }
    146 
    147 TimeZoneGenericNameMatchInfo::~TimeZoneGenericNameMatchInfo() {
    148    delete fMatches;
    149 }
    150 
    151 int32_t
    152 TimeZoneGenericNameMatchInfo::size() const {
    153    if (fMatches == nullptr) {
    154        return 0;
    155    }
    156    return fMatches->size();
    157 }
    158 
    159 UTimeZoneGenericNameType
    160 TimeZoneGenericNameMatchInfo::getGenericNameType(int32_t index) const {
    161    GMatchInfo* minfo = static_cast<GMatchInfo*>(fMatches->elementAt(index));
    162    if (minfo != nullptr) {
    163        return static_cast<UTimeZoneGenericNameType>(minfo->gnameInfo->type);
    164    }
    165    return UTZGNM_UNKNOWN;
    166 }
    167 
    168 int32_t
    169 TimeZoneGenericNameMatchInfo::getMatchLength(int32_t index) const {
    170    ZMatchInfo* minfo = static_cast<ZMatchInfo*>(fMatches->elementAt(index));
    171    if (minfo != nullptr) {
    172        return minfo->matchLength;
    173    }
    174    return -1;
    175 }
    176 
    177 UnicodeString&
    178 TimeZoneGenericNameMatchInfo::getTimeZoneID(int32_t index, UnicodeString& tzID) const {
    179    GMatchInfo* minfo = static_cast<GMatchInfo*>(fMatches->elementAt(index));
    180    if (minfo != nullptr && minfo->gnameInfo->tzID != nullptr) {
    181        tzID.setTo(true, minfo->gnameInfo->tzID, -1);
    182    } else {
    183        tzID.setToBogus();
    184    }
    185    return tzID;
    186 }
    187 
    188 // ---------------------------------------------------
    189 // GNameSearchHandler
    190 // ---------------------------------------------------
    191 class GNameSearchHandler : public TextTrieMapSearchResultHandler {
    192 public:
    193    GNameSearchHandler(uint32_t types);
    194    virtual ~GNameSearchHandler();
    195 
    196    UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) override;
    197    UVector* getMatches(int32_t& maxMatchLen);
    198 
    199 private:
    200    uint32_t fTypes;
    201    UVector* fResults;
    202    int32_t fMaxMatchLen;
    203 };
    204 
    205 GNameSearchHandler::GNameSearchHandler(uint32_t types)
    206 : fTypes(types), fResults(nullptr), fMaxMatchLen(0) {
    207 }
    208 
    209 GNameSearchHandler::~GNameSearchHandler() {
    210    delete fResults;
    211 }
    212 
    213 UBool
    214 GNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
    215    if (U_FAILURE(status)) {
    216        return false;
    217    }
    218    if (node->hasValues()) {
    219        int32_t valuesCount = node->countValues();
    220        for (int32_t i = 0; i < valuesCount; i++) {
    221            GNameInfo *nameinfo = (ZNameInfo *)node->getValue(i);
    222            if (nameinfo == nullptr) {
    223                break;
    224            }
    225            if ((nameinfo->type & fTypes) != 0) {
    226                // matches a requested type
    227                if (fResults == nullptr) {
    228                    LocalPointer<UVector> lpResults(new UVector(uprv_free, nullptr, status), status);
    229                    if (U_FAILURE(status)) {
    230                        return false;
    231                    }
    232                    fResults = lpResults.orphan();
    233                }
    234                GMatchInfo* gmatch = static_cast<GMatchInfo*>(uprv_malloc(sizeof(GMatchInfo)));
    235                if (gmatch == nullptr) {
    236                    status = U_MEMORY_ALLOCATION_ERROR;
    237                    return false;
    238                }
    239                // add the match to the vector
    240                gmatch->gnameInfo = nameinfo;
    241                gmatch->matchLength = matchLength;
    242                gmatch->timeType = UTZFMT_TIME_TYPE_UNKNOWN;
    243                fResults->adoptElement(gmatch, status);
    244                if (U_FAILURE(status)) {
    245                    return false;
    246                }
    247                if (matchLength > fMaxMatchLen) {
    248                    fMaxMatchLen = matchLength;
    249                }
    250            }
    251        }
    252    }
    253    return true;
    254 }
    255 
    256 UVector*
    257 GNameSearchHandler::getMatches(int32_t& maxMatchLen) {
    258    // give the ownership to the caller
    259    UVector *results = fResults;
    260    maxMatchLen = fMaxMatchLen;
    261 
    262    // reset
    263    fResults = nullptr;
    264    fMaxMatchLen = 0;
    265    return results;
    266 }
    267 
    268 static UMutex gLock;
    269 
    270 class TZGNCore : public UMemory {
    271 public:
    272    TZGNCore(const Locale& locale, UErrorCode& status);
    273    virtual ~TZGNCore();
    274 
    275    UnicodeString& getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,
    276                        UDate date, UnicodeString& name) const;
    277 
    278    UnicodeString& getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const;
    279 
    280    int32_t findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
    281        UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const;
    282 
    283 private:
    284    Locale fLocale;
    285    const TimeZoneNames* fTimeZoneNames;
    286    UHashtable* fLocationNamesMap;
    287    UHashtable* fPartialLocationNamesMap;
    288 
    289    SimpleFormatter fRegionFormat;
    290    SimpleFormatter fFallbackFormat;
    291 
    292    LocaleDisplayNames* fLocaleDisplayNames;
    293    ZNStringPool fStringPool;
    294 
    295    TextTrieMap fGNamesTrie;
    296    UBool fGNamesTrieFullyLoaded;
    297 
    298    FixedString fTargetRegion;
    299 
    300    void initialize(const Locale& locale, UErrorCode& status);
    301    void cleanup();
    302 
    303    void loadStrings(const UnicodeString& tzCanonicalID);
    304 
    305    const char16_t* getGenericLocationName(const UnicodeString& tzCanonicalID);
    306 
    307    UnicodeString& formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type,
    308                        UDate date, UnicodeString& name) const;
    309 
    310    UnicodeString& getPartialLocationName(const UnicodeString& tzCanonicalID,
    311                        const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName,
    312                        UnicodeString& name) const;
    313 
    314    const char16_t* getPartialLocationName(const UnicodeString& tzCanonicalID,
    315                        const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName);
    316 
    317    TimeZoneGenericNameMatchInfo* findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
    318 
    319    TimeZoneNames::MatchInfoCollection* findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
    320 };
    321 
    322 
    323 // ---------------------------------------------------
    324 // TZGNCore - core implementation of TimeZoneGenericNames
    325 //
    326 // TimeZoneGenericNames is parallel to TimeZoneNames,
    327 // but handles run-time generated time zone names.
    328 // This is the main part of this module.
    329 // ---------------------------------------------------
    330 TZGNCore::TZGNCore(const Locale& locale, UErrorCode& status)
    331 : fLocale(locale),
    332  fTimeZoneNames(nullptr),
    333  fLocationNamesMap(nullptr),
    334  fPartialLocationNamesMap(nullptr),
    335  fLocaleDisplayNames(nullptr),
    336  fStringPool(status),
    337  fGNamesTrie(true, deleteGNameInfo),
    338  fGNamesTrieFullyLoaded(false),
    339  fTargetRegion() {
    340    initialize(locale, status);
    341 }
    342 
    343 TZGNCore::~TZGNCore() {
    344    cleanup();
    345 }
    346 
    347 void
    348 TZGNCore::initialize(const Locale& locale, UErrorCode& status) {
    349    if (U_FAILURE(status)) {
    350        return;
    351    }
    352 
    353    // TimeZoneNames
    354    fTimeZoneNames = TimeZoneNames::createInstance(locale, status);
    355    if (U_FAILURE(status)) {
    356        return;
    357    }
    358 
    359    // Initialize format patterns
    360    UnicodeString rpat(true, gDefRegionPattern, -1);
    361    UnicodeString fpat(true, gDefFallbackPattern, -1);
    362 
    363    UErrorCode tmpsts = U_ZERO_ERROR;   // OK with fallback warning..
    364    UResourceBundle *zoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts);
    365    zoneStrings = ures_getByKeyWithFallback(zoneStrings, gZoneStrings, zoneStrings, &tmpsts);
    366 
    367    if (U_SUCCESS(tmpsts)) {
    368        const char16_t *regionPattern = ures_getStringByKeyWithFallback(zoneStrings, gRegionFormatTag, nullptr, &tmpsts);
    369        if (U_SUCCESS(tmpsts) && u_strlen(regionPattern) > 0) {
    370            rpat.setTo(regionPattern, -1);
    371        }
    372        tmpsts = U_ZERO_ERROR;
    373        const char16_t *fallbackPattern = ures_getStringByKeyWithFallback(zoneStrings, gFallbackFormatTag, nullptr, &tmpsts);
    374        if (U_SUCCESS(tmpsts) && u_strlen(fallbackPattern) > 0) {
    375            fpat.setTo(fallbackPattern, -1);
    376        }
    377    }
    378    ures_close(zoneStrings);
    379 
    380    fRegionFormat.applyPatternMinMaxArguments(rpat, 1, 1, status);
    381    fFallbackFormat.applyPatternMinMaxArguments(fpat, 2, 2, status);
    382    if (U_FAILURE(status)) {
    383        cleanup();
    384        return;
    385    }
    386 
    387    // locale display names
    388    fLocaleDisplayNames = LocaleDisplayNames::createInstance(locale);
    389 
    390    // hash table for names - no key/value deleters
    391    fLocationNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, nullptr, &status);
    392    if (U_FAILURE(status)) {
    393        cleanup();
    394        return;
    395    }
    396 
    397    fPartialLocationNamesMap = uhash_open(hashPartialLocationKey, comparePartialLocationKey, nullptr, &status);
    398    if (U_FAILURE(status)) {
    399        cleanup();
    400        return;
    401    }
    402    uhash_setKeyDeleter(fPartialLocationNamesMap, uprv_free);
    403    // no value deleter
    404 
    405    // target region
    406    const char* region = fLocale.getCountry();
    407    int32_t regionLen = static_cast<int32_t>(uprv_strlen(region));
    408    if (regionLen == 0) {
    409        CharString loc = ulocimp_addLikelySubtags(fLocale.getName(), status);
    410        CharString tmp;
    411        ulocimp_getSubtags(loc.toStringPiece(), nullptr, nullptr, &tmp, nullptr, nullptr, status);
    412        if (U_FAILURE(status)) {
    413            cleanup();
    414            return;
    415        }
    416        fTargetRegion = tmp.toStringPiece();
    417        if (fTargetRegion.isEmpty() != tmp.isEmpty()) {
    418            status = U_MEMORY_ALLOCATION_ERROR;
    419            cleanup();
    420            return;
    421        }
    422    } else {
    423        fTargetRegion = {region, static_cast<std::string_view::size_type>(regionLen)};
    424        if (fTargetRegion.isEmpty()) {
    425            status = U_MEMORY_ALLOCATION_ERROR;
    426            cleanup();
    427            return;
    428        }
    429    }
    430 
    431    // preload generic names for the default zone
    432    TimeZone *tz = TimeZone::createDefault();
    433    const char16_t *tzID = ZoneMeta::getCanonicalCLDRID(*tz);
    434    if (tzID != nullptr) {
    435        loadStrings(UnicodeString(true, tzID, -1));
    436    }
    437    delete tz;
    438 }
    439 
    440 void
    441 TZGNCore::cleanup() {
    442    delete fLocaleDisplayNames;
    443    delete fTimeZoneNames;
    444 
    445    uhash_close(fLocationNamesMap);
    446    uhash_close(fPartialLocationNamesMap);
    447 }
    448 
    449 
    450 UnicodeString&
    451 TZGNCore::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const {
    452    name.setToBogus();
    453    switch (type) {
    454    case UTZGNM_LOCATION:
    455        {
    456            const char16_t* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz);
    457            if (tzCanonicalID != nullptr) {
    458                getGenericLocationName(UnicodeString(true, tzCanonicalID, -1), name);
    459            }
    460        }
    461        break;
    462    case UTZGNM_LONG:
    463    case UTZGNM_SHORT:
    464        formatGenericNonLocationName(tz, type, date, name);
    465        if (name.isEmpty()) {
    466            const char16_t* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz);
    467            if (tzCanonicalID != nullptr) {
    468                getGenericLocationName(UnicodeString(true, tzCanonicalID, -1), name);
    469            }
    470        }
    471        break;
    472    default:
    473        break;
    474    }
    475    return name;
    476 }
    477 
    478 UnicodeString&
    479 TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
    480    if (tzCanonicalID.isEmpty()) {
    481        name.setToBogus();
    482        return name;
    483    }
    484 
    485    const char16_t *locname = nullptr;
    486    TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
    487    umtx_lock(&gLock);
    488    {
    489        locname = nonConstThis->getGenericLocationName(tzCanonicalID);
    490    }
    491    umtx_unlock(&gLock);
    492 
    493    if (locname == nullptr) {
    494        name.setToBogus();
    495    } else {
    496        name.setTo(locname, u_strlen(locname));
    497    }
    498 
    499    return name;
    500 }
    501 
    502 /*
    503 * This method updates the cache and must be called with a lock
    504 */
    505 const char16_t*
    506 TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID) {
    507    U_ASSERT(!tzCanonicalID.isEmpty());
    508    if (tzCanonicalID.length() > ZID_KEY_MAX) {
    509        return nullptr;
    510    }
    511 
    512    UErrorCode status = U_ZERO_ERROR;
    513    char16_t tzIDKey[ZID_KEY_MAX + 1];
    514    int32_t tzIDKeyLen = tzCanonicalID.extract(tzIDKey, ZID_KEY_MAX + 1, status);
    515    U_ASSERT(status == U_ZERO_ERROR);   // already checked length above
    516    tzIDKey[tzIDKeyLen] = 0;
    517 
    518    const char16_t* locname = static_cast<const char16_t*>(uhash_get(fLocationNamesMap, tzIDKey));
    519 
    520    if (locname != nullptr) {
    521        // gEmpty indicate the name is not available
    522        if (locname == gEmpty) {
    523            return nullptr;
    524        }
    525        return locname;
    526    }
    527 
    528    // Construct location name
    529    UnicodeString name;
    530    UnicodeString usCountryCode;
    531    UBool isPrimary = false;
    532 
    533    ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode, &isPrimary);
    534 
    535    if (!usCountryCode.isEmpty()) {
    536        if (isPrimary) {
    537            // If this is the primary zone in the country, use the country name.
    538            char countryCode[ULOC_COUNTRY_CAPACITY];
    539            U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY);
    540            int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV);
    541            countryCode[ccLen] = 0;
    542 
    543            UnicodeString country;
    544            fLocaleDisplayNames->regionDisplayName(countryCode, country);
    545            fRegionFormat.format(country, name, status);
    546        } else {
    547            // If this is not the primary zone in the country,
    548            // use the exemplar city name.
    549 
    550            // getExemplarLocationName should return non-empty string
    551            // if the time zone is associated with a region
    552 
    553            UnicodeString city;
    554            fTimeZoneNames->getExemplarLocationName(tzCanonicalID, city);
    555            fRegionFormat.format(city, name, status);
    556        }
    557        if (U_FAILURE(status)) {
    558            return nullptr;
    559        }
    560    }
    561 
    562    locname = name.isEmpty() ? nullptr : fStringPool.get(name, status);
    563    if (U_SUCCESS(status)) {
    564        // Cache the result
    565        const char16_t* cacheID = ZoneMeta::findTimeZoneID(tzCanonicalID);
    566        U_ASSERT(cacheID != nullptr);
    567        if (locname == nullptr) {
    568            // gEmpty to indicate - no location name available
    569            uhash_put(fLocationNamesMap, (void *)cacheID, (void *)gEmpty, &status);
    570        } else {
    571            uhash_put(fLocationNamesMap, (void *)cacheID, (void *)locname, &status);
    572            if (U_FAILURE(status)) {
    573                locname = nullptr;
    574            } else {
    575                // put the name info into the trie
    576                GNameInfo* nameinfo = static_cast<ZNameInfo*>(uprv_malloc(sizeof(GNameInfo)));
    577                if (nameinfo != nullptr) {
    578                    nameinfo->type = UTZGNM_LOCATION;
    579                    nameinfo->tzID = cacheID;
    580                    fGNamesTrie.put(locname, nameinfo, status);
    581                }
    582            }
    583        }
    584    }
    585 
    586    return locname;
    587 }
    588 
    589 UnicodeString&
    590 TZGNCore::formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const {
    591    U_ASSERT(type == UTZGNM_LONG || type == UTZGNM_SHORT);
    592    name.setToBogus();
    593 
    594    const char16_t* uID = ZoneMeta::getCanonicalCLDRID(tz);
    595    if (uID == nullptr) {
    596        return name;
    597    }
    598 
    599    UnicodeString tzID(true, uID, -1);
    600 
    601    // Try to get a name from time zone first
    602    UTimeZoneNameType nameType = (type == UTZGNM_LONG) ? UTZNM_LONG_GENERIC : UTZNM_SHORT_GENERIC;
    603    fTimeZoneNames->getTimeZoneDisplayName(tzID, nameType, name);
    604 
    605    if (!name.isEmpty()) {
    606        return name;
    607    }
    608 
    609    // Try meta zone
    610    char16_t mzIDBuf[32];
    611    UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf));
    612    fTimeZoneNames->getMetaZoneID(tzID, date, mzID);
    613    if (!mzID.isEmpty()) {
    614        UErrorCode status = U_ZERO_ERROR;
    615        UBool useStandard = false;
    616        int32_t raw, sav;
    617        char16_t tmpNameBuf[ZONE_NAME_U16_MAX];
    618 
    619        tz.getOffset(date, false, raw, sav, status);
    620        if (U_FAILURE(status)) {
    621            return name;
    622        }
    623 
    624        if (sav == 0) {
    625            useStandard = true;
    626 
    627            TimeZone *tmptz = tz.clone();
    628            // Check if the zone actually uses daylight saving time around the time
    629            BasicTimeZone *btz = nullptr;
    630            if (dynamic_cast<OlsonTimeZone *>(tmptz) != nullptr
    631                || dynamic_cast<SimpleTimeZone *>(tmptz) != nullptr
    632                || dynamic_cast<RuleBasedTimeZone *>(tmptz) != nullptr
    633                || dynamic_cast<VTimeZone *>(tmptz) != nullptr) {
    634                btz = (BasicTimeZone*)tmptz;
    635            }
    636 
    637            if (btz != nullptr) {
    638                TimeZoneTransition before;
    639                UBool beforTrs = btz->getPreviousTransition(date, true, before);
    640                if (beforTrs
    641                        && (date - before.getTime() < kDstCheckRange)
    642                        && before.getFrom()->getDSTSavings() != 0) {
    643                    useStandard = false;
    644                } else {
    645                    TimeZoneTransition after;
    646                    UBool afterTrs = btz->getNextTransition(date, false, after);
    647                    if (afterTrs
    648                            && (after.getTime() - date < kDstCheckRange)
    649                            && after.getTo()->getDSTSavings() != 0) {
    650                        useStandard = false;
    651                    }
    652                }
    653            } else {
    654                // If not BasicTimeZone... only if the instance is not an ICU's implementation.
    655                // We may get a wrong answer in edge case, but it should practically work OK.
    656                tmptz->getOffset(date - kDstCheckRange, false, raw, sav, status);
    657                if (sav != 0) {
    658                    useStandard = false;
    659                } else {
    660                    tmptz->getOffset(date + kDstCheckRange, false, raw, sav, status);
    661                    if (sav != 0){
    662                        useStandard = false;
    663                    }
    664                }
    665                if (U_FAILURE(status)) {
    666                    delete tmptz;
    667                    return name;
    668                }
    669            }
    670            delete tmptz;
    671        }
    672        if (useStandard) {
    673            UTimeZoneNameType stdNameType = (nameType == UTZNM_LONG_GENERIC)
    674                ? UTZNM_LONG_STANDARD : UTZNM_SHORT_STANDARD;
    675            UnicodeString stdName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf));
    676            fTimeZoneNames->getDisplayName(tzID, stdNameType, date, stdName);
    677            if (!stdName.isEmpty()) {
    678                name.setTo(stdName);
    679 
    680                // TODO: revisit this issue later
    681                // In CLDR, a same display name is used for both generic and standard
    682                // for some meta zones in some locales.  This looks like a data bugs.
    683                // For now, we check if the standard name is different from its generic
    684                // name below.
    685                char16_t genNameBuf[ZONE_NAME_U16_MAX];
    686                UnicodeString mzGenericName(genNameBuf, 0, UPRV_LENGTHOF(genNameBuf));
    687                fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzGenericName);
    688                if (stdName.caseCompare(mzGenericName, 0) == 0) {
    689                    name.setToBogus();
    690                }
    691            }
    692        }
    693        if (name.isEmpty()) {
    694            // Get a name from meta zone
    695            UnicodeString mzName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf));
    696            fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzName);
    697            if (!mzName.isEmpty()) {
    698                // Check if we need to use a partial location format.
    699                // This check is done by comparing offset with the meta zone's
    700                // golden zone at the given date.
    701                char16_t idBuf[32];
    702                UnicodeString goldenID(idBuf, 0, UPRV_LENGTHOF(idBuf));
    703                fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion.data(), goldenID);
    704                if (!goldenID.isEmpty() && goldenID != tzID) {
    705                    TimeZone *goldenZone = TimeZone::createTimeZone(goldenID);
    706                    int32_t raw1, sav1;
    707 
    708                    // Check offset in the golden zone with wall time.
    709                    // With getOffset(date, false, offsets1),
    710                    // you may get incorrect results because of time overlap at DST->STD
    711                    // transition.
    712                    goldenZone->getOffset(date + raw + sav, true, raw1, sav1, status);
    713                    delete goldenZone;
    714                    if (U_SUCCESS(status)) {
    715                        if (raw != raw1 || sav != sav1) {
    716                            // Now we need to use a partial location format
    717                            getPartialLocationName(tzID, mzID, (nameType == UTZNM_LONG_GENERIC), mzName, name);
    718                        } else {
    719                            name.setTo(mzName);
    720                        }
    721                    }
    722                } else {
    723                    name.setTo(mzName);
    724                }
    725            }
    726        }
    727    }
    728    return name;
    729 }
    730 
    731 UnicodeString&
    732 TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID,
    733                        const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName,
    734                        UnicodeString& name) const {
    735    name.setToBogus();
    736    if (tzCanonicalID.isEmpty() || mzID.isEmpty() || mzDisplayName.isEmpty()) {
    737        return name;
    738    }
    739 
    740    const char16_t *uplname = nullptr;
    741    TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
    742    umtx_lock(&gLock);
    743    {
    744        uplname = nonConstThis->getPartialLocationName(tzCanonicalID, mzID, isLong, mzDisplayName);
    745    }
    746    umtx_unlock(&gLock);
    747 
    748    if (uplname == nullptr) {
    749        name.setToBogus();
    750    } else {
    751        name.setTo(true, uplname, -1);
    752    }
    753    return name;
    754 }
    755 
    756 /*
    757 * This method updates the cache and must be called with a lock
    758 */
    759 const char16_t*
    760 TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID,
    761                        const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName) {
    762    U_ASSERT(!tzCanonicalID.isEmpty());
    763    U_ASSERT(!mzID.isEmpty());
    764    U_ASSERT(!mzDisplayName.isEmpty());
    765 
    766    PartialLocationKey key;
    767    key.tzID = ZoneMeta::findTimeZoneID(tzCanonicalID);
    768    key.mzID = ZoneMeta::findMetaZoneID(mzID);
    769    key.isLong = isLong;
    770    U_ASSERT(key.tzID != nullptr && key.mzID != nullptr);
    771 
    772    const char16_t* uplname = static_cast<const char16_t*>(uhash_get(fPartialLocationNamesMap, &key));
    773    if (uplname != nullptr) {
    774        return uplname;
    775    }
    776 
    777    UnicodeString location;
    778    UnicodeString usCountryCode;
    779    ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode);
    780    if (!usCountryCode.isEmpty()) {
    781        char countryCode[ULOC_COUNTRY_CAPACITY];
    782        U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY);
    783        int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV);
    784        countryCode[ccLen] = 0;
    785 
    786        UnicodeString regionalGolden;
    787        fTimeZoneNames->getReferenceZoneID(mzID, countryCode, regionalGolden);
    788        if (tzCanonicalID == regionalGolden) {
    789            // Use country name
    790            fLocaleDisplayNames->regionDisplayName(countryCode, location);
    791        } else {
    792            // Otherwise, use exemplar city name
    793            fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);
    794        }
    795    } else {
    796        fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);
    797        if (location.isEmpty()) {
    798            // This could happen when the time zone is not associated with a country,
    799            // and its ID is not hierarchical, for example, CST6CDT.
    800            // We use the canonical ID itself as the location for this case.
    801            location.setTo(tzCanonicalID);
    802        }
    803    }
    804 
    805    UErrorCode status = U_ZERO_ERROR;
    806    UnicodeString name;
    807    fFallbackFormat.format(location, mzDisplayName, name, status);
    808    if (U_FAILURE(status)) {
    809        return nullptr;
    810    }
    811 
    812    uplname = fStringPool.get(name, status);
    813    if (U_SUCCESS(status)) {
    814        // Add the name to cache
    815        PartialLocationKey* cacheKey = static_cast<PartialLocationKey*>(uprv_malloc(sizeof(PartialLocationKey)));
    816        if (cacheKey != nullptr) {
    817            cacheKey->tzID = key.tzID;
    818            cacheKey->mzID = key.mzID;
    819            cacheKey->isLong = key.isLong;
    820            uhash_put(fPartialLocationNamesMap, (void *)cacheKey, (void *)uplname, &status);
    821            if (U_FAILURE(status)) {
    822                uprv_free(cacheKey);
    823            } else {
    824                // put the name to the local trie as well
    825                GNameInfo* nameinfo = static_cast<ZNameInfo*>(uprv_malloc(sizeof(GNameInfo)));
    826                if (nameinfo != nullptr) {
    827                    nameinfo->type = isLong ? UTZGNM_LONG : UTZGNM_SHORT;
    828                    nameinfo->tzID = key.tzID;
    829                    fGNamesTrie.put(uplname, nameinfo, status);
    830                }
    831            }
    832        }
    833    }
    834    return uplname;
    835 }
    836 
    837 /*
    838 * This method updates the cache and must be called with a lock,
    839 * except initializer.
    840 */
    841 void
    842 TZGNCore::loadStrings(const UnicodeString& tzCanonicalID) {
    843    // load the generic location name
    844    getGenericLocationName(tzCanonicalID);
    845 
    846    // partial location names
    847    UErrorCode status = U_ZERO_ERROR;
    848 
    849    const UnicodeString *mzID;
    850    UnicodeString goldenID;
    851    UnicodeString mzGenName;
    852    UTimeZoneNameType genNonLocTypes[] = {
    853        UTZNM_LONG_GENERIC, UTZNM_SHORT_GENERIC,
    854        UTZNM_UNKNOWN /*terminator*/
    855    };
    856 
    857    StringEnumeration *mzIDs = fTimeZoneNames->getAvailableMetaZoneIDs(tzCanonicalID, status);
    858    while ((mzID = mzIDs->snext(status)) != nullptr) {
    859        if (U_FAILURE(status)) {
    860            break;
    861        }
    862        // if this time zone is not the golden zone of the meta zone,
    863        // partial location name (such as "PT (Los Angeles)") might be
    864        // available.
    865        fTimeZoneNames->getReferenceZoneID(*mzID, fTargetRegion.data(), goldenID);
    866        if (tzCanonicalID != goldenID) {
    867            for (int32_t i = 0; genNonLocTypes[i] != UTZNM_UNKNOWN; i++) {
    868                fTimeZoneNames->getMetaZoneDisplayName(*mzID, genNonLocTypes[i], mzGenName);
    869                if (!mzGenName.isEmpty()) {
    870                    // getPartialLocationName formats a name and put it into the trie
    871                    getPartialLocationName(tzCanonicalID, *mzID,
    872                        (genNonLocTypes[i] == UTZNM_LONG_GENERIC), mzGenName);
    873                }
    874            }
    875        }
    876    }
    877    delete mzIDs;
    878 }
    879 
    880 int32_t
    881 TZGNCore::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
    882        UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const {
    883    timeType = UTZFMT_TIME_TYPE_UNKNOWN;
    884    tzID.setToBogus();
    885 
    886    if (U_FAILURE(status)) {
    887        return 0;
    888    }
    889 
    890    // Find matches in the TimeZoneNames first
    891    TimeZoneNames::MatchInfoCollection *tznamesMatches = findTimeZoneNames(text, start, types, status);
    892    if (U_FAILURE(status)) {
    893        return 0;
    894    }
    895 
    896    int32_t bestMatchLen = 0;
    897    UTimeZoneFormatTimeType bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
    898    UnicodeString bestMatchTzID;
    899    // UBool isLongStandard = false;   // workaround - see the comments below
    900    UBool isStandard = false;       // TODO: Temporary hack (on hack) for short standard name/location name conflict (found in zh_Hant), should be removed after CLDR 21m1 integration
    901 
    902    if (tznamesMatches != nullptr) {
    903        UnicodeString mzID;
    904        for (int32_t i = 0; i < tznamesMatches->size(); i++) {
    905            int32_t len = tznamesMatches->getMatchLengthAt(i);
    906            if (len > bestMatchLen) {
    907                bestMatchLen = len;
    908                if (!tznamesMatches->getTimeZoneIDAt(i, bestMatchTzID)) {
    909                    // name for a meta zone
    910                    if (tznamesMatches->getMetaZoneIDAt(i, mzID)) {
    911                        fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion.data(), bestMatchTzID);
    912                    }
    913                }
    914                UTimeZoneNameType nameType = tznamesMatches->getNameTypeAt(i);
    915                if (U_FAILURE(status)) {
    916                    break;
    917                }
    918                switch (nameType) {
    919                case UTZNM_LONG_STANDARD:
    920                    // isLongStandard = true;
    921                case UTZNM_SHORT_STANDARD:  // this one is never used for generic, but just in case
    922                    isStandard = true;      // TODO: Remove this later, see the comments above.
    923                    bestMatchTimeType = UTZFMT_TIME_TYPE_STANDARD;
    924                    break;
    925                case UTZNM_LONG_DAYLIGHT:
    926                case UTZNM_SHORT_DAYLIGHT: // this one is never used for generic, but just in case
    927                    bestMatchTimeType = UTZFMT_TIME_TYPE_DAYLIGHT;
    928                    break;
    929                default:
    930                    bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
    931                }
    932            }
    933        }
    934        delete tznamesMatches;
    935        if (U_FAILURE(status)) {
    936            return 0;
    937        }
    938 
    939        if (bestMatchLen == (text.length() - start)) {
    940            // Full match
    941 
    942            //tzID.setTo(bestMatchTzID);
    943            //timeType = bestMatchTimeType;
    944            //return bestMatchLen;
    945 
    946            // TODO Some time zone uses a same name for the long standard name
    947            // and the location name. When the match is a long standard name,
    948            // then we need to check if the name is same with the location name.
    949            // This is probably a data error or a design bug.
    950 /*
    951            if (!isLongStandard) {
    952                tzID.setTo(bestMatchTzID);
    953                timeType = bestMatchTimeType;
    954                return bestMatchLen;
    955            }
    956 */
    957            // TODO The deprecation of commonlyUsed flag introduced the name
    958            // conflict not only for long standard names, but short standard names too.
    959            // These short names (found in zh_Hant) should be gone once we clean
    960            // up CLDR time zone display name data. Once the short name conflict
    961            // problem (with location name) is resolved, we should change the condition
    962            // below back to the original one above. -Yoshito (2011-09-14)
    963            if (!isStandard) {
    964                tzID.setTo(bestMatchTzID);
    965                timeType = bestMatchTimeType;
    966                return bestMatchLen;
    967            }
    968        }
    969    }
    970 
    971    // Find matches in the local trie
    972    TimeZoneGenericNameMatchInfo *localMatches = findLocal(text, start, types, status);
    973    if (U_FAILURE(status)) {
    974        return 0;
    975    }
    976    if (localMatches != nullptr) {
    977        for (int32_t i = 0; i < localMatches->size(); i++) {
    978            int32_t len = localMatches->getMatchLength(i);
    979 
    980            // TODO See the above TODO. We use len >= bestMatchLen
    981            // because of the long standard/location name collision
    982            // problem. If it is also a location name, carrying
    983            // timeType = UTZFMT_TIME_TYPE_STANDARD will cause a
    984            // problem in SimpleDateFormat
    985            if (len >= bestMatchLen) {
    986                bestMatchLen = localMatches->getMatchLength(i);
    987                bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;   // because generic
    988                localMatches->getTimeZoneID(i, bestMatchTzID);
    989            }
    990        }
    991        delete localMatches;
    992    }
    993 
    994    if (bestMatchLen > 0) {
    995        timeType = bestMatchTimeType;
    996        tzID.setTo(bestMatchTzID);
    997    }
    998    return bestMatchLen;
    999 }
   1000 
   1001 TimeZoneGenericNameMatchInfo*
   1002 TZGNCore::findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
   1003    GNameSearchHandler handler(types);
   1004 
   1005    TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
   1006 
   1007    umtx_lock(&gLock);
   1008    {
   1009        fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
   1010    }
   1011    umtx_unlock(&gLock);
   1012 
   1013    if (U_FAILURE(status)) {
   1014        return nullptr;
   1015    }
   1016 
   1017    TimeZoneGenericNameMatchInfo *gmatchInfo = nullptr;
   1018 
   1019    int32_t maxLen = 0;
   1020    UVector *results = handler.getMatches(maxLen);
   1021    if (results != nullptr && ((maxLen == (text.length() - start)) || fGNamesTrieFullyLoaded)) {
   1022        // perfect match
   1023        gmatchInfo = new TimeZoneGenericNameMatchInfo(results);
   1024        if (gmatchInfo == nullptr) {
   1025            status = U_MEMORY_ALLOCATION_ERROR;
   1026            delete results;
   1027            return nullptr;
   1028        }
   1029        return gmatchInfo;
   1030    }
   1031 
   1032    delete results;
   1033 
   1034    // All names are not yet loaded into the local trie.
   1035    // Load all available names into the trie. This could be very heavy.
   1036    umtx_lock(&gLock);
   1037    {
   1038        if (!fGNamesTrieFullyLoaded) {
   1039            StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, nullptr, nullptr, status);
   1040            if (U_SUCCESS(status)) {
   1041                const UnicodeString *tzID;
   1042                while ((tzID = tzIDs->snext(status)) != nullptr) {
   1043                    if (U_FAILURE(status)) {
   1044                        break;
   1045                    }
   1046                    nonConstThis->loadStrings(*tzID);
   1047                }
   1048            }
   1049            delete tzIDs;
   1050 
   1051            if (U_SUCCESS(status)) {
   1052                nonConstThis->fGNamesTrieFullyLoaded = true;
   1053            }
   1054        }
   1055    }
   1056    umtx_unlock(&gLock);
   1057 
   1058    if (U_FAILURE(status)) {
   1059        return nullptr;
   1060    }
   1061 
   1062    umtx_lock(&gLock);
   1063    {
   1064        // now try it again
   1065        fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
   1066    }
   1067    umtx_unlock(&gLock);
   1068 
   1069    results = handler.getMatches(maxLen);
   1070    if (results != nullptr && maxLen > 0) {
   1071        gmatchInfo = new TimeZoneGenericNameMatchInfo(results);
   1072        if (gmatchInfo == nullptr) {
   1073            status = U_MEMORY_ALLOCATION_ERROR;
   1074            delete results;
   1075            return nullptr;
   1076        }
   1077    }
   1078 
   1079    return gmatchInfo;
   1080 }
   1081 
   1082 TimeZoneNames::MatchInfoCollection*
   1083 TZGNCore::findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
   1084    // Check if the target name typs is really in the TimeZoneNames
   1085    uint32_t nameTypes = 0;
   1086    if (types & UTZGNM_LONG) {
   1087        nameTypes |= (UTZNM_LONG_GENERIC | UTZNM_LONG_STANDARD);
   1088    }
   1089    if (types & UTZGNM_SHORT) {
   1090        nameTypes |= (UTZNM_SHORT_GENERIC | UTZNM_SHORT_STANDARD);
   1091    }
   1092 
   1093    if (types) {
   1094        // Find matches in the TimeZoneNames
   1095        return fTimeZoneNames->find(text, start, nameTypes, status);
   1096    }
   1097 
   1098    return nullptr;
   1099 }
   1100 
   1101 typedef struct TZGNCoreRef {
   1102    TZGNCore*       obj;
   1103    int32_t         refCount;
   1104    double          lastAccess;
   1105 } TZGNCoreRef;
   1106 
   1107 // TZGNCore object cache handling
   1108 static UMutex gTZGNLock;
   1109 static UHashtable *gTZGNCoreCache = nullptr;
   1110 static UBool gTZGNCoreCacheInitialized = false;
   1111 
   1112 // Access count - incremented every time up to SWEEP_INTERVAL,
   1113 // then reset to 0
   1114 static int32_t gAccessCount = 0;
   1115 
   1116 // Interval for calling the cache sweep function - every 100 times
   1117 #define SWEEP_INTERVAL 100
   1118 
   1119 // Cache expiration in millisecond. When a cached entry is no
   1120 // longer referenced and exceeding this threshold since last
   1121 // access time, then the cache entry will be deleted by the sweep
   1122 // function. For now, 3 minutes.
   1123 #define CACHE_EXPIRATION 180000.0
   1124 
   1125 U_CDECL_BEGIN
   1126 /**
   1127 * Cleanup callback func
   1128 */
   1129 static UBool U_CALLCONV tzgnCore_cleanup()
   1130 {
   1131    if (gTZGNCoreCache != nullptr) {
   1132        uhash_close(gTZGNCoreCache);
   1133        gTZGNCoreCache = nullptr;
   1134    }
   1135    gTZGNCoreCacheInitialized = false;
   1136    return true;
   1137 }
   1138 
   1139 /**
   1140 * Deleter for TZGNCoreRef
   1141 */
   1142 static void U_CALLCONV
   1143 deleteTZGNCoreRef(void *obj) {
   1144    icu::TZGNCoreRef *entry = (icu::TZGNCoreRef*)obj;
   1145    delete (icu::TZGNCore*) entry->obj;
   1146    uprv_free(entry);
   1147 }
   1148 U_CDECL_END
   1149 
   1150 /**
   1151 * Function used for removing unreferrenced cache entries exceeding
   1152 * the expiration time. This function must be called with in the mutex
   1153 * block.
   1154 */
   1155 static void sweepCache() {
   1156    int32_t pos = UHASH_FIRST;
   1157    const UHashElement* elem;
   1158    double now = static_cast<double>(uprv_getUTCtime());
   1159 
   1160    while ((elem = uhash_nextElement(gTZGNCoreCache, &pos)) != nullptr) {
   1161        TZGNCoreRef* entry = static_cast<TZGNCoreRef*>(elem->value.pointer);
   1162        if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) {
   1163            // delete this entry
   1164            uhash_removeElement(gTZGNCoreCache, elem);
   1165        }
   1166    }
   1167 }
   1168 
   1169 TimeZoneGenericNames::TimeZoneGenericNames()
   1170 : fRef(nullptr) {
   1171 }
   1172 
   1173 TimeZoneGenericNames::~TimeZoneGenericNames() {
   1174    umtx_lock(&gTZGNLock);
   1175    {
   1176        U_ASSERT(fRef->refCount > 0);
   1177        // Just decrement the reference count
   1178        fRef->refCount--;
   1179    }
   1180    umtx_unlock(&gTZGNLock);
   1181 }
   1182 
   1183 TimeZoneGenericNames*
   1184 TimeZoneGenericNames::createInstance(const Locale& locale, UErrorCode& status) {
   1185    if (U_FAILURE(status)) {
   1186        return nullptr;
   1187    }
   1188    LocalPointer<TimeZoneGenericNames> instance(new TimeZoneGenericNames(), status);
   1189    if (U_FAILURE(status)) {
   1190        return nullptr;
   1191    }
   1192 
   1193    TZGNCoreRef *cacheEntry = nullptr;
   1194    {
   1195        Mutex lock(&gTZGNLock);
   1196 
   1197        if (!gTZGNCoreCacheInitialized) {
   1198            // Create empty hashtable
   1199            gTZGNCoreCache = uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &status);
   1200            if (U_SUCCESS(status)) {
   1201                uhash_setKeyDeleter(gTZGNCoreCache, uprv_free);
   1202                uhash_setValueDeleter(gTZGNCoreCache, deleteTZGNCoreRef);
   1203                gTZGNCoreCacheInitialized = true;
   1204                ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEGENERICNAMES, tzgnCore_cleanup);
   1205            }
   1206        }
   1207        if (U_FAILURE(status)) {
   1208            return nullptr;
   1209        }
   1210 
   1211        // Check the cache, if not available, create new one and cache
   1212        const char *key = locale.getName();
   1213        cacheEntry = static_cast<TZGNCoreRef*>(uhash_get(gTZGNCoreCache, key));
   1214        if (cacheEntry == nullptr) {
   1215            TZGNCore *tzgnCore = nullptr;
   1216            char *newKey = nullptr;
   1217 
   1218            tzgnCore = new TZGNCore(locale, status);
   1219            if (tzgnCore == nullptr) {
   1220                status = U_MEMORY_ALLOCATION_ERROR;
   1221            }
   1222            if (U_SUCCESS(status)) {
   1223                newKey = static_cast<char*>(uprv_malloc(uprv_strlen(key) + 1));
   1224                if (newKey == nullptr) {
   1225                    status = U_MEMORY_ALLOCATION_ERROR;
   1226                } else {
   1227                    uprv_strcpy(newKey, key);
   1228                }
   1229            }
   1230            if (U_SUCCESS(status)) {
   1231                cacheEntry = static_cast<TZGNCoreRef*>(uprv_malloc(sizeof(TZGNCoreRef)));
   1232                if (cacheEntry == nullptr) {
   1233                    status = U_MEMORY_ALLOCATION_ERROR;
   1234                } else {
   1235                    cacheEntry->obj = tzgnCore;
   1236                    cacheEntry->refCount = 1;
   1237                    cacheEntry->lastAccess = static_cast<double>(uprv_getUTCtime());
   1238 
   1239                    uhash_put(gTZGNCoreCache, newKey, cacheEntry, &status);
   1240                }
   1241            }
   1242            if (U_FAILURE(status)) {
   1243                delete tzgnCore;
   1244                if (newKey != nullptr) {
   1245                    uprv_free(newKey);
   1246                }
   1247                if (cacheEntry != nullptr) {
   1248                    uprv_free(cacheEntry);
   1249                }
   1250                cacheEntry = nullptr;
   1251            }
   1252        } else {
   1253            // Update the reference count
   1254            cacheEntry->refCount++;
   1255            cacheEntry->lastAccess = static_cast<double>(uprv_getUTCtime());
   1256        }
   1257        gAccessCount++;
   1258        if (gAccessCount >= SWEEP_INTERVAL) {
   1259            // sweep
   1260            sweepCache();
   1261            gAccessCount = 0;
   1262        }
   1263    }  // End of mutex locked block
   1264 
   1265    if (cacheEntry == nullptr) {
   1266        return nullptr;
   1267    }
   1268 
   1269    instance->fRef = cacheEntry;
   1270    return instance.orphan();
   1271 }
   1272 
   1273 bool
   1274 TimeZoneGenericNames::operator==(const TimeZoneGenericNames& other) const {
   1275    // Just compare if the other object also use the same
   1276    // ref entry
   1277    return fRef == other.fRef;
   1278 }
   1279 
   1280 TimeZoneGenericNames*
   1281 TimeZoneGenericNames::clone() const {
   1282    TimeZoneGenericNames* other = new TimeZoneGenericNames();
   1283    if (other) {
   1284        umtx_lock(&gTZGNLock);
   1285        {
   1286            // Just increments the reference count
   1287            fRef->refCount++;
   1288            other->fRef = fRef;
   1289        }
   1290        umtx_unlock(&gTZGNLock);
   1291    }
   1292    return other;
   1293 }
   1294 
   1295 UnicodeString&
   1296 TimeZoneGenericNames::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,
   1297                        UDate date, UnicodeString& name) const {
   1298    return fRef->obj->getDisplayName(tz, type, date, name);
   1299 }
   1300 
   1301 UnicodeString&
   1302 TimeZoneGenericNames::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
   1303    return fRef->obj->getGenericLocationName(tzCanonicalID, name);
   1304 }
   1305 
   1306 int32_t
   1307 TimeZoneGenericNames::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
   1308        UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const {
   1309    return fRef->obj->findBestMatch(text, start, types, tzID, timeType, status);
   1310 }
   1311 
   1312 U_NAMESPACE_END
   1313 #endif