Home | History | Annotate | Download | only in i18n
      1 // Copyright (C) 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/vtzone.h"
     22 
     23 #include "cmemory.h"
     24 #include "cstring.h"
     25 #include "mutex.h"
     26 #include "uhash.h"
     27 #include "uassert.h"
     28 #include "umutex.h"
     29 #include "uresimp.h"
     30 #include "ureslocs.h"
     31 #include "zonemeta.h"
     32 #include "tznames_impl.h"
     33 #include "olsontz.h"
     34 #include "ucln_in.h"
     35 
     36 U_NAMESPACE_BEGIN
     37 
     38 #define ZID_KEY_MAX  128
     39 
     40 static const char gZoneStrings[]                = "zoneStrings";
     41 
     42 static const char gRegionFormatTag[]            = "regionFormat";
     43 static const char gFallbackFormatTag[]          = "fallbackFormat";
     44 
     45 static const UChar gEmpty[]                     = {0x00};
     46 
     47 static const UChar gDefRegionPattern[]          = {0x7B, 0x30, 0x7D, 0x00}; // "{0}"
     48 static const UChar gDefFallbackPattern[]        = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})"
     49 
     50 static const double kDstCheckRange      = (double)184*U_MILLIS_PER_DAY;
     51 
     52 
     53 
     54 U_CDECL_BEGIN
     55 
     56 typedef struct PartialLocationKey {
     57     const UChar* tzID;
     58     const UChar* mzID;
     59     UBool isLong;
     60 } PartialLocationKey;
     61 
     62 /**
     63  * Hash function for partial location name hash key
     64  */
     65 static int32_t U_CALLCONV
     66 hashPartialLocationKey(const UHashTok key) {
     67     // <tzID>&<mzID>#[L|S]
     68     PartialLocationKey *p = (PartialLocationKey *)key.pointer;
     69     UnicodeString str(p->tzID);
     70     str.append((UChar)0x26)
     71         .append(p->mzID, -1)
     72         .append((UChar)0x23)
     73         .append((UChar)(p->isLong ? 0x4C : 0x53));
     74     return str.hashCode();
     75 }
     76 
     77 /**
     78  * Comparer for partial location name hash key
     79  */
     80 static UBool U_CALLCONV
     81 comparePartialLocationKey(const UHashTok key1, const UHashTok key2) {
     82     PartialLocationKey *p1 = (PartialLocationKey *)key1.pointer;
     83     PartialLocationKey *p2 = (PartialLocationKey *)key2.pointer;
     84 
     85     if (p1 == p2) {
     86         return TRUE;
     87     }
     88     if (p1 == NULL || p2 == NULL) {
     89         return FALSE;
     90     }
     91     // We just check identity of tzID/mzID
     92     return (p1->tzID == p2->tzID && p1->mzID == p2->mzID && p1->isLong == p2->isLong);
     93 }
     94 
     95 /**
     96  * Deleter for GNameInfo
     97  */
     98 static void U_CALLCONV
     99 deleteGNameInfo(void *obj) {
    100     uprv_free(obj);
    101 }
    102 
    103 /**
    104  * GNameInfo stores zone name information in the local trie
    105  */
    106 typedef struct GNameInfo {
    107     UTimeZoneGenericNameType    type;
    108     const UChar*                tzID;
    109 } ZNameInfo;
    110 
    111 /**
    112  * GMatchInfo stores zone name match information used by find method
    113  */
    114 typedef struct GMatchInfo {
    115     const GNameInfo*    gnameInfo;
    116     int32_t             matchLength;
    117     UTimeZoneFormatTimeType   timeType;
    118 } ZMatchInfo;
    119 
    120 U_CDECL_END
    121 
    122 // ---------------------------------------------------
    123 // The class stores time zone generic name match information
    124 // ---------------------------------------------------
    125 class TimeZoneGenericNameMatchInfo : public UMemory {
    126 public:
    127     TimeZoneGenericNameMatchInfo(UVector* matches);
    128     ~TimeZoneGenericNameMatchInfo();
    129 
    130     int32_t size() const;
    131     UTimeZoneGenericNameType getGenericNameType(int32_t index) const;
    132     int32_t getMatchLength(int32_t index) const;
    133     UnicodeString& getTimeZoneID(int32_t index, UnicodeString& tzID) const;
    134 
    135 private:
    136     UVector* fMatches;  // vector of MatchEntry
    137 };
    138 
    139 TimeZoneGenericNameMatchInfo::TimeZoneGenericNameMatchInfo(UVector* matches)
    140 : fMatches(matches) {
    141 }
    142 
    143 TimeZoneGenericNameMatchInfo::~TimeZoneGenericNameMatchInfo() {
    144     if (fMatches != NULL) {
    145         delete fMatches;
    146     }
    147 }
    148 
    149 int32_t
    150 TimeZoneGenericNameMatchInfo::size() const {
    151     if (fMatches == NULL) {
    152         return 0;
    153     }
    154     return fMatches->size();
    155 }
    156 
    157 UTimeZoneGenericNameType
    158 TimeZoneGenericNameMatchInfo::getGenericNameType(int32_t index) const {
    159     GMatchInfo *minfo = (GMatchInfo *)fMatches->elementAt(index);
    160     if (minfo != NULL) {
    161         return static_cast<UTimeZoneGenericNameType>(minfo->gnameInfo->type);
    162     }
    163     return UTZGNM_UNKNOWN;
    164 }
    165 
    166 int32_t
    167 TimeZoneGenericNameMatchInfo::getMatchLength(int32_t index) const {
    168     ZMatchInfo *minfo = (ZMatchInfo *)fMatches->elementAt(index);
    169     if (minfo != NULL) {
    170         return minfo->matchLength;
    171     }
    172     return -1;
    173 }
    174 
    175 UnicodeString&
    176 TimeZoneGenericNameMatchInfo::getTimeZoneID(int32_t index, UnicodeString& tzID) const {
    177     GMatchInfo *minfo = (GMatchInfo *)fMatches->elementAt(index);
    178     if (minfo != NULL && minfo->gnameInfo->tzID != NULL) {
    179         tzID.setTo(TRUE, minfo->gnameInfo->tzID, -1);
    180     } else {
    181         tzID.setToBogus();
    182     }
    183     return tzID;
    184 }
    185 
    186 // ---------------------------------------------------
    187 // GNameSearchHandler
    188 // ---------------------------------------------------
    189 class GNameSearchHandler : public TextTrieMapSearchResultHandler {
    190 public:
    191     GNameSearchHandler(uint32_t types);
    192     virtual ~GNameSearchHandler();
    193 
    194     UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status);
    195     UVector* getMatches(int32_t& maxMatchLen);
    196 
    197 private:
    198     uint32_t fTypes;
    199     UVector* fResults;
    200     int32_t fMaxMatchLen;
    201 };
    202 
    203 GNameSearchHandler::GNameSearchHandler(uint32_t types)
    204 : fTypes(types), fResults(NULL), fMaxMatchLen(0) {
    205 }
    206 
    207 GNameSearchHandler::~GNameSearchHandler() {
    208     if (fResults != NULL) {
    209         delete fResults;
    210     }
    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 == NULL) {
    223                 break;
    224             }
    225             if ((nameinfo->type & fTypes) != 0) {
    226                 // matches a requested type
    227                 if (fResults == NULL) {
    228                     fResults = new UVector(uprv_free, NULL, status);
    229                     if (fResults == NULL) {
    230                         status = U_MEMORY_ALLOCATION_ERROR;
    231                     }
    232                 }
    233                 if (U_SUCCESS(status)) {
    234                     U_ASSERT(fResults != NULL);
    235                     GMatchInfo *gmatch = (GMatchInfo *)uprv_malloc(sizeof(GMatchInfo));
    236                     if (gmatch == NULL) {
    237                         status = U_MEMORY_ALLOCATION_ERROR;
    238                     } else {
    239                         // add the match to the vector
    240                         gmatch->gnameInfo = nameinfo;
    241                         gmatch->matchLength = matchLength;
    242                         gmatch->timeType = UTZFMT_TIME_TYPE_UNKNOWN;
    243                         fResults->addElement(gmatch, status);
    244                         if (U_FAILURE(status)) {
    245                             uprv_free(gmatch);
    246                         } else {
    247                             if (matchLength > fMaxMatchLen) {
    248                                 fMaxMatchLen = matchLength;
    249                             }
    250                         }
    251                     }
    252                 }
    253             }
    254         }
    255     }
    256     return TRUE;
    257 }
    258 
    259 UVector*
    260 GNameSearchHandler::getMatches(int32_t& maxMatchLen) {
    261     // give the ownership to the caller
    262     UVector *results = fResults;
    263     maxMatchLen = fMaxMatchLen;
    264 
    265     // reset
    266     fResults = NULL;
    267     fMaxMatchLen = 0;
    268     return results;
    269 }
    270 
    271 static UMutex gLock = U_MUTEX_INITIALIZER;
    272 
    273 class TZGNCore : public UMemory {
    274 public:
    275     TZGNCore(const Locale& locale, UErrorCode& status);
    276     virtual ~TZGNCore();
    277 
    278     UnicodeString& getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,
    279                         UDate date, UnicodeString& name) const;
    280 
    281     UnicodeString& getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const;
    282 
    283     int32_t findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
    284         UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const;
    285 
    286 private:
    287     Locale fLocale;
    288     const TimeZoneNames* fTimeZoneNames;
    289     UHashtable* fLocationNamesMap;
    290     UHashtable* fPartialLocationNamesMap;
    291 
    292     SimpleFormatter fRegionFormat;
    293     SimpleFormatter fFallbackFormat;
    294 
    295     LocaleDisplayNames* fLocaleDisplayNames;
    296     ZNStringPool fStringPool;
    297 
    298     TextTrieMap fGNamesTrie;
    299     UBool fGNamesTrieFullyLoaded;
    300 
    301     char fTargetRegion[ULOC_COUNTRY_CAPACITY];
    302 
    303     void initialize(const Locale& locale, UErrorCode& status);
    304     void cleanup();
    305 
    306     void loadStrings(const UnicodeString& tzCanonicalID);
    307 
    308     const UChar* getGenericLocationName(const UnicodeString& tzCanonicalID);
    309 
    310     UnicodeString& formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type,
    311                         UDate date, UnicodeString& name) const;
    312 
    313     UnicodeString& getPartialLocationName(const UnicodeString& tzCanonicalID,
    314                         const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName,
    315                         UnicodeString& name) const;
    316 
    317     const UChar* getPartialLocationName(const UnicodeString& tzCanonicalID,
    318                         const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName);
    319 
    320     TimeZoneGenericNameMatchInfo* findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
    321 
    322     TimeZoneNames::MatchInfoCollection* findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
    323 };
    324 
    325 
    326 // ---------------------------------------------------
    327 // TZGNCore - core implmentation of TimeZoneGenericNames
    328 //
    329 // TimeZoneGenericNames is parallel to TimeZoneNames,
    330 // but handles run-time generated time zone names.
    331 // This is the main part of this module.
    332 // ---------------------------------------------------
    333 TZGNCore::TZGNCore(const Locale& locale, UErrorCode& status)
    334 : fLocale(locale),
    335   fTimeZoneNames(NULL),
    336   fLocationNamesMap(NULL),
    337   fPartialLocationNamesMap(NULL),
    338   fLocaleDisplayNames(NULL),
    339   fStringPool(status),
    340   fGNamesTrie(TRUE, deleteGNameInfo),
    341   fGNamesTrieFullyLoaded(FALSE) {
    342     initialize(locale, status);
    343 }
    344 
    345 TZGNCore::~TZGNCore() {
    346     cleanup();
    347 }
    348 
    349 void
    350 TZGNCore::initialize(const Locale& locale, UErrorCode& status) {
    351     if (U_FAILURE(status)) {
    352         return;
    353     }
    354 
    355     // TimeZoneNames
    356     fTimeZoneNames = TimeZoneNames::createInstance(locale, status);
    357     if (U_FAILURE(status)) {
    358         return;
    359     }
    360 
    361     // Initialize format patterns
    362     UnicodeString rpat(TRUE, gDefRegionPattern, -1);
    363     UnicodeString fpat(TRUE, gDefFallbackPattern, -1);
    364 
    365     UErrorCode tmpsts = U_ZERO_ERROR;   // OK with fallback warning..
    366     UResourceBundle *zoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts);
    367     zoneStrings = ures_getByKeyWithFallback(zoneStrings, gZoneStrings, zoneStrings, &tmpsts);
    368 
    369     if (U_SUCCESS(tmpsts)) {
    370         const UChar *regionPattern = ures_getStringByKeyWithFallback(zoneStrings, gRegionFormatTag, NULL, &tmpsts);
    371         if (U_SUCCESS(tmpsts) && u_strlen(regionPattern) > 0) {
    372             rpat.setTo(regionPattern, -1);
    373         }
    374         tmpsts = U_ZERO_ERROR;
    375         const UChar *fallbackPattern = ures_getStringByKeyWithFallback(zoneStrings, gFallbackFormatTag, NULL, &tmpsts);
    376         if (U_SUCCESS(tmpsts) && u_strlen(fallbackPattern) > 0) {
    377             fpat.setTo(fallbackPattern, -1);
    378         }
    379     }
    380     ures_close(zoneStrings);
    381 
    382     fRegionFormat.applyPatternMinMaxArguments(rpat, 1, 1, status);
    383     fFallbackFormat.applyPatternMinMaxArguments(fpat, 2, 2, status);
    384     if (U_FAILURE(status)) {
    385         cleanup();
    386         return;
    387     }
    388 
    389     // locale display names
    390     fLocaleDisplayNames = LocaleDisplayNames::createInstance(locale);
    391 
    392     // hash table for names - no key/value deleters
    393     fLocationNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
    394     if (U_FAILURE(status)) {
    395         cleanup();
    396         return;
    397     }
    398 
    399     fPartialLocationNamesMap = uhash_open(hashPartialLocationKey, comparePartialLocationKey, NULL, &status);
    400     if (U_FAILURE(status)) {
    401         cleanup();
    402         return;
    403     }
    404     uhash_setKeyDeleter(fPartialLocationNamesMap, uprv_free);
    405     // no value deleter
    406 
    407     // target region
    408     const char* region = fLocale.getCountry();
    409     int32_t regionLen = uprv_strlen(region);
    410     if (regionLen == 0) {
    411         char loc[ULOC_FULLNAME_CAPACITY];
    412         uloc_addLikelySubtags(fLocale.getName(), loc, sizeof(loc), &status);
    413 
    414         regionLen = uloc_getCountry(loc, fTargetRegion, sizeof(fTargetRegion), &status);
    415         if (U_SUCCESS(status)) {
    416             fTargetRegion[regionLen] = 0;
    417         } else {
    418             cleanup();
    419             return;
    420         }
    421     } else if (regionLen < (int32_t)sizeof(fTargetRegion)) {
    422         uprv_strcpy(fTargetRegion, region);
    423     } else {
    424         fTargetRegion[0] = 0;
    425     }
    426 
    427     // preload generic names for the default zone
    428     TimeZone *tz = TimeZone::createDefault();
    429     const UChar *tzID = ZoneMeta::getCanonicalCLDRID(*tz);
    430     if (tzID != NULL) {
    431         loadStrings(UnicodeString(TRUE, tzID, -1));
    432     }
    433     delete tz;
    434 }
    435 
    436 void
    437 TZGNCore::cleanup() {
    438     if (fLocaleDisplayNames != NULL) {
    439         delete fLocaleDisplayNames;
    440     }
    441     if (fTimeZoneNames != NULL) {
    442         delete fTimeZoneNames;
    443     }
    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 UChar* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz);
    457             if (tzCanonicalID != NULL) {
    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 UChar* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz);
    467             if (tzCanonicalID != NULL) {
    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 UChar *locname = NULL;
    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 == NULL) {
    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 UChar*
    506 TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID) {
    507     U_ASSERT(!tzCanonicalID.isEmpty());
    508     if (tzCanonicalID.length() > ZID_KEY_MAX) {
    509         return NULL;
    510     }
    511 
    512     UErrorCode status = U_ZERO_ERROR;
    513     UChar 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 UChar *locname = (const UChar *)uhash_get(fLocationNamesMap, tzIDKey);
    519 
    520     if (locname != NULL) {
    521         // gEmpty indicate the name is not available
    522         if (locname == gEmpty) {
    523             return NULL;
    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 retur 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 NULL;
    559         }
    560     }
    561 
    562     locname = name.isEmpty() ? NULL : fStringPool.get(name, status);
    563     if (U_SUCCESS(status)) {
    564         // Cache the result
    565         const UChar* cacheID = ZoneMeta::findTimeZoneID(tzCanonicalID);
    566         U_ASSERT(cacheID != NULL);
    567         if (locname == NULL) {
    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 = NULL;
    574             } else {
    575                 // put the name info into the trie
    576                 GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo));
    577                 if (nameinfo != NULL) {
    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 UChar* uID = ZoneMeta::getCanonicalCLDRID(tz);
    595     if (uID == NULL) {
    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     UChar 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         UChar tmpNameBuf[64];
    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 = NULL;
    630             if (dynamic_cast<OlsonTimeZone *>(tmptz) != NULL
    631                 || dynamic_cast<SimpleTimeZone *>(tmptz) != NULL
    632                 || dynamic_cast<RuleBasedTimeZone *>(tmptz) != NULL
    633                 || dynamic_cast<VTimeZone *>(tmptz) != NULL) {
    634                 btz = (BasicTimeZone*)tmptz;
    635             }
    636 
    637             if (btz != NULL) {
    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                 UChar genNameBuf[64];
    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                 UChar idBuf[32];
    702                 UnicodeString goldenID(idBuf, 0, UPRV_LENGTHOF(idBuf));
    703                 fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, 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 UChar *uplname = NULL;
    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 == NULL) {
    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 UChar*
    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 != NULL && key.mzID != NULL);
    771 
    772     const UChar* uplname = (const UChar*)uhash_get(fPartialLocationNamesMap, (void *)&key);
    773     if (uplname != NULL) {
    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 NULL;
    810     }
    811 
    812     uplname = fStringPool.get(name, status);
    813     if (U_SUCCESS(status)) {
    814         // Add the name to cache
    815         PartialLocationKey* cacheKey = (PartialLocationKey *)uprv_malloc(sizeof(PartialLocationKey));
    816         if (cacheKey != NULL) {
    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 = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo));
    826                 if (nameinfo != NULL) {
    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))) {
    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, 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     if (mzIDs != NULL) {
    878         delete mzIDs;
    879     }
    880 }
    881 
    882 int32_t
    883 TZGNCore::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
    884         UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const {
    885     timeType = UTZFMT_TIME_TYPE_UNKNOWN;
    886     tzID.setToBogus();
    887 
    888     if (U_FAILURE(status)) {
    889         return 0;
    890     }
    891 
    892     // Find matches in the TimeZoneNames first
    893     TimeZoneNames::MatchInfoCollection *tznamesMatches = findTimeZoneNames(text, start, types, status);
    894     if (U_FAILURE(status)) {
    895         return 0;
    896     }
    897 
    898     int32_t bestMatchLen = 0;
    899     UTimeZoneFormatTimeType bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
    900     UnicodeString bestMatchTzID;
    901     // UBool isLongStandard = FALSE;   // workaround - see the comments below
    902     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
    903 
    904     if (tznamesMatches != NULL) {
    905         UnicodeString mzID;
    906         for (int32_t i = 0; i < tznamesMatches->size(); i++) {
    907             int32_t len = tznamesMatches->getMatchLengthAt(i);
    908             if (len > bestMatchLen) {
    909                 bestMatchLen = len;
    910                 if (!tznamesMatches->getTimeZoneIDAt(i, bestMatchTzID)) {
    911                     // name for a meta zone
    912                     if (tznamesMatches->getMetaZoneIDAt(i, mzID)) {
    913                         fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, bestMatchTzID);
    914                     }
    915                 }
    916                 UTimeZoneNameType nameType = tznamesMatches->getNameTypeAt(i);
    917                 if (U_FAILURE(status)) {
    918                     break;
    919                 }
    920                 switch (nameType) {
    921                 case UTZNM_LONG_STANDARD:
    922                     // isLongStandard = TRUE;
    923                 case UTZNM_SHORT_STANDARD:  // this one is never used for generic, but just in case
    924                     isStandard = TRUE;      // TODO: Remove this later, see the comments above.
    925                     bestMatchTimeType = UTZFMT_TIME_TYPE_STANDARD;
    926                     break;
    927                 case UTZNM_LONG_DAYLIGHT:
    928                 case UTZNM_SHORT_DAYLIGHT: // this one is never used for generic, but just in case
    929                     bestMatchTimeType = UTZFMT_TIME_TYPE_DAYLIGHT;
    930                     break;
    931                 default:
    932                     bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
    933                 }
    934             }
    935         }
    936         delete tznamesMatches;
    937         if (U_FAILURE(status)) {
    938             return 0;
    939         }
    940 
    941         if (bestMatchLen == (text.length() - start)) {
    942             // Full match
    943 
    944             //tzID.setTo(bestMatchTzID);
    945             //timeType = bestMatchTimeType;
    946             //return bestMatchLen;
    947 
    948             // TODO Some time zone uses a same name for the long standard name
    949             // and the location name. When the match is a long standard name,
    950             // then we need to check if the name is same with the location name.
    951             // This is probably a data error or a design bug.
    952 /*
    953             if (!isLongStandard) {
    954                 tzID.setTo(bestMatchTzID);
    955                 timeType = bestMatchTimeType;
    956                 return bestMatchLen;
    957             }
    958 */
    959             // TODO The deprecation of commonlyUsed flag introduced the name
    960             // conflict not only for long standard names, but short standard names too.
    961             // These short names (found in zh_Hant) should be gone once we clean
    962             // up CLDR time zone display name data. Once the short name conflict
    963             // problem (with location name) is resolved, we should change the condition
    964             // below back to the original one above. -Yoshito (2011-09-14)
    965             if (!isStandard) {
    966                 tzID.setTo(bestMatchTzID);
    967                 timeType = bestMatchTimeType;
    968                 return bestMatchLen;
    969             }
    970         }
    971     }
    972 
    973     // Find matches in the local trie
    974     TimeZoneGenericNameMatchInfo *localMatches = findLocal(text, start, types, status);
    975     if (U_FAILURE(status)) {
    976         return 0;
    977     }
    978     if (localMatches != NULL) {
    979         for (int32_t i = 0; i < localMatches->size(); i++) {
    980             int32_t len = localMatches->getMatchLength(i);
    981 
    982             // TODO See the above TODO. We use len >= bestMatchLen
    983             // because of the long standard/location name collision
    984             // problem. If it is also a location name, carrying
    985             // timeType = UTZFMT_TIME_TYPE_STANDARD will cause a
    986             // problem in SimpleDateFormat
    987             if (len >= bestMatchLen) {
    988                 bestMatchLen = localMatches->getMatchLength(i);
    989                 bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;   // because generic
    990                 localMatches->getTimeZoneID(i, bestMatchTzID);
    991             }
    992         }
    993         delete localMatches;
    994     }
    995 
    996     if (bestMatchLen > 0) {
    997         timeType = bestMatchTimeType;
    998         tzID.setTo(bestMatchTzID);
    999     }
   1000     return bestMatchLen;
   1001 }
   1002 
   1003 TimeZoneGenericNameMatchInfo*
   1004 TZGNCore::findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
   1005     GNameSearchHandler handler(types);
   1006 
   1007     TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
   1008 
   1009     umtx_lock(&gLock);
   1010     {
   1011         fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
   1012     }
   1013     umtx_unlock(&gLock);
   1014 
   1015     if (U_FAILURE(status)) {
   1016         return NULL;
   1017     }
   1018 
   1019     TimeZoneGenericNameMatchInfo *gmatchInfo = NULL;
   1020 
   1021     int32_t maxLen = 0;
   1022     UVector *results = handler.getMatches(maxLen);
   1023     if (results != NULL && ((maxLen == (text.length() - start)) || fGNamesTrieFullyLoaded)) {
   1024         // perfect match
   1025         gmatchInfo = new TimeZoneGenericNameMatchInfo(results);
   1026         if (gmatchInfo == NULL) {
   1027             status = U_MEMORY_ALLOCATION_ERROR;
   1028             delete results;
   1029             return NULL;
   1030         }
   1031         return gmatchInfo;
   1032     }
   1033 
   1034     if (results != NULL) {
   1035         delete results;
   1036     }
   1037 
   1038     // All names are not yet loaded into the local trie.
   1039     // Load all available names into the trie. This could be very heavy.
   1040     umtx_lock(&gLock);
   1041     {
   1042         if (!fGNamesTrieFullyLoaded) {
   1043             StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
   1044             if (U_SUCCESS(status)) {
   1045                 const UnicodeString *tzID;
   1046                 while ((tzID = tzIDs->snext(status))) {
   1047                     if (U_FAILURE(status)) {
   1048                         break;
   1049                     }
   1050                     nonConstThis->loadStrings(*tzID);
   1051                 }
   1052             }
   1053             if (tzIDs != NULL) {
   1054                 delete tzIDs;
   1055             }
   1056 
   1057             if (U_SUCCESS(status)) {
   1058                 nonConstThis->fGNamesTrieFullyLoaded = TRUE;
   1059             }
   1060         }
   1061     }
   1062     umtx_unlock(&gLock);
   1063 
   1064     if (U_FAILURE(status)) {
   1065         return NULL;
   1066     }
   1067 
   1068     umtx_lock(&gLock);
   1069     {
   1070         // now try it again
   1071         fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
   1072     }
   1073     umtx_unlock(&gLock);
   1074 
   1075     results = handler.getMatches(maxLen);
   1076     if (results != NULL && maxLen > 0) {
   1077         gmatchInfo = new TimeZoneGenericNameMatchInfo(results);
   1078         if (gmatchInfo == NULL) {
   1079             status = U_MEMORY_ALLOCATION_ERROR;
   1080             delete results;
   1081             return NULL;
   1082         }
   1083     }
   1084 
   1085     return gmatchInfo;
   1086 }
   1087 
   1088 TimeZoneNames::MatchInfoCollection*
   1089 TZGNCore::findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
   1090     // Check if the target name typs is really in the TimeZoneNames
   1091     uint32_t nameTypes = 0;
   1092     if (types & UTZGNM_LONG) {
   1093         nameTypes |= (UTZNM_LONG_GENERIC | UTZNM_LONG_STANDARD);
   1094     }
   1095     if (types & UTZGNM_SHORT) {
   1096         nameTypes |= (UTZNM_SHORT_GENERIC | UTZNM_SHORT_STANDARD);
   1097     }
   1098 
   1099     if (types) {
   1100         // Find matches in the TimeZoneNames
   1101         return fTimeZoneNames->find(text, start, nameTypes, status);
   1102     }
   1103 
   1104     return NULL;
   1105 }
   1106 
   1107 typedef struct TZGNCoreRef {
   1108     TZGNCore*       obj;
   1109     int32_t         refCount;
   1110     double          lastAccess;
   1111 } TZGNCoreRef;
   1112 
   1113 // TZGNCore object cache handling
   1114 static UMutex gTZGNLock = U_MUTEX_INITIALIZER;
   1115 static UHashtable *gTZGNCoreCache = NULL;
   1116 static UBool gTZGNCoreCacheInitialized = FALSE;
   1117 
   1118 // Access count - incremented every time up to SWEEP_INTERVAL,
   1119 // then reset to 0
   1120 static int32_t gAccessCount = 0;
   1121 
   1122 // Interval for calling the cache sweep function - every 100 times
   1123 #define SWEEP_INTERVAL 100
   1124 
   1125 // Cache expiration in millisecond. When a cached entry is no
   1126 // longer referenced and exceeding this threshold since last
   1127 // access time, then the cache entry will be deleted by the sweep
   1128 // function. For now, 3 minutes.
   1129 #define CACHE_EXPIRATION 180000.0
   1130 
   1131 U_CDECL_BEGIN
   1132 /**
   1133  * Cleanup callback func
   1134  */
   1135 static UBool U_CALLCONV tzgnCore_cleanup(void)
   1136 {
   1137     if (gTZGNCoreCache != NULL) {
   1138         uhash_close(gTZGNCoreCache);
   1139         gTZGNCoreCache = NULL;
   1140     }
   1141     gTZGNCoreCacheInitialized = FALSE;
   1142     return TRUE;
   1143 }
   1144 
   1145 /**
   1146  * Deleter for TZGNCoreRef
   1147  */
   1148 static void U_CALLCONV
   1149 deleteTZGNCoreRef(void *obj) {
   1150     icu::TZGNCoreRef *entry = (icu::TZGNCoreRef*)obj;
   1151     delete (icu::TZGNCore*) entry->obj;
   1152     uprv_free(entry);
   1153 }
   1154 U_CDECL_END
   1155 
   1156 /**
   1157  * Function used for removing unreferrenced cache entries exceeding
   1158  * the expiration time. This function must be called with in the mutex
   1159  * block.
   1160  */
   1161 static void sweepCache() {
   1162     int32_t pos = UHASH_FIRST;
   1163     const UHashElement* elem;
   1164     double now = (double)uprv_getUTCtime();
   1165 
   1166     while ((elem = uhash_nextElement(gTZGNCoreCache, &pos))) {
   1167         TZGNCoreRef *entry = (TZGNCoreRef *)elem->value.pointer;
   1168         if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) {
   1169             // delete this entry
   1170             uhash_removeElement(gTZGNCoreCache, elem);
   1171         }
   1172     }
   1173 }
   1174 
   1175 TimeZoneGenericNames::TimeZoneGenericNames()
   1176 : fRef(0) {
   1177 }
   1178 
   1179 TimeZoneGenericNames::~TimeZoneGenericNames() {
   1180     umtx_lock(&gTZGNLock);
   1181     {
   1182         U_ASSERT(fRef->refCount > 0);
   1183         // Just decrement the reference count
   1184         fRef->refCount--;
   1185     }
   1186     umtx_unlock(&gTZGNLock);
   1187 }
   1188 
   1189 TimeZoneGenericNames*
   1190 TimeZoneGenericNames::createInstance(const Locale& locale, UErrorCode& status) {
   1191     if (U_FAILURE(status)) {
   1192         return NULL;
   1193     }
   1194     TimeZoneGenericNames* instance = new TimeZoneGenericNames();
   1195     if (instance == NULL) {
   1196         status = U_MEMORY_ALLOCATION_ERROR;
   1197         return NULL;
   1198     }
   1199 
   1200     TZGNCoreRef *cacheEntry = NULL;
   1201     {
   1202         Mutex lock(&gTZGNLock);
   1203 
   1204         if (!gTZGNCoreCacheInitialized) {
   1205             // Create empty hashtable
   1206             gTZGNCoreCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
   1207             if (U_SUCCESS(status)) {
   1208                 uhash_setKeyDeleter(gTZGNCoreCache, uprv_free);
   1209                 uhash_setValueDeleter(gTZGNCoreCache, deleteTZGNCoreRef);
   1210                 gTZGNCoreCacheInitialized = TRUE;
   1211                 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEGENERICNAMES, tzgnCore_cleanup);
   1212             }
   1213         }
   1214         if (U_FAILURE(status)) {
   1215             return NULL;
   1216         }
   1217 
   1218         // Check the cache, if not available, create new one and cache
   1219         const char *key = locale.getName();
   1220         cacheEntry = (TZGNCoreRef *)uhash_get(gTZGNCoreCache, key);
   1221         if (cacheEntry == NULL) {
   1222             TZGNCore *tzgnCore = NULL;
   1223             char *newKey = NULL;
   1224 
   1225             tzgnCore = new TZGNCore(locale, status);
   1226             if (tzgnCore == NULL) {
   1227                 status = U_MEMORY_ALLOCATION_ERROR;
   1228             }
   1229             if (U_SUCCESS(status)) {
   1230                 newKey = (char *)uprv_malloc(uprv_strlen(key) + 1);
   1231                 if (newKey == NULL) {
   1232                     status = U_MEMORY_ALLOCATION_ERROR;
   1233                 } else {
   1234                     uprv_strcpy(newKey, key);
   1235                 }
   1236             }
   1237             if (U_SUCCESS(status)) {
   1238                 cacheEntry = (TZGNCoreRef *)uprv_malloc(sizeof(TZGNCoreRef));
   1239                 if (cacheEntry == NULL) {
   1240                     status = U_MEMORY_ALLOCATION_ERROR;
   1241                 } else {
   1242                     cacheEntry->obj = tzgnCore;
   1243                     cacheEntry->refCount = 1;
   1244                     cacheEntry->lastAccess = (double)uprv_getUTCtime();
   1245 
   1246                     uhash_put(gTZGNCoreCache, newKey, cacheEntry, &status);
   1247                 }
   1248             }
   1249             if (U_FAILURE(status)) {
   1250                 if (tzgnCore != NULL) {
   1251                     delete tzgnCore;
   1252                 }
   1253                 if (newKey != NULL) {
   1254                     uprv_free(newKey);
   1255                 }
   1256                 if (cacheEntry != NULL) {
   1257                     uprv_free(cacheEntry);
   1258                 }
   1259                 cacheEntry = NULL;
   1260             }
   1261         } else {
   1262             // Update the reference count
   1263             cacheEntry->refCount++;
   1264             cacheEntry->lastAccess = (double)uprv_getUTCtime();
   1265         }
   1266         gAccessCount++;
   1267         if (gAccessCount >= SWEEP_INTERVAL) {
   1268             // sweep
   1269             sweepCache();
   1270             gAccessCount = 0;
   1271         }
   1272     }  // End of mutex locked block
   1273 
   1274     if (cacheEntry == NULL) {
   1275         delete instance;
   1276         return NULL;
   1277     }
   1278 
   1279     instance->fRef = cacheEntry;
   1280     return instance;
   1281 }
   1282 
   1283 UBool
   1284 TimeZoneGenericNames::operator==(const TimeZoneGenericNames& other) const {
   1285     // Just compare if the other object also use the same
   1286     // ref entry
   1287     return fRef == other.fRef;
   1288 }
   1289 
   1290 TimeZoneGenericNames*
   1291 TimeZoneGenericNames::clone() const {
   1292     TimeZoneGenericNames* other = new TimeZoneGenericNames();
   1293     if (other) {
   1294         umtx_lock(&gTZGNLock);
   1295         {
   1296             // Just increments the reference count
   1297             fRef->refCount++;
   1298             other->fRef = fRef;
   1299         }
   1300         umtx_unlock(&gTZGNLock);
   1301     }
   1302     return other;
   1303 }
   1304 
   1305 UnicodeString&
   1306 TimeZoneGenericNames::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,
   1307                         UDate date, UnicodeString& name) const {
   1308     return fRef->obj->getDisplayName(tz, type, date, name);
   1309 }
   1310 
   1311 UnicodeString&
   1312 TimeZoneGenericNames::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
   1313     return fRef->obj->getGenericLocationName(tzCanonicalID, name);
   1314 }
   1315 
   1316 int32_t
   1317 TimeZoneGenericNames::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
   1318         UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const {
   1319     return fRef->obj->findBestMatch(text, start, types, tzID, timeType, status);
   1320 }
   1321 
   1322 U_NAMESPACE_END
   1323 #endif
   1324