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() {
33 static UMutex
*m
= STATIC_NEW(UMutex
);
36 static UHashtable
*gTimeZoneNamesCache
= NULL
;
37 static UBool gTimeZoneNamesCacheInitialized
= FALSE
;
39 // Access count - incremented every time up to SWEEP_INTERVAL,
41 static int32_t gAccessCount
= 0;
43 // Interval for calling the cache sweep function - every 100 times
44 #define SWEEP_INTERVAL 100
46 // Cache expiration in millisecond. When a cached entry is no
47 // longer referenced and exceeding this threshold since last
48 // access time, then the cache entry will be deleted by the sweep
49 // function. For now, 3 minutes.
50 #define CACHE_EXPIRATION 180000.0
52 typedef struct TimeZoneNamesCacheEntry
{
56 } TimeZoneNamesCacheEntry
;
60 * Cleanup callback func
62 static UBool U_CALLCONV
timeZoneNames_cleanup(void)
64 if (gTimeZoneNamesCache
!= NULL
) {
65 uhash_close(gTimeZoneNamesCache
);
66 gTimeZoneNamesCache
= NULL
;
68 gTimeZoneNamesCacheInitialized
= FALSE
;
73 * Deleter for TimeZoneNamesCacheEntry
75 static void U_CALLCONV
76 deleteTimeZoneNamesCacheEntry(void *obj
) {
77 icu::TimeZoneNamesCacheEntry
*entry
= (icu::TimeZoneNamesCacheEntry
*)obj
;
78 delete (icu::TimeZoneNamesImpl
*) entry
->names
;
84 * Function used for removing unreferrenced cache entries exceeding
85 * the expiration time. This function must be called with in the mutex
88 static void sweepCache() {
89 int32_t pos
= UHASH_FIRST
;
90 const UHashElement
* elem
;
91 double now
= (double)uprv_getUTCtime();
93 while ((elem
= uhash_nextElement(gTimeZoneNamesCache
, &pos
)) != 0) {
94 TimeZoneNamesCacheEntry
*entry
= (TimeZoneNamesCacheEntry
*)elem
->value
.pointer
;
95 if (entry
->refCount
<= 0 && (now
- entry
->lastAccess
) > CACHE_EXPIRATION
) {
97 uhash_removeElement(gTimeZoneNamesCache
, elem
);
102 // ---------------------------------------------------
103 // TimeZoneNamesDelegate
104 // ---------------------------------------------------
105 class TimeZoneNamesDelegate
: public TimeZoneNames
{
107 TimeZoneNamesDelegate(const Locale
& locale
, UErrorCode
& status
);
108 virtual ~TimeZoneNamesDelegate();
110 virtual UBool
operator==(const TimeZoneNames
& other
) const;
111 virtual UBool
operator!=(const TimeZoneNames
& other
) const {return !operator==(other
);}
112 virtual TimeZoneNames
* clone() const;
114 StringEnumeration
* getAvailableMetaZoneIDs(UErrorCode
& status
) const;
115 StringEnumeration
* getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) const;
116 UnicodeString
& getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) const;
117 UnicodeString
& getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) const;
119 UnicodeString
& getMetaZoneDisplayName(const UnicodeString
& mzID
, UTimeZoneNameType type
, UnicodeString
& name
) const;
120 UnicodeString
& getTimeZoneDisplayName(const UnicodeString
& tzID
, UTimeZoneNameType type
, UnicodeString
& name
) const;
122 UnicodeString
& getExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) const;
124 void loadAllDisplayNames(UErrorCode
& status
);
125 void getDisplayNames(const UnicodeString
& tzID
, const UTimeZoneNameType types
[], int32_t numTypes
, UDate date
, UnicodeString dest
[], UErrorCode
& status
) const;
127 MatchInfoCollection
* find(const UnicodeString
& text
, int32_t start
, uint32_t types
, UErrorCode
& status
) const;
129 TimeZoneNamesDelegate();
130 TimeZoneNamesCacheEntry
* fTZnamesCacheEntry
;
133 TimeZoneNamesDelegate::TimeZoneNamesDelegate()
134 : fTZnamesCacheEntry(0) {
137 TimeZoneNamesDelegate::TimeZoneNamesDelegate(const Locale
& locale
, UErrorCode
& status
) {
138 Mutex
lock(gTimeZoneNamesLock());
139 if (!gTimeZoneNamesCacheInitialized
) {
140 // Create empty hashtable if it is not already initialized.
141 gTimeZoneNamesCache
= uhash_open(uhash_hashChars
, uhash_compareChars
, NULL
, &status
);
142 if (U_SUCCESS(status
)) {
143 uhash_setKeyDeleter(gTimeZoneNamesCache
, uprv_free
);
144 uhash_setValueDeleter(gTimeZoneNamesCache
, deleteTimeZoneNamesCacheEntry
);
145 gTimeZoneNamesCacheInitialized
= TRUE
;
146 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONENAMES
, timeZoneNames_cleanup
);
150 if (U_FAILURE(status
)) {
154 // Check the cache, if not available, create new one and cache
155 TimeZoneNamesCacheEntry
*cacheEntry
= NULL
;
157 const char *key
= locale
.getName();
158 cacheEntry
= (TimeZoneNamesCacheEntry
*)uhash_get(gTimeZoneNamesCache
, key
);
159 if (cacheEntry
== NULL
) {
160 TimeZoneNames
*tznames
= NULL
;
163 tznames
= new TimeZoneNamesImpl(locale
, status
);
164 if (tznames
== NULL
) {
165 status
= U_MEMORY_ALLOCATION_ERROR
;
167 if (U_SUCCESS(status
)) {
168 newKey
= (char *)uprv_malloc(uprv_strlen(key
) + 1);
169 if (newKey
== NULL
) {
170 status
= U_MEMORY_ALLOCATION_ERROR
;
172 uprv_strcpy(newKey
, key
);
175 if (U_SUCCESS(status
)) {
176 cacheEntry
= (TimeZoneNamesCacheEntry
*)uprv_malloc(sizeof(TimeZoneNamesCacheEntry
));
177 if (cacheEntry
== NULL
) {
178 status
= U_MEMORY_ALLOCATION_ERROR
;
180 cacheEntry
->names
= tznames
;
181 cacheEntry
->refCount
= 1;
182 cacheEntry
->lastAccess
= (double)uprv_getUTCtime();
184 uhash_put(gTimeZoneNamesCache
, newKey
, cacheEntry
, &status
);
187 if (U_FAILURE(status
)) {
188 if (tznames
!= NULL
) {
191 if (newKey
!= NULL
) {
194 if (cacheEntry
!= NULL
) {
195 uprv_free(cacheEntry
);
200 // Update the reference count
201 cacheEntry
->refCount
++;
202 cacheEntry
->lastAccess
= (double)uprv_getUTCtime();
205 if (gAccessCount
>= SWEEP_INTERVAL
) {
210 fTZnamesCacheEntry
= cacheEntry
;
213 TimeZoneNamesDelegate::~TimeZoneNamesDelegate() {
214 umtx_lock(gTimeZoneNamesLock());
216 if (fTZnamesCacheEntry
) {
217 U_ASSERT(fTZnamesCacheEntry
->refCount
> 0);
218 // Just decrement the reference count
219 fTZnamesCacheEntry
->refCount
--;
222 umtx_unlock(gTimeZoneNamesLock());
226 TimeZoneNamesDelegate::operator==(const TimeZoneNames
& other
) const {
227 if (this == &other
) {
230 // Just compare if the other object also use the same
232 const TimeZoneNamesDelegate
* rhs
= dynamic_cast<const TimeZoneNamesDelegate
*>(&other
);
234 return fTZnamesCacheEntry
== rhs
->fTZnamesCacheEntry
;
240 TimeZoneNamesDelegate::clone() const {
241 TimeZoneNamesDelegate
* other
= new TimeZoneNamesDelegate();
243 umtx_lock(gTimeZoneNamesLock());
245 // Just increment the reference count
246 fTZnamesCacheEntry
->refCount
++;
247 other
->fTZnamesCacheEntry
= fTZnamesCacheEntry
;
249 umtx_unlock(gTimeZoneNamesLock());
255 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(UErrorCode
& status
) const {
256 return fTZnamesCacheEntry
->names
->getAvailableMetaZoneIDs(status
);
260 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(const UnicodeString
& tzID
, UErrorCode
& status
) const {
261 return fTZnamesCacheEntry
->names
->getAvailableMetaZoneIDs(tzID
, status
);
265 TimeZoneNamesDelegate::getMetaZoneID(const UnicodeString
& tzID
, UDate date
, UnicodeString
& mzID
) const {
266 return fTZnamesCacheEntry
->names
->getMetaZoneID(tzID
, date
, mzID
);
270 TimeZoneNamesDelegate::getReferenceZoneID(const UnicodeString
& mzID
, const char* region
, UnicodeString
& tzID
) const {
271 return fTZnamesCacheEntry
->names
->getReferenceZoneID(mzID
, region
, tzID
);
275 TimeZoneNamesDelegate::getMetaZoneDisplayName(const UnicodeString
& mzID
, UTimeZoneNameType type
, UnicodeString
& name
) const {
276 return fTZnamesCacheEntry
->names
->getMetaZoneDisplayName(mzID
, type
, name
);
280 TimeZoneNamesDelegate::getTimeZoneDisplayName(const UnicodeString
& tzID
, UTimeZoneNameType type
, UnicodeString
& name
) const {
281 return fTZnamesCacheEntry
->names
->getTimeZoneDisplayName(tzID
, type
, name
);
285 TimeZoneNamesDelegate::getExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) const {
286 return fTZnamesCacheEntry
->names
->getExemplarLocationName(tzID
, name
);
290 TimeZoneNamesDelegate::loadAllDisplayNames(UErrorCode
& status
) {
291 fTZnamesCacheEntry
->names
->loadAllDisplayNames(status
);
295 TimeZoneNamesDelegate::getDisplayNames(const UnicodeString
& tzID
, const UTimeZoneNameType types
[], int32_t numTypes
, UDate date
, UnicodeString dest
[], UErrorCode
& status
) const {
296 fTZnamesCacheEntry
->names
->getDisplayNames(tzID
, types
, numTypes
, date
, dest
, status
);
299 TimeZoneNames::MatchInfoCollection
*
300 TimeZoneNamesDelegate::find(const UnicodeString
& text
, int32_t start
, uint32_t types
, UErrorCode
& status
) const {
301 return fTZnamesCacheEntry
->names
->find(text
, start
, types
, status
);
304 // ---------------------------------------------------
305 // TimeZoneNames base class
306 // ---------------------------------------------------
307 TimeZoneNames::~TimeZoneNames() {
311 TimeZoneNames::createInstance(const Locale
& locale
, UErrorCode
& status
) {
312 TimeZoneNames
*instance
= NULL
;
313 if (U_SUCCESS(status
)) {
314 instance
= new TimeZoneNamesDelegate(locale
, status
);
315 if (instance
== NULL
&& U_SUCCESS(status
)) {
316 status
= U_MEMORY_ALLOCATION_ERROR
;
323 TimeZoneNames::createTZDBInstance(const Locale
& locale
, UErrorCode
& status
) {
324 TimeZoneNames
*instance
= NULL
;
325 if (U_SUCCESS(status
)) {
326 instance
= new TZDBTimeZoneNames(locale
);
327 if (instance
== NULL
&& U_SUCCESS(status
)) {
328 status
= U_MEMORY_ALLOCATION_ERROR
;
335 TimeZoneNames::getExemplarLocationName(const UnicodeString
& tzID
, UnicodeString
& name
) const {
336 return TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID
, name
);
340 TimeZoneNames::getDisplayName(const UnicodeString
& tzID
, UTimeZoneNameType type
, UDate date
, UnicodeString
& name
) const {
341 getTimeZoneDisplayName(tzID
, type
, name
);
342 if (name
.isEmpty()) {
344 UnicodeString
mzID(mzIDBuf
, 0, UPRV_LENGTHOF(mzIDBuf
));
345 getMetaZoneID(tzID
, date
, mzID
);
346 getMetaZoneDisplayName(mzID
, type
, name
);
351 // Empty default implementation, to be overriden in tznames_impl.cpp.
353 TimeZoneNames::loadAllDisplayNames(UErrorCode
& /*status*/) {
356 // A default, lightweight implementation of getDisplayNames.
357 // Overridden in tznames_impl.cpp.
359 TimeZoneNames::getDisplayNames(const UnicodeString
& tzID
, const UTimeZoneNameType types
[], int32_t numTypes
, UDate date
, UnicodeString dest
[], UErrorCode
& status
) const {
360 if (U_FAILURE(status
)) { return; }
361 if (tzID
.isEmpty()) { return; }
363 for (int i
= 0; i
< numTypes
; i
++) {
364 getTimeZoneDisplayName(tzID
, types
[i
], dest
[i
]);
365 if (dest
[i
].isEmpty()) {
366 if (mzID
.isEmpty()) {
367 getMetaZoneID(tzID
, date
, mzID
);
369 getMetaZoneDisplayName(mzID
, types
[i
], dest
[i
]);
375 struct MatchInfo
: UMemory
{
376 UTimeZoneNameType nameType
;
381 MatchInfo(UTimeZoneNameType nameType
, int32_t matchLength
, const UnicodeString
* tzID
, const UnicodeString
* mzID
) {
382 this->nameType
= nameType
;
383 this->matchLength
= matchLength
;
385 this->id
.setTo(*tzID
);
388 this->id
.setTo(*mzID
);
389 this->isTZID
= FALSE
;
395 static void U_CALLCONV
396 deleteMatchInfo(void *obj
) {
397 delete static_cast<MatchInfo
*>(obj
);
401 // ---------------------------------------------------
402 // MatchInfoCollection class
403 // ---------------------------------------------------
404 TimeZoneNames::MatchInfoCollection::MatchInfoCollection()
408 TimeZoneNames::MatchInfoCollection::~MatchInfoCollection() {
409 if (fMatches
!= NULL
) {
415 TimeZoneNames::MatchInfoCollection::addZone(UTimeZoneNameType nameType
, int32_t matchLength
,
416 const UnicodeString
& tzID
, UErrorCode
& status
) {
417 if (U_FAILURE(status
)) {
420 MatchInfo
* matchInfo
= new MatchInfo(nameType
, matchLength
, &tzID
, NULL
);
421 if (matchInfo
== NULL
) {
422 status
= U_MEMORY_ALLOCATION_ERROR
;
425 matches(status
)->addElement(matchInfo
, status
);
426 if (U_FAILURE(status
)) {
432 TimeZoneNames::MatchInfoCollection::addMetaZone(UTimeZoneNameType nameType
, int32_t matchLength
,
433 const UnicodeString
& mzID
, UErrorCode
& status
) {
434 if (U_FAILURE(status
)) {
437 MatchInfo
* matchInfo
= new MatchInfo(nameType
, matchLength
, NULL
, &mzID
);
438 if (matchInfo
== NULL
) {
439 status
= U_MEMORY_ALLOCATION_ERROR
;
442 matches(status
)->addElement(matchInfo
, status
);
443 if (U_FAILURE(status
)) {
449 TimeZoneNames::MatchInfoCollection::size() const {
450 if (fMatches
== NULL
) {
453 return fMatches
->size();
457 TimeZoneNames::MatchInfoCollection::getNameTypeAt(int32_t idx
) const {
458 const MatchInfo
* match
= (const MatchInfo
*)fMatches
->elementAt(idx
);
460 return match
->nameType
;
462 return UTZNM_UNKNOWN
;
466 TimeZoneNames::MatchInfoCollection::getMatchLengthAt(int32_t idx
) const {
467 const MatchInfo
* match
= (const MatchInfo
*)fMatches
->elementAt(idx
);
469 return match
->matchLength
;
475 TimeZoneNames::MatchInfoCollection::getTimeZoneIDAt(int32_t idx
, UnicodeString
& tzID
) const {
477 const MatchInfo
* match
= (const MatchInfo
*)fMatches
->elementAt(idx
);
478 if (match
&& match
->isTZID
) {
479 tzID
.setTo(match
->id
);
486 TimeZoneNames::MatchInfoCollection::getMetaZoneIDAt(int32_t idx
, UnicodeString
& mzID
) const {
488 const MatchInfo
* match
= (const MatchInfo
*)fMatches
->elementAt(idx
);
489 if (match
&& !match
->isTZID
) {
490 mzID
.setTo(match
->id
);
497 TimeZoneNames::MatchInfoCollection::matches(UErrorCode
& status
) {
498 if (U_FAILURE(status
)) {
501 if (fMatches
!= NULL
) {
504 fMatches
= new UVector(deleteMatchInfo
, NULL
, status
);
505 if (fMatches
== NULL
) {
506 status
= U_MEMORY_ALLOCATION_ERROR
;
507 } else if (U_FAILURE(status
)) {