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