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