1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 2011-2015, International Business Machines Corporation and *
6 * others. All Rights Reserved. *
7 *******************************************************************************
10 #include "unicode/utypes.h"
12 #if !UCONFIG_NO_FORMATTING
14 #include "unicode/locid.h"
15 #include "unicode/tznames.h"
16 #include "unicode/uenum.h"
21 #include "tznames_impl.h"
31 // TimeZoneNames object cache handling
32 static UMutex gTimeZoneNamesLock
= U_MUTEX_INITIALIZER
;
33 static UHashtable
*gTimeZoneNamesCache
= NULL
;
34 static UBool gTimeZoneNamesCacheInitialized
= FALSE
;
36 // Access count - incremented every time up to SWEEP_INTERVAL,
38 static int32_t gAccessCount
= 0;
40 // Interval for calling the cache sweep function - every 100 times
41 #define SWEEP_INTERVAL 100
43 // Cache expiration in millisecond. When a cached entry is no
44 // longer referenced and exceeding this threshold since last
45 // access time, then the cache entry will be deleted by the sweep
46 // function. For now, 3 minutes.
47 #define CACHE_EXPIRATION 180000.0
49 typedef struct TimeZoneNamesCacheEntry
{
53 } TimeZoneNamesCacheEntry
;
57 * Cleanup callback func
59 static UBool U_CALLCONV
timeZoneNames_cleanup(void)
61 if (gTimeZoneNamesCache
!= NULL
) {
62 uhash_close(gTimeZoneNamesCache
);
63 gTimeZoneNamesCache
= NULL
;
65 gTimeZoneNamesCacheInitialized
= FALSE
;
70 * Deleter for TimeZoneNamesCacheEntry
72 static void U_CALLCONV
73 deleteTimeZoneNamesCacheEntry(void *obj
) {
74 icu::TimeZoneNamesCacheEntry
*entry
= (icu::TimeZoneNamesCacheEntry
*)obj
;
75 delete (icu::TimeZoneNamesImpl
*) entry
->names
;
81 * Function used for removing unreferrenced cache entries exceeding
82 * the expiration time. This function must be called with in the mutex
85 static void sweepCache() {
86 int32_t pos
= UHASH_FIRST
;
87 const UHashElement
* elem
;
88 double now
= (double)uprv_getUTCtime();
90 while ((elem
= uhash_nextElement(gTimeZoneNamesCache
, &pos
))) {
91 TimeZoneNamesCacheEntry
*entry
= (TimeZoneNamesCacheEntry
*)elem
->value
.pointer
;
92 if (entry
->refCount
<= 0 && (now
- entry
->lastAccess
) > CACHE_EXPIRATION
) {
94 uhash_removeElement(gTimeZoneNamesCache
, elem
);
99 // ---------------------------------------------------
100 // TimeZoneNamesDelegate
101 // ---------------------------------------------------
102 class TimeZoneNamesDelegate
: public TimeZoneNames
{
104 TimeZoneNamesDelegate(const Locale
& locale
, UErrorCode
& status
);
105 virtual ~TimeZoneNamesDelegate();
107 virtual UBool
operator==(const TimeZoneNames
& other
) const;
108 virtual UBool
operator!=(const TimeZoneNames
& other
) const {return !operator==(other
);};
109 virtual TimeZoneNames
* clone() const;
111 StringEnumeration
* getAvailableMetaZoneIDs(UErrorCode
& status
) const;
112 StringEnumeration
* getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) const;
113 UnicodeString
& getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) const;
114 UnicodeString
& getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) const;
116 UnicodeString
& getMetaZoneDisplayName(const UnicodeString
& mzID
, UTimeZoneNameType type
, UnicodeString
& name
) const;
117 UnicodeString
& getTimeZoneDisplayName(const UnicodeString
& tzID
, UTimeZoneNameType type
, UnicodeString
& name
) const;
119 UnicodeString
& getExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) const;
121 void loadAllDisplayNames(UErrorCode
& status
);
122 void getDisplayNames(const UnicodeString
& tzID
, const UTimeZoneNameType types
[], int32_t numTypes
, UDate date
, UnicodeString dest
[], UErrorCode
& status
) const;
124 MatchInfoCollection
* find(const UnicodeString
& text
, int32_t start
, uint32_t types
, UErrorCode
& status
) const;
126 TimeZoneNamesDelegate();
127 TimeZoneNamesCacheEntry
* fTZnamesCacheEntry
;
130 TimeZoneNamesDelegate::TimeZoneNamesDelegate()
131 : fTZnamesCacheEntry(0) {
134 TimeZoneNamesDelegate::TimeZoneNamesDelegate(const Locale
& locale
, UErrorCode
& status
) {
135 Mutex
lock(&gTimeZoneNamesLock
);
136 if (!gTimeZoneNamesCacheInitialized
) {
137 // Create empty hashtable if it is not already initialized.
138 gTimeZoneNamesCache
= uhash_open(uhash_hashChars
, uhash_compareChars
, NULL
, &status
);
139 if (U_SUCCESS(status
)) {
140 uhash_setKeyDeleter(gTimeZoneNamesCache
, uprv_free
);
141 uhash_setValueDeleter(gTimeZoneNamesCache
, deleteTimeZoneNamesCacheEntry
);
142 gTimeZoneNamesCacheInitialized
= TRUE
;
143 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONENAMES
, timeZoneNames_cleanup
);
147 if (U_FAILURE(status
)) {
151 // Check the cache, if not available, create new one and cache
152 TimeZoneNamesCacheEntry
*cacheEntry
= NULL
;
154 const char *key
= locale
.getName();
155 cacheEntry
= (TimeZoneNamesCacheEntry
*)uhash_get(gTimeZoneNamesCache
, key
);
156 if (cacheEntry
== NULL
) {
157 TimeZoneNames
*tznames
= NULL
;
160 tznames
= new TimeZoneNamesImpl(locale
, status
);
161 if (tznames
== NULL
) {
162 status
= U_MEMORY_ALLOCATION_ERROR
;
164 if (U_SUCCESS(status
)) {
165 newKey
= (char *)uprv_malloc(uprv_strlen(key
) + 1);
166 if (newKey
== NULL
) {
167 status
= U_MEMORY_ALLOCATION_ERROR
;
169 uprv_strcpy(newKey
, key
);
172 if (U_SUCCESS(status
)) {
173 cacheEntry
= (TimeZoneNamesCacheEntry
*)uprv_malloc(sizeof(TimeZoneNamesCacheEntry
));
174 if (cacheEntry
== NULL
) {
175 status
= U_MEMORY_ALLOCATION_ERROR
;
177 cacheEntry
->names
= tznames
;
178 cacheEntry
->refCount
= 1;
179 cacheEntry
->lastAccess
= (double)uprv_getUTCtime();
181 uhash_put(gTimeZoneNamesCache
, newKey
, cacheEntry
, &status
);
184 if (U_FAILURE(status
)) {
185 if (tznames
!= NULL
) {
188 if (newKey
!= NULL
) {
191 if (cacheEntry
!= NULL
) {
192 uprv_free(cacheEntry
);
197 // Update the reference count
198 cacheEntry
->refCount
++;
199 cacheEntry
->lastAccess
= (double)uprv_getUTCtime();
202 if (gAccessCount
>= SWEEP_INTERVAL
) {
207 fTZnamesCacheEntry
= cacheEntry
;
210 TimeZoneNamesDelegate::~TimeZoneNamesDelegate() {
211 umtx_lock(&gTimeZoneNamesLock
);
213 if (fTZnamesCacheEntry
) {
214 U_ASSERT(fTZnamesCacheEntry
->refCount
> 0);
215 // Just decrement the reference count
216 fTZnamesCacheEntry
->refCount
--;
219 umtx_unlock(&gTimeZoneNamesLock
);
223 TimeZoneNamesDelegate::operator==(const TimeZoneNames
& other
) const {
224 if (this == &other
) {
227 // Just compare if the other object also use the same
229 const TimeZoneNamesDelegate
* rhs
= dynamic_cast<const TimeZoneNamesDelegate
*>(&other
);
231 return fTZnamesCacheEntry
== rhs
->fTZnamesCacheEntry
;
237 TimeZoneNamesDelegate::clone() const {
238 TimeZoneNamesDelegate
* other
= new TimeZoneNamesDelegate();
240 umtx_lock(&gTimeZoneNamesLock
);
242 // Just increment the reference count
243 fTZnamesCacheEntry
->refCount
++;
244 other
->fTZnamesCacheEntry
= fTZnamesCacheEntry
;
246 umtx_unlock(&gTimeZoneNamesLock
);
252 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(UErrorCode
& status
) const {
253 return fTZnamesCacheEntry
->names
->getAvailableMetaZoneIDs(status
);
257 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) const {
258 return fTZnamesCacheEntry
->names
->getAvailableMetaZoneIDs(tzID
, status
);
262 TimeZoneNamesDelegate::getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) const {
263 return fTZnamesCacheEntry
->names
->getMetaZoneID(tzID
, date
, mzID
);
267 TimeZoneNamesDelegate::getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) const {
268 return fTZnamesCacheEntry
->names
->getReferenceZoneID(mzID
, region
, tzID
);
272 TimeZoneNamesDelegate::getMetaZoneDisplayName(const UnicodeString
& mzID
, UTimeZoneNameType type
, UnicodeString
& name
) const {
273 return fTZnamesCacheEntry
->names
->getMetaZoneDisplayName(mzID
, type
, name
);
277 TimeZoneNamesDelegate::getTimeZoneDisplayName(const UnicodeString
& tzID
, UTimeZoneNameType type
, UnicodeString
& name
) const {
278 return fTZnamesCacheEntry
->names
->getTimeZoneDisplayName(tzID
, type
, name
);
282 TimeZoneNamesDelegate::getExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) const {
283 return fTZnamesCacheEntry
->names
->getExemplarLocationName(tzID
, name
);
287 TimeZoneNamesDelegate::loadAllDisplayNames(UErrorCode
& status
) {
288 fTZnamesCacheEntry
->names
->loadAllDisplayNames(status
);
292 TimeZoneNamesDelegate::getDisplayNames(const UnicodeString
& tzID
, const UTimeZoneNameType types
[], int32_t numTypes
, UDate date
, UnicodeString dest
[], UErrorCode
& status
) const {
293 fTZnamesCacheEntry
->names
->getDisplayNames(tzID
, types
, numTypes
, date
, dest
, status
);
296 TimeZoneNames::MatchInfoCollection
*
297 TimeZoneNamesDelegate::find(const UnicodeString
& text
, int32_t start
, uint32_t types
, UErrorCode
& status
) const {
298 return fTZnamesCacheEntry
->names
->find(text
, start
, types
, status
);
301 // ---------------------------------------------------
302 // TimeZoneNames base class
303 // ---------------------------------------------------
304 TimeZoneNames::~TimeZoneNames() {
308 TimeZoneNames::createInstance(const Locale
& locale
, UErrorCode
& status
) {
309 TimeZoneNames
*instance
= NULL
;
310 if (U_SUCCESS(status
)) {
311 instance
= new TimeZoneNamesDelegate(locale
, status
);
312 if (instance
== NULL
&& U_SUCCESS(status
)) {
313 status
= U_MEMORY_ALLOCATION_ERROR
;
320 TimeZoneNames::createTZDBInstance(const Locale
& locale
, UErrorCode
& status
) {
321 TimeZoneNames
*instance
= NULL
;
322 if (U_SUCCESS(status
)) {
323 instance
= new TZDBTimeZoneNames(locale
);
324 if (instance
== NULL
&& U_SUCCESS(status
)) {
325 status
= U_MEMORY_ALLOCATION_ERROR
;
332 TimeZoneNames::getExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) const {
333 return TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID
, name
);
337 TimeZoneNames::getDisplayName(const UnicodeString
& tzID
, UTimeZoneNameType type
, UDate date
, UnicodeString
& name
) const {
338 getTimeZoneDisplayName(tzID
, type
, name
);
339 if (name
.isEmpty()) {
341 UnicodeString
mzID(mzIDBuf
, 0, UPRV_LENGTHOF(mzIDBuf
));
342 getMetaZoneID(tzID
, date
, mzID
);
343 getMetaZoneDisplayName(mzID
, type
, name
);
348 // Empty default implementation, to be overriden in tznames_impl.cpp.
350 TimeZoneNames::loadAllDisplayNames(UErrorCode
& /*status*/) {
353 // A default, lightweight implementation of getDisplayNames.
354 // Overridden in tznames_impl.cpp.
356 TimeZoneNames::getDisplayNames(const UnicodeString
& tzID
, const UTimeZoneNameType types
[], int32_t numTypes
, UDate date
, UnicodeString dest
[], UErrorCode
& status
) const {
357 if (U_FAILURE(status
)) { return; }
358 if (tzID
.isEmpty()) { return; }
360 for (int i
= 0; i
< numTypes
; i
++) {
361 getTimeZoneDisplayName(tzID
, types
[i
], dest
[i
]);
362 if (dest
[i
].isEmpty()) {
363 if (mzID
.isEmpty()) {
364 getMetaZoneID(tzID
, date
, mzID
);
366 getMetaZoneDisplayName(mzID
, types
[i
], dest
[i
]);
372 struct MatchInfo
: UMemory
{
373 UTimeZoneNameType nameType
;
378 MatchInfo(UTimeZoneNameType nameType
, int32_t matchLength
, const UnicodeString
* tzID
, const UnicodeString
* mzID
) {
379 this->nameType
= nameType
;
380 this->matchLength
= matchLength
;
382 this->id
.setTo(*tzID
);
385 this->id
.setTo(*mzID
);
386 this->isTZID
= FALSE
;
392 static void U_CALLCONV
393 deleteMatchInfo(void *obj
) {
394 delete static_cast<MatchInfo
*>(obj
);
398 // ---------------------------------------------------
399 // MatchInfoCollection class
400 // ---------------------------------------------------
401 TimeZoneNames::MatchInfoCollection::MatchInfoCollection()
405 TimeZoneNames::MatchInfoCollection::~MatchInfoCollection() {
406 if (fMatches
!= NULL
) {
412 TimeZoneNames::MatchInfoCollection::addZone(UTimeZoneNameType nameType
, int32_t matchLength
,
413 const UnicodeString
& tzID
, UErrorCode
& status
) {
414 if (U_FAILURE(status
)) {
417 MatchInfo
* matchInfo
= new MatchInfo(nameType
, matchLength
, &tzID
, NULL
);
418 if (matchInfo
== NULL
) {
419 status
= U_MEMORY_ALLOCATION_ERROR
;
422 matches(status
)->addElement(matchInfo
, status
);
423 if (U_FAILURE(status
)) {
429 TimeZoneNames::MatchInfoCollection::addMetaZone(UTimeZoneNameType nameType
, int32_t matchLength
,
430 const UnicodeString
& mzID
, UErrorCode
& status
) {
431 if (U_FAILURE(status
)) {
434 MatchInfo
* matchInfo
= new MatchInfo(nameType
, matchLength
, NULL
, &mzID
);
435 if (matchInfo
== NULL
) {
436 status
= U_MEMORY_ALLOCATION_ERROR
;
439 matches(status
)->addElement(matchInfo
, status
);
440 if (U_FAILURE(status
)) {
446 TimeZoneNames::MatchInfoCollection::size() const {
447 if (fMatches
== NULL
) {
450 return fMatches
->size();
454 TimeZoneNames::MatchInfoCollection::getNameTypeAt(int32_t idx
) const {
455 const MatchInfo
* match
= (const MatchInfo
*)fMatches
->elementAt(idx
);
457 return match
->nameType
;
459 return UTZNM_UNKNOWN
;
463 TimeZoneNames::MatchInfoCollection::getMatchLengthAt(int32_t idx
) const {
464 const MatchInfo
* match
= (const MatchInfo
*)fMatches
->elementAt(idx
);
466 return match
->matchLength
;
472 TimeZoneNames::MatchInfoCollection::getTimeZoneIDAt(int32_t idx
, UnicodeString
& tzID
) const {
474 const MatchInfo
* match
= (const MatchInfo
*)fMatches
->elementAt(idx
);
475 if (match
&& match
->isTZID
) {
476 tzID
.setTo(match
->id
);
483 TimeZoneNames::MatchInfoCollection::getMetaZoneIDAt(int32_t idx
, UnicodeString
& mzID
) const {
485 const MatchInfo
* match
= (const MatchInfo
*)fMatches
->elementAt(idx
);
486 if (match
&& !match
->isTZID
) {
487 mzID
.setTo(match
->id
);
494 TimeZoneNames::MatchInfoCollection::matches(UErrorCode
& status
) {
495 if (U_FAILURE(status
)) {
498 if (fMatches
!= NULL
) {
501 fMatches
= new UVector(deleteMatchInfo
, NULL
, status
);
502 if (fMatches
== NULL
) {
503 status
= U_MEMORY_ALLOCATION_ERROR
;
504 } else if (U_FAILURE(status
)) {