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