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