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