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