intl/icu/source/i18n/tzgnames.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /*
     2 *******************************************************************************
     3 * Copyright (C) 2011-2013, International Business Machines Corporation and
     4 * others. All Rights Reserved.
     5 *******************************************************************************
     6 */
     8 #include "unicode/utypes.h"
    10 #if !UCONFIG_NO_FORMATTING
    12 #include "tzgnames.h"
    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"
    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"
    34 U_NAMESPACE_BEGIN
    36 #define ZID_KEY_MAX  128
    38 static const char gZoneStrings[]                = "zoneStrings";
    40 static const char gRegionFormatTag[]            = "regionFormat";
    41 static const char gFallbackFormatTag[]          = "fallbackFormat";
    43 static const UChar gEmpty[]                     = {0x00};
    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})"
    48 static const double kDstCheckRange      = (double)184*U_MILLIS_PER_DAY;
    52 U_CDECL_BEGIN
    54 typedef struct PartialLocationKey {
    55     const UChar* tzID;
    56     const UChar* mzID;
    57     UBool isLong;
    58 } PartialLocationKey;
    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 }
    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;
    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 }
    93 /**
    94  * Deleter for GNameInfo
    95  */
    96 static void U_CALLCONV
    97 deleteGNameInfo(void *obj) {
    98     uprv_free(obj);
    99 }
   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;
   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;
   118 U_CDECL_END
   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();
   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;
   133 private:
   134     UVector* fMatches;  // vector of MatchEntry
   135 };
   137 TimeZoneGenericNameMatchInfo::TimeZoneGenericNameMatchInfo(UVector* matches)
   138 : fMatches(matches) {
   139 }
   141 TimeZoneGenericNameMatchInfo::~TimeZoneGenericNameMatchInfo() {
   142     if (fMatches != NULL) {
   143         delete fMatches;
   144     }
   145 }
   147 int32_t
   148 TimeZoneGenericNameMatchInfo::size() const {
   149     if (fMatches == NULL) {
   150         return 0;
   151     }
   152     return fMatches->size();
   153 }
   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 }
   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 }
   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 }
   184 // ---------------------------------------------------
   185 // GNameSearchHandler
   186 // ---------------------------------------------------
   187 class GNameSearchHandler : public TextTrieMapSearchResultHandler {
   188 public:
   189     GNameSearchHandler(uint32_t types);
   190     virtual ~GNameSearchHandler();
   192     UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status);
   193     UVector* getMatches(int32_t& maxMatchLen);
   195 private:
   196     uint32_t fTypes;
   197     UVector* fResults;
   198     int32_t fMaxMatchLen;
   199 };
   201 GNameSearchHandler::GNameSearchHandler(uint32_t types)
   202 : fTypes(types), fResults(NULL), fMaxMatchLen(0) {
   203 }
   205 GNameSearchHandler::~GNameSearchHandler() {
   206     if (fResults != NULL) {
   207         delete fResults;
   208     }
   209 }
   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 }
   257 UVector*
   258 GNameSearchHandler::getMatches(int32_t& maxMatchLen) {
   259     // give the ownership to the caller
   260     UVector *results = fResults;
   261     maxMatchLen = fMaxMatchLen;
   263     // reset
   264     fResults = NULL;
   265     fMaxMatchLen = 0;
   266     return results;
   267 }
   269 static UMutex gLock = U_MUTEX_INITIALIZER;
   271 class TZGNCore : public UMemory {
   272 public:
   273     TZGNCore(const Locale& locale, UErrorCode& status);
   274     virtual ~TZGNCore();
   276     UnicodeString& getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,
   277                         UDate date, UnicodeString& name) const;
   279     UnicodeString& getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const;
   281     int32_t findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
   282         UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const;
   284 private:
   285     Locale fLocale;
   286     const TimeZoneNames* fTimeZoneNames;
   287     UHashtable* fLocationNamesMap;
   288     UHashtable* fPartialLocationNamesMap;
   290     MessageFormat* fRegionFormat;
   291     MessageFormat* fFallbackFormat;
   293     LocaleDisplayNames* fLocaleDisplayNames;
   294     ZNStringPool fStringPool;
   296     TextTrieMap fGNamesTrie;
   297     UBool fGNamesTrieFullyLoaded;
   299     char fTargetRegion[ULOC_COUNTRY_CAPACITY];
   301     void initialize(const Locale& locale, UErrorCode& status);
   302     void cleanup();
   304     void loadStrings(const UnicodeString& tzCanonicalID);
   306     const UChar* getGenericLocationName(const UnicodeString& tzCanonicalID);
   308     UnicodeString& formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type,
   309                         UDate date, UnicodeString& name) const;
   311     UnicodeString& getPartialLocationName(const UnicodeString& tzCanonicalID,
   312                         const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName,
   313                         UnicodeString& name) const;
   315     const UChar* getPartialLocationName(const UnicodeString& tzCanonicalID,
   316                         const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName);
   318     TimeZoneGenericNameMatchInfo* findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
   320     TimeZoneNames::MatchInfoCollection* findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
   321 };
   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 }
   345 TZGNCore::~TZGNCore() {
   346     cleanup();
   347 }
   349 void
   350 TZGNCore::initialize(const Locale& locale, UErrorCode& status) {
   351     if (U_FAILURE(status)) {
   352         return;
   353     }
   355     // TimeZoneNames
   356     fTimeZoneNames = TimeZoneNames::createInstance(locale, status);
   357     if (U_FAILURE(status)) {
   358         return;
   359     }
   361     // Initialize format patterns
   362     UnicodeString rpat(TRUE, gDefRegionPattern, -1);
   363     UnicodeString fpat(TRUE, gDefFallbackPattern, -1);
   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);
   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);
   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     }
   395     // locale display names
   396     fLocaleDisplayNames = LocaleDisplayNames::createInstance(locale);
   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     }
   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
   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);
   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     }
   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 }
   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     }
   457     uhash_close(fLocationNamesMap);
   458     uhash_close(fPartialLocationNamesMap);
   459 }
   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 }
   490 UnicodeString&
   491 TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
   492     if (tzCanonicalID.isEmpty()) {
   493         name.setToBogus();
   494         return name;
   495     }
   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);
   505     if (locname == NULL) {
   506         name.setToBogus();
   507     } else {
   508         name.setTo(locname, u_strlen(locname));
   509     }
   511     return name;
   512 }
   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     }
   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;
   530     const UChar *locname = (const UChar *)uhash_get(fLocationNamesMap, tzIDKey);
   532     if (locname != NULL) {
   533         // gEmpty indicate the name is not available
   534         if (locname == gEmpty) {
   535             return NULL;
   536         }
   537         return locname;
   538     }
   540     // Construct location name
   541     UnicodeString name;
   542     UnicodeString usCountryCode;
   543     UBool isPrimary = FALSE;
   545     ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode, &isPrimary);
   547     if (!usCountryCode.isEmpty()) {
   548         FieldPosition fpos;
   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;
   557             UnicodeString country;
   558             fLocaleDisplayNames->regionDisplayName(countryCode, country);
   560             Formattable param[] = {
   561                 Formattable(country)
   562             };
   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.
   569             // getExemplarLocationName should retur non-empty string
   570             // if the time zone is associated with a region
   572             UnicodeString city;
   573             fTimeZoneNames->getExemplarLocationName(tzCanonicalID, city);
   575             Formattable param[] = {
   576                 Formattable(city),
   577             };
   579             fRegionFormat->format(param, 1, name, fpos, status);
   580         }
   581         if (U_FAILURE(status)) {
   582             return NULL;
   583         }
   584     }
   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     }
   610     return locname;
   611 }
   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();
   618     const UChar* uID = ZoneMeta::getCanonicalCLDRID(tz);
   619     if (uID == NULL) {
   620         return name;
   621     }
   623     UnicodeString tzID(uID);
   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);
   629     if (!name.isEmpty()) {
   630         return name;
   631     }
   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;
   641         tz.getOffset(date, FALSE, raw, sav, status);
   642         if (U_FAILURE(status)) {
   643             return name;
   644         }
   646         if (sav == 0) {
   647             useStandard = TRUE;
   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             }
   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);
   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;
   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 }
   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     }
   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);
   768     if (uplname == NULL) {
   769         name.setToBogus();
   770     } else {
   771         name.setTo(TRUE, uplname, -1);
   772     }
   773     return name;
   774 }
   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());
   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);
   792     const UChar* uplname = (const UChar*)uhash_get(fPartialLocationNamesMap, (void *)&key);
   793     if (uplname != NULL) {
   794         return uplname;
   795     }
   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;
   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     }
   825     UErrorCode status = U_ZERO_ERROR;
   826     UnicodeString name;
   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     }
   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 }
   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);
   872     // partial location names
   873     UErrorCode status = U_ZERO_ERROR;
   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     };
   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 }
   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();
   914     if (U_FAILURE(status)) {
   915         return 0;
   916     }
   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     }
   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
   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         }
   967         if (bestMatchLen == (text.length() - start)) {
   968             // Full match
   970             //tzID.setTo(bestMatchTzID);
   971             //timeType = bestMatchTimeType;
   972             //return bestMatchLen;
   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     }
   999     // Find matches in the local trie
  1000     TimeZoneGenericNameMatchInfo *localMatches = findLocal(text, start, types, status);
  1001     if (U_FAILURE(status)) {
  1002         return 0;
  1004     if (localMatches != NULL) {
  1005         for (int32_t i = 0; i < localMatches->size(); i++) {
  1006             int32_t len = localMatches->getMatchLength(i);
  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);
  1019         delete localMatches;
  1022     if (bestMatchLen > 0) {
  1023         timeType = bestMatchTimeType;
  1024         tzID.setTo(bestMatchTzID);
  1026     return bestMatchLen;
  1029 TimeZoneGenericNameMatchInfo*
  1030 TZGNCore::findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
  1031     GNameSearchHandler handler(types);
  1033     TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
  1035     umtx_lock(&gLock);
  1037         fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
  1039     umtx_unlock(&gLock);
  1041     if (U_FAILURE(status)) {
  1042         return NULL;
  1045     TimeZoneGenericNameMatchInfo *gmatchInfo = NULL;
  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;
  1057         return gmatchInfo;
  1060     if (results != NULL) {
  1061         delete results;
  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);
  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;
  1076                     nonConstThis->loadStrings(*tzID);
  1079             if (tzIDs != NULL) {
  1080                 delete tzIDs;
  1083             if (U_SUCCESS(status)) {
  1084                 nonConstThis->fGNamesTrieFullyLoaded = TRUE;
  1088     umtx_unlock(&gLock);
  1090     if (U_FAILURE(status)) {
  1091         return NULL;
  1094     umtx_lock(&gLock);
  1096         // now try it again
  1097         fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
  1099     umtx_unlock(&gLock);
  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;
  1111     return gmatchInfo;
  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);
  1121     if (types & UTZGNM_SHORT) {
  1122         nameTypes |= (UTZNM_SHORT_GENERIC | UTZNM_SHORT_STANDARD);
  1125     if (types) {
  1126         // Find matches in the TimeZoneNames
  1127         return fTimeZoneNames->find(text, start, nameTypes, status);
  1130     return NULL;
  1133 typedef struct TZGNCoreRef {
  1134     TZGNCore*       obj;
  1135     int32_t         refCount;
  1136     double          lastAccess;
  1137 } TZGNCoreRef;
  1139 // TZGNCore object cache handling
  1140 static UMutex gTZGNLock = U_MUTEX_INITIALIZER;
  1141 static UHashtable *gTZGNCoreCache = NULL;
  1142 static UBool gTZGNCoreCacheInitialized = FALSE;
  1144 // Access count - incremented every time up to SWEEP_INTERVAL,
  1145 // then reset to 0
  1146 static int32_t gAccessCount = 0;
  1148 // Interval for calling the cache sweep function - every 100 times
  1149 #define SWEEP_INTERVAL 100
  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
  1157 U_CDECL_BEGIN
  1158 /**
  1159  * Cleanup callback func
  1160  */
  1161 static UBool U_CALLCONV tzgnCore_cleanup(void)
  1163     if (gTZGNCoreCache != NULL) {
  1164         uhash_close(gTZGNCoreCache);
  1165         gTZGNCoreCache = NULL;
  1167     gTZGNCoreCacheInitialized = FALSE;
  1168     return TRUE;
  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);
  1180 U_CDECL_END
  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();
  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);
  1201 TimeZoneGenericNames::TimeZoneGenericNames()
  1202 : fRef(0) {
  1205 TimeZoneGenericNames::~TimeZoneGenericNames() {
  1206     umtx_lock(&gTZGNLock);
  1208         U_ASSERT(fRef->refCount > 0);
  1209         // Just decrement the reference count
  1210         fRef->refCount--;
  1212     umtx_unlock(&gTZGNLock);
  1215 TimeZoneGenericNames*
  1216 TimeZoneGenericNames::createInstance(const Locale& locale, UErrorCode& status) {
  1217     if (U_FAILURE(status)) {
  1218         return NULL;
  1220     TimeZoneGenericNames* instance = new TimeZoneGenericNames();
  1221     if (instance == NULL) {
  1222         status = U_MEMORY_ALLOCATION_ERROR;
  1223         return NULL;
  1226     TZGNCoreRef *cacheEntry = NULL;
  1228         Mutex lock(&gTZGNLock);
  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);
  1240         if (U_FAILURE(status)) {
  1241             return NULL;
  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;
  1251             tzgnCore = new TZGNCore(locale, status);
  1252             if (tzgnCore == NULL) {
  1253                 status = U_MEMORY_ALLOCATION_ERROR;
  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);
  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();
  1272                     uhash_put(gTZGNCoreCache, newKey, cacheEntry, &status);
  1275             if (U_FAILURE(status)) {
  1276                 if (tzgnCore != NULL) {
  1277                     delete tzgnCore;
  1279                 if (newKey != NULL) {
  1280                     uprv_free(newKey);
  1282                 if (cacheEntry != NULL) {
  1283                     uprv_free(cacheEntry);
  1285                 cacheEntry = NULL;
  1287         } else {
  1288             // Update the reference count
  1289             cacheEntry->refCount++;
  1290             cacheEntry->lastAccess = (double)uprv_getUTCtime();
  1292         gAccessCount++;
  1293         if (gAccessCount >= SWEEP_INTERVAL) {
  1294             // sweep
  1295             sweepCache();
  1296             gAccessCount = 0;
  1298     }  // End of mutex locked block
  1300     if (cacheEntry == NULL) {
  1301         delete instance;
  1302         return NULL;
  1305     instance->fRef = cacheEntry;
  1306     return instance;
  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;
  1316 TimeZoneGenericNames*
  1317 TimeZoneGenericNames::clone() const {
  1318     TimeZoneGenericNames* other = new TimeZoneGenericNames();
  1319     if (other) {
  1320         umtx_lock(&gTZGNLock);
  1322             // Just increments the reference count
  1323             fRef->refCount++;
  1324             other->fRef = fRef;
  1326         umtx_unlock(&gTZGNLock);
  1328     return other;
  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);
  1337 UnicodeString&
  1338 TimeZoneGenericNames::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
  1339     return fRef->obj->getGenericLocationName(tzCanonicalID, name);
  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);
  1348 U_NAMESPACE_END
  1349 #endif

mercurial