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 "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(tzID));
    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(tzCanonicalID), 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(tzCanonicalID), 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(uID);
    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     UnicodeString mzID;
    635     fTimeZoneNames->getMetaZoneID(tzID, date, mzID);
    636     if (!mzID.isEmpty()) {
    637         UErrorCode status = U_ZERO_ERROR;
    638         UBool useStandard = FALSE;
    639         int32_t raw, sav;
    640 
    641         tz.getOffset(date, FALSE, raw, sav, status);
    642         if (U_FAILURE(status)) {
    643             return name;
    644         }
    645 
    646         if (sav == 0) {
    647             useStandard = TRUE;
    648 
    649             TimeZone *tmptz = tz.clone();
    650             // Check if the zone actually uses daylight saving time around the time
    651             BasicTimeZone *btz = NULL;
    652             if (dynamic_cast<OlsonTimeZone *>(tmptz) != NULL
    653                 || dynamic_cast<SimpleTimeZone *>(tmptz) != NULL
    654                 || dynamic_cast<RuleBasedTimeZone *>(tmptz) != NULL
    655                 || dynamic_cast<VTimeZone *>(tmptz) != NULL) {
    656                 btz = (BasicTimeZone*)tmptz;
    657             }
    658 
    659             if (btz != NULL) {
    660                 TimeZoneTransition before;
    661                 UBool beforTrs = btz->getPreviousTransition(date, TRUE, before);
    662                 if (beforTrs
    663                         && (date - before.getTime() < kDstCheckRange)
    664                         && before.getFrom()->getDSTSavings() != 0) {
    665                     useStandard = FALSE;
    666                 } else {
    667                     TimeZoneTransition after;
    668                     UBool afterTrs = btz->getNextTransition(date, FALSE, after);
    669                     if (afterTrs
    670                             && (after.getTime() - date < kDstCheckRange)
    671                             && after.getTo()->getDSTSavings() != 0) {
    672                         useStandard = FALSE;
    673                     }
    674                 }
    675             } else {
    676                 // If not BasicTimeZone... only if the instance is not an ICU's implementation.
    677                 // We may get a wrong answer in edge case, but it should practically work OK.
    678                 tmptz->getOffset(date - kDstCheckRange, FALSE, raw, sav, status);
    679                 if (sav != 0) {
    680                     useStandard = FALSE;
    681                 } else {
    682                     tmptz->getOffset(date + kDstCheckRange, FALSE, raw, sav, status);
    683                     if (sav != 0){
    684                         useStandard = FALSE;
    685                     }
    686                 }
    687                 if (U_FAILURE(status)) {
    688                     delete tmptz;
    689                     return name;
    690                 }
    691             }
    692             delete tmptz;
    693         }
    694         if (useStandard) {
    695             UTimeZoneNameType stdNameType = (nameType == UTZNM_LONG_GENERIC)
    696                 ? UTZNM_LONG_STANDARD : UTZNM_SHORT_STANDARD;
    697             UnicodeString stdName;
    698             fTimeZoneNames->getDisplayName(tzID, stdNameType, date, stdName);
    699             if (!stdName.isEmpty()) {
    700                 name.setTo(stdName);
    701 
    702                 // TODO: revisit this issue later
    703                 // In CLDR, a same display name is used for both generic and standard
    704                 // for some meta zones in some locales.  This looks like a data bugs.
    705                 // For now, we check if the standard name is different from its generic
    706                 // name below.
    707                 UnicodeString mzGenericName;
    708                 fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzGenericName);
    709                 if (stdName.caseCompare(mzGenericName, 0) == 0) {
    710                     name.setToBogus();
    711                 }
    712             }
    713         }
    714         if (name.isEmpty()) {
    715             // Get a name from meta zone
    716             UnicodeString mzName;
    717             fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzName);
    718             if (!mzName.isEmpty()) {
    719                 // Check if we need to use a partial location format.
    720                 // This check is done by comparing offset with the meta zone's
    721                 // golden zone at the given date.
    722                 UnicodeString goldenID;
    723                 fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, goldenID);
    724                 if (!goldenID.isEmpty() && goldenID != tzID) {
    725                     TimeZone *goldenZone = TimeZone::createTimeZone(goldenID);
    726                     int32_t raw1, sav1;
    727 
    728                     // Check offset in the golden zone with wall time.
    729                     // With getOffset(date, false, offsets1),
    730                     // you may get incorrect results because of time overlap at DST->STD
    731                     // transition.
    732                     goldenZone->getOffset(date + raw + sav, TRUE, raw1, sav1, status);
    733                     delete goldenZone;
    734                     if (U_SUCCESS(status)) {
    735                         if (raw != raw1 || sav != sav1) {
    736                             // Now we need to use a partial location format
    737                             getPartialLocationName(tzID, mzID, (nameType == UTZNM_LONG_GENERIC), mzName, name);
    738                         } else {
    739                             name.setTo(mzName);
    740                         }
    741                     }
    742                 } else {
    743                     name.setTo(mzName);
    744                 }
    745             }
    746         }
    747     }
    748     return name;
    749 }
    750 
    751 UnicodeString&
    752 TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID,
    753                         const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName,
    754                         UnicodeString& name) const {
    755     name.setToBogus();
    756     if (tzCanonicalID.isEmpty() || mzID.isEmpty() || mzDisplayName.isEmpty()) {
    757         return name;
    758     }
    759 
    760     const UChar *uplname = NULL;
    761     TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
    762     umtx_lock(&gLock);
    763     {
    764         uplname = nonConstThis->getPartialLocationName(tzCanonicalID, mzID, isLong, mzDisplayName);
    765     }
    766     umtx_unlock(&gLock);
    767 
    768     if (uplname == NULL) {
    769         name.setToBogus();
    770     } else {
    771         name.setTo(TRUE, uplname, -1);
    772     }
    773     return name;
    774 }
    775 
    776 /*
    777  * This method updates the cache and must be called with a lock
    778  */
    779 const UChar*
    780 TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID,
    781                         const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName) {
    782     U_ASSERT(!tzCanonicalID.isEmpty());
    783     U_ASSERT(!mzID.isEmpty());
    784     U_ASSERT(!mzDisplayName.isEmpty());
    785 
    786     PartialLocationKey key;
    787     key.tzID = ZoneMeta::findTimeZoneID(tzCanonicalID);
    788     key.mzID = ZoneMeta::findMetaZoneID(mzID);
    789     key.isLong = isLong;
    790     U_ASSERT(key.tzID != NULL && key.mzID != NULL);
    791 
    792     const UChar* uplname = (const UChar*)uhash_get(fPartialLocationNamesMap, (void *)&key);
    793     if (uplname != NULL) {
    794         return uplname;
    795     }
    796 
    797     UnicodeString location;
    798     UnicodeString usCountryCode;
    799     ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode);
    800     if (!usCountryCode.isEmpty()) {
    801         char countryCode[ULOC_COUNTRY_CAPACITY];
    802         U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY);
    803         int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV);
    804         countryCode[ccLen] = 0;
    805 
    806         UnicodeString regionalGolden;
    807         fTimeZoneNames->getReferenceZoneID(mzID, countryCode, regionalGolden);
    808         if (tzCanonicalID == regionalGolden) {
    809             // Use country name
    810             fLocaleDisplayNames->regionDisplayName(countryCode, location);
    811         } else {
    812             // Otherwise, use exemplar city name
    813             fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);
    814         }
    815     } else {
    816         fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);
    817         if (location.isEmpty()) {
    818             // This could happen when the time zone is not associated with a country,
    819             // and its ID is not hierarchical, for example, CST6CDT.
    820             // We use the canonical ID itself as the location for this case.
    821             location.setTo(tzCanonicalID);
    822         }
    823     }
    824 
    825     UErrorCode status = U_ZERO_ERROR;
    826     UnicodeString name;
    827 
    828     FieldPosition fpos;
    829     Formattable param[] = {
    830         Formattable(location),
    831         Formattable(mzDisplayName)
    832     };
    833     fFallbackFormat->format(param, 2, name, fpos, status);
    834     if (U_FAILURE(status)) {
    835         return NULL;
    836     }
    837 
    838     uplname = fStringPool.get(name, status);
    839     if (U_SUCCESS(status)) {
    840         // Add the name to cache
    841         PartialLocationKey* cacheKey = (PartialLocationKey *)uprv_malloc(sizeof(PartialLocationKey));
    842         if (cacheKey != NULL) {
    843             cacheKey->tzID = key.tzID;
    844             cacheKey->mzID = key.mzID;
    845             cacheKey->isLong = key.isLong;
    846             uhash_put(fPartialLocationNamesMap, (void *)cacheKey, (void *)uplname, &status);
    847             if (U_FAILURE(status)) {
    848                 uprv_free(cacheKey);
    849             } else {
    850                 // put the name to the local trie as well
    851                 GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo));
    852                 if (nameinfo != NULL) {
    853                     nameinfo->type = isLong ? UTZGNM_LONG : UTZGNM_SHORT;
    854                     nameinfo->tzID = key.tzID;
    855                     fGNamesTrie.put(uplname, nameinfo, status);
    856                 }
    857             }
    858         }
    859     }
    860     return uplname;
    861 }
    862 
    863 /*
    864  * This method updates the cache and must be called with a lock,
    865  * except initializer.
    866  */
    867 void
    868 TZGNCore::loadStrings(const UnicodeString& tzCanonicalID) {
    869     // load the generic location name
    870     getGenericLocationName(tzCanonicalID);
    871 
    872     // partial location names
    873     UErrorCode status = U_ZERO_ERROR;
    874 
    875     const UnicodeString *mzID;
    876     UnicodeString goldenID;
    877     UnicodeString mzGenName;
    878     UTimeZoneNameType genNonLocTypes[] = {
    879         UTZNM_LONG_GENERIC, UTZNM_SHORT_GENERIC,
    880         UTZNM_UNKNOWN /*terminator*/
    881     };
    882 
    883     StringEnumeration *mzIDs = fTimeZoneNames->getAvailableMetaZoneIDs(tzCanonicalID, status);
    884     while ((mzID = mzIDs->snext(status))) {
    885         if (U_FAILURE(status)) {
    886             break;
    887         }
    888         // if this time zone is not the golden zone of the meta zone,
    889         // partial location name (such as "PT (Los Angeles)") might be
    890         // available.
    891         fTimeZoneNames->getReferenceZoneID(*mzID, fTargetRegion, goldenID);
    892         if (tzCanonicalID != goldenID) {
    893             for (int32_t i = 0; genNonLocTypes[i] != UTZNM_UNKNOWN; i++) {
    894                 fTimeZoneNames->getMetaZoneDisplayName(*mzID, genNonLocTypes[i], mzGenName);
    895                 if (!mzGenName.isEmpty()) {
    896                     // getPartialLocationName formats a name and put it into the trie
    897                     getPartialLocationName(tzCanonicalID, *mzID,
    898                         (genNonLocTypes[i] == UTZNM_LONG_GENERIC), mzGenName);
    899                 }
    900             }
    901         }
    902     }
    903     if (mzIDs != NULL) {
    904         delete mzIDs;
    905     }
    906 }
    907 
    908 int32_t
    909 TZGNCore::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
    910         UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const {
    911     timeType = UTZFMT_TIME_TYPE_UNKNOWN;
    912     tzID.setToBogus();
    913 
    914     if (U_FAILURE(status)) {
    915         return 0;
    916     }
    917 
    918     // Find matches in the TimeZoneNames first
    919     TimeZoneNames::MatchInfoCollection *tznamesMatches = findTimeZoneNames(text, start, types, status);
    920     if (U_FAILURE(status)) {
    921         return 0;
    922     }
    923 
    924     int32_t bestMatchLen = 0;
    925     UTimeZoneFormatTimeType bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
    926     UnicodeString bestMatchTzID;
    927     // UBool isLongStandard = FALSE;   // workaround - see the comments below
    928     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
    929 
    930     if (tznamesMatches != NULL) {
    931         UnicodeString mzID;
    932         for (int32_t i = 0; i < tznamesMatches->size(); i++) {
    933             int32_t len = tznamesMatches->getMatchLengthAt(i);
    934             if (len > bestMatchLen) {
    935                 bestMatchLen = len;
    936                 if (!tznamesMatches->getTimeZoneIDAt(i, bestMatchTzID)) {
    937                     // name for a meta zone
    938                     if (tznamesMatches->getMetaZoneIDAt(i, mzID)) {
    939                         fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, bestMatchTzID);
    940                     }
    941                 }
    942                 UTimeZoneNameType nameType = tznamesMatches->getNameTypeAt(i);
    943                 if (U_FAILURE(status)) {
    944                     break;
    945                 }
    946                 switch (nameType) {
    947                 case UTZNM_LONG_STANDARD:
    948                     // isLongStandard = TRUE;
    949                 case UTZNM_SHORT_STANDARD:  // this one is never used for generic, but just in case
    950                     isStandard = TRUE;      // TODO: Remove this later, see the comments above.
    951                     bestMatchTimeType = UTZFMT_TIME_TYPE_STANDARD;
    952                     break;
    953                 case UTZNM_LONG_DAYLIGHT:
    954                 case UTZNM_SHORT_DAYLIGHT: // this one is never used for generic, but just in case
    955                     bestMatchTimeType = UTZFMT_TIME_TYPE_DAYLIGHT;
    956                     break;
    957                 default:
    958                     bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
    959                 }
    960             }
    961         }
    962         delete tznamesMatches;
    963         if (U_FAILURE(status)) {
    964             return 0;
    965         }
    966 
    967         if (bestMatchLen == (text.length() - start)) {
    968             // Full match
    969 
    970             //tzID.setTo(bestMatchTzID);
    971             //timeType = bestMatchTimeType;
    972             //return bestMatchLen;
    973 
    974             // TODO Some time zone uses a same name for the long standard name
    975             // and the location name. When the match is a long standard name,
    976             // then we need to check if the name is same with the location name.
    977             // This is probably a data error or a design bug.
    978 /*
    979             if (!isLongStandard) {
    980                 tzID.setTo(bestMatchTzID);
    981                 timeType = bestMatchTimeType;
    982                 return bestMatchLen;
    983             }
    984 */
    985             // TODO The deprecation of commonlyUsed flag introduced the name
    986             // conflict not only for long standard names, but short standard names too.
    987             // These short names (found in zh_Hant) should be gone once we clean
    988             // up CLDR time zone display name data. Once the short name conflict
    989             // problem (with location name) is resolved, we should change the condition
    990             // below back to the original one above. -Yoshito (2011-09-14)
    991             if (!isStandard) {
    992                 tzID.setTo(bestMatchTzID);
    993                 timeType = bestMatchTimeType;
    994                 return bestMatchLen;
    995             }
    996         }
    997     }
    998 
    999     // Find matches in the local trie
   1000     TimeZoneGenericNameMatchInfo *localMatches = findLocal(text, start, types, status);
   1001     if (U_FAILURE(status)) {
   1002         return 0;
   1003     }
   1004     if (localMatches != NULL) {
   1005         for (int32_t i = 0; i < localMatches->size(); i++) {
   1006             int32_t len = localMatches->getMatchLength(i);
   1007 
   1008             // TODO See the above TODO. We use len >= bestMatchLen
   1009             // because of the long standard/location name collision
   1010             // problem. If it is also a location name, carrying
   1011             // timeType = UTZFMT_TIME_TYPE_STANDARD will cause a
   1012             // problem in SimpleDateFormat
   1013             if (len >= bestMatchLen) {
   1014                 bestMatchLen = localMatches->getMatchLength(i);
   1015                 bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;   // because generic
   1016                 localMatches->getTimeZoneID(i, bestMatchTzID);
   1017             }
   1018         }
   1019         delete localMatches;
   1020     }
   1021 
   1022     if (bestMatchLen > 0) {
   1023         timeType = bestMatchTimeType;
   1024         tzID.setTo(bestMatchTzID);
   1025     }
   1026     return bestMatchLen;
   1027 }
   1028 
   1029 TimeZoneGenericNameMatchInfo*
   1030 TZGNCore::findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
   1031     GNameSearchHandler handler(types);
   1032 
   1033     TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
   1034 
   1035     umtx_lock(&gLock);
   1036     {
   1037         fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
   1038     }
   1039     umtx_unlock(&gLock);
   1040 
   1041     if (U_FAILURE(status)) {
   1042         return NULL;
   1043     }
   1044 
   1045     TimeZoneGenericNameMatchInfo *gmatchInfo = NULL;
   1046 
   1047     int32_t maxLen = 0;
   1048     UVector *results = handler.getMatches(maxLen);
   1049     if (results != NULL && ((maxLen == (text.length() - start)) || fGNamesTrieFullyLoaded)) {
   1050         // perfect match
   1051         gmatchInfo = new TimeZoneGenericNameMatchInfo(results);
   1052         if (gmatchInfo == NULL) {
   1053             status = U_MEMORY_ALLOCATION_ERROR;
   1054             delete results;
   1055             return NULL;
   1056         }
   1057         return gmatchInfo;
   1058     }
   1059 
   1060     if (results != NULL) {
   1061         delete results;
   1062     }
   1063 
   1064     // All names are not yet loaded into the local trie.
   1065     // Load all available names into the trie. This could be very heavy.
   1066     umtx_lock(&gLock);
   1067     {
   1068         if (!fGNamesTrieFullyLoaded) {
   1069             StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
   1070             if (U_SUCCESS(status)) {
   1071                 const UnicodeString *tzID;
   1072                 while ((tzID = tzIDs->snext(status))) {
   1073                     if (U_FAILURE(status)) {
   1074                         break;
   1075                     }
   1076                     nonConstThis->loadStrings(*tzID);
   1077                 }
   1078             }
   1079             if (tzIDs != NULL) {
   1080                 delete tzIDs;
   1081             }
   1082 
   1083             if (U_SUCCESS(status)) {
   1084                 nonConstThis->fGNamesTrieFullyLoaded = TRUE;
   1085             }
   1086         }
   1087     }
   1088     umtx_unlock(&gLock);
   1089 
   1090     if (U_FAILURE(status)) {
   1091         return NULL;
   1092     }
   1093 
   1094     umtx_lock(&gLock);
   1095     {
   1096         // now try it again
   1097         fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
   1098     }
   1099     umtx_unlock(&gLock);
   1100 
   1101     results = handler.getMatches(maxLen);
   1102     if (results != NULL && maxLen > 0) {
   1103         gmatchInfo = new TimeZoneGenericNameMatchInfo(results);
   1104         if (gmatchInfo == NULL) {
   1105             status = U_MEMORY_ALLOCATION_ERROR;
   1106             delete results;
   1107             return NULL;
   1108         }
   1109     }
   1110 
   1111     return gmatchInfo;
   1112 }
   1113 
   1114 TimeZoneNames::MatchInfoCollection*
   1115 TZGNCore::findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
   1116     // Check if the target name typs is really in the TimeZoneNames
   1117     uint32_t nameTypes = 0;
   1118     if (types & UTZGNM_LONG) {
   1119         nameTypes |= (UTZNM_LONG_GENERIC | UTZNM_LONG_STANDARD);
   1120     }
   1121     if (types & UTZGNM_SHORT) {
   1122         nameTypes |= (UTZNM_SHORT_GENERIC | UTZNM_SHORT_STANDARD);
   1123     }
   1124 
   1125     if (types) {
   1126         // Find matches in the TimeZoneNames
   1127         return fTimeZoneNames->find(text, start, nameTypes, status);
   1128     }
   1129 
   1130     return NULL;
   1131 }
   1132 
   1133 typedef struct TZGNCoreRef {
   1134     TZGNCore*       obj;
   1135     int32_t         refCount;
   1136     double          lastAccess;
   1137 } TZGNCoreRef;
   1138 
   1139 // TZGNCore object cache handling
   1140 static UMutex gTZGNLock = U_MUTEX_INITIALIZER;
   1141 static UHashtable *gTZGNCoreCache = NULL;
   1142 static UBool gTZGNCoreCacheInitialized = FALSE;
   1143 
   1144 // Access count - incremented every time up to SWEEP_INTERVAL,
   1145 // then reset to 0
   1146 static int32_t gAccessCount = 0;
   1147 
   1148 // Interval for calling the cache sweep function - every 100 times
   1149 #define SWEEP_INTERVAL 100
   1150 
   1151 // Cache expiration in millisecond. When a cached entry is no
   1152 // longer referenced and exceeding this threshold since last
   1153 // access time, then the cache entry will be deleted by the sweep
   1154 // function. For now, 3 minutes.
   1155 #define CACHE_EXPIRATION 180000.0
   1156 
   1157 U_CDECL_BEGIN
   1158 /**
   1159  * Cleanup callback func
   1160  */
   1161 static UBool U_CALLCONV tzgnCore_cleanup(void)
   1162 {
   1163     if (gTZGNCoreCache != NULL) {
   1164         uhash_close(gTZGNCoreCache);
   1165         gTZGNCoreCache = NULL;
   1166     }
   1167     gTZGNCoreCacheInitialized = FALSE;
   1168     return TRUE;
   1169 }
   1170 
   1171 /**
   1172  * Deleter for TZGNCoreRef
   1173  */
   1174 static void U_CALLCONV
   1175 deleteTZGNCoreRef(void *obj) {
   1176     icu::TZGNCoreRef *entry = (icu::TZGNCoreRef*)obj;
   1177     delete (icu::TZGNCore*) entry->obj;
   1178     uprv_free(entry);
   1179 }
   1180 U_CDECL_END
   1181 
   1182 /**
   1183  * Function used for removing unreferrenced cache entries exceeding
   1184  * the expiration time. This function must be called with in the mutex
   1185  * block.
   1186  */
   1187 static void sweepCache() {
   1188     int32_t pos = -1;
   1189     const UHashElement* elem;
   1190     double now = (double)uprv_getUTCtime();
   1191 
   1192     while ((elem = uhash_nextElement(gTZGNCoreCache, &pos))) {
   1193         TZGNCoreRef *entry = (TZGNCoreRef *)elem->value.pointer;
   1194         if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) {
   1195             // delete this entry
   1196             uhash_removeElement(gTZGNCoreCache, elem);
   1197         }
   1198     }
   1199 }
   1200 
   1201 TimeZoneGenericNames::TimeZoneGenericNames()
   1202 : fRef(0) {
   1203 }
   1204 
   1205 TimeZoneGenericNames::~TimeZoneGenericNames() {
   1206     umtx_lock(&gTZGNLock);
   1207     {
   1208         U_ASSERT(fRef->refCount > 0);
   1209         // Just decrement the reference count
   1210         fRef->refCount--;
   1211     }
   1212     umtx_unlock(&gTZGNLock);
   1213 }
   1214 
   1215 TimeZoneGenericNames*
   1216 TimeZoneGenericNames::createInstance(const Locale& locale, UErrorCode& status) {
   1217     if (U_FAILURE(status)) {
   1218         return NULL;
   1219     }
   1220     TimeZoneGenericNames* instance = new TimeZoneGenericNames();
   1221     if (instance == NULL) {
   1222         status = U_MEMORY_ALLOCATION_ERROR;
   1223         return NULL;
   1224     }
   1225 
   1226     TZGNCoreRef *cacheEntry = NULL;
   1227     {
   1228         Mutex lock(&gTZGNLock);
   1229 
   1230         if (!gTZGNCoreCacheInitialized) {
   1231             // Create empty hashtable
   1232             gTZGNCoreCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
   1233             if (U_SUCCESS(status)) {
   1234                 uhash_setKeyDeleter(gTZGNCoreCache, uprv_free);
   1235                 uhash_setValueDeleter(gTZGNCoreCache, deleteTZGNCoreRef);
   1236                 gTZGNCoreCacheInitialized = TRUE;
   1237                 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEGENERICNAMES, tzgnCore_cleanup);
   1238             }
   1239         }
   1240         if (U_FAILURE(status)) {
   1241             return NULL;
   1242         }
   1243 
   1244         // Check the cache, if not available, create new one and cache
   1245         const char *key = locale.getName();
   1246         cacheEntry = (TZGNCoreRef *)uhash_get(gTZGNCoreCache, key);
   1247         if (cacheEntry == NULL) {
   1248             TZGNCore *tzgnCore = NULL;
   1249             char *newKey = NULL;
   1250 
   1251             tzgnCore = new TZGNCore(locale, status);
   1252             if (tzgnCore == NULL) {
   1253                 status = U_MEMORY_ALLOCATION_ERROR;
   1254             }
   1255             if (U_SUCCESS(status)) {
   1256                 newKey = (char *)uprv_malloc(uprv_strlen(key) + 1);
   1257                 if (newKey == NULL) {
   1258                     status = U_MEMORY_ALLOCATION_ERROR;
   1259                 } else {
   1260                     uprv_strcpy(newKey, key);
   1261                 }
   1262             }
   1263             if (U_SUCCESS(status)) {
   1264                 cacheEntry = (TZGNCoreRef *)uprv_malloc(sizeof(TZGNCoreRef));
   1265                 if (cacheEntry == NULL) {
   1266                     status = U_MEMORY_ALLOCATION_ERROR;
   1267                 } else {
   1268                     cacheEntry->obj = tzgnCore;
   1269                     cacheEntry->refCount = 1;
   1270                     cacheEntry->lastAccess = (double)uprv_getUTCtime();
   1271 
   1272                     uhash_put(gTZGNCoreCache, newKey, cacheEntry, &status);
   1273                 }
   1274             }
   1275             if (U_FAILURE(status)) {
   1276                 if (tzgnCore != NULL) {
   1277                     delete tzgnCore;
   1278                 }
   1279                 if (newKey != NULL) {
   1280                     uprv_free(newKey);
   1281                 }
   1282                 if (cacheEntry != NULL) {
   1283                     uprv_free(cacheEntry);
   1284                 }
   1285                 cacheEntry = NULL;
   1286             }
   1287         } else {
   1288             // Update the reference count
   1289             cacheEntry->refCount++;
   1290             cacheEntry->lastAccess = (double)uprv_getUTCtime();
   1291         }
   1292         gAccessCount++;
   1293         if (gAccessCount >= SWEEP_INTERVAL) {
   1294             // sweep
   1295             sweepCache();
   1296             gAccessCount = 0;
   1297         }
   1298     }  // End of mutex locked block
   1299 
   1300     if (cacheEntry == NULL) {
   1301         delete instance;
   1302         return NULL;
   1303     }
   1304 
   1305     instance->fRef = cacheEntry;
   1306     return instance;
   1307 }
   1308 
   1309 UBool
   1310 TimeZoneGenericNames::operator==(const TimeZoneGenericNames& other) const {
   1311     // Just compare if the other object also use the same
   1312     // ref entry
   1313     return fRef == other.fRef;
   1314 }
   1315 
   1316 TimeZoneGenericNames*
   1317 TimeZoneGenericNames::clone() const {
   1318     TimeZoneGenericNames* other = new TimeZoneGenericNames();
   1319     if (other) {
   1320         umtx_lock(&gTZGNLock);
   1321         {
   1322             // Just increments the reference count
   1323             fRef->refCount++;
   1324             other->fRef = fRef;
   1325         }
   1326         umtx_unlock(&gTZGNLock);
   1327     }
   1328     return other;
   1329 }
   1330 
   1331 UnicodeString&
   1332 TimeZoneGenericNames::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,
   1333                         UDate date, UnicodeString& name) const {
   1334     return fRef->obj->getDisplayName(tz, type, date, name);
   1335 }
   1336 
   1337 UnicodeString&
   1338 TimeZoneGenericNames::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
   1339     return fRef->obj->getGenericLocationName(tzCanonicalID, name);
   1340 }
   1341 
   1342 int32_t
   1343 TimeZoneGenericNames::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
   1344         UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const {
   1345     return fRef->obj->findBestMatch(text, start, types, tzID, timeType, status);
   1346 }
   1347 
   1348 U_NAMESPACE_END
   1349 #endif
   1350