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