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