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