1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 2007-2014, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
10 #include "unicode/utypes.h"
12 #if !UCONFIG_NO_FORMATTING
16 #include "unicode/timezone.h"
17 #include "unicode/ustring.h"
18 #include "unicode/putil.h"
19 #include "unicode/simpletz.h"
20 #include "unicode/strenum.h"
33 static UMutex gZoneMetaLock
= U_MUTEX_INITIALIZER
;
35 // CLDR Canonical ID mapping table
36 static UHashtable
*gCanonicalIDCache
= NULL
;
37 static icu::UInitOnce gCanonicalIDCacheInitOnce
= U_INITONCE_INITIALIZER
;
39 // Metazone mapping table
40 static UHashtable
*gOlsonToMeta
= NULL
;
41 static icu::UInitOnce gOlsonToMetaInitOnce
= U_INITONCE_INITIALIZER
;
43 // Available metazone IDs vector and table
44 static icu::UVector
*gMetaZoneIDs
= NULL
;
45 static UHashtable
*gMetaZoneIDTable
= NULL
;
46 static icu::UInitOnce gMetaZoneIDsInitOnce
= U_INITONCE_INITIALIZER
;
48 // Country info vectors
49 static icu::UVector
*gSingleZoneCountries
= NULL
;
50 static icu::UVector
*gMultiZonesCountries
= NULL
;
51 static icu::UInitOnce gCountryInfoVectorsInitOnce
= U_INITONCE_INITIALIZER
;
56 * Cleanup callback func
58 static UBool U_CALLCONV
zoneMeta_cleanup(void)
60 if (gCanonicalIDCache
!= NULL
) {
61 uhash_close(gCanonicalIDCache
);
62 gCanonicalIDCache
= NULL
;
64 gCanonicalIDCacheInitOnce
.reset();
66 if (gOlsonToMeta
!= NULL
) {
67 uhash_close(gOlsonToMeta
);
70 gOlsonToMetaInitOnce
.reset();
72 if (gMetaZoneIDTable
!= NULL
) {
73 uhash_close(gMetaZoneIDTable
);
74 gMetaZoneIDTable
= NULL
;
76 // delete after closing gMetaZoneIDTable, because it holds
77 // value objects held by the hashtable
80 gMetaZoneIDsInitOnce
.reset();
82 delete gSingleZoneCountries
;
83 gSingleZoneCountries
= NULL
;
84 delete gMultiZonesCountries
;
85 gMultiZonesCountries
= NULL
;
86 gCountryInfoVectorsInitOnce
.reset();
92 * Deleter for UChar* string
94 static void U_CALLCONV
95 deleteUCharString(void *obj
) {
96 UChar
*entry
= (UChar
*)obj
;
101 * Deleter for UVector
103 static void U_CALLCONV
104 deleteUVector(void *obj
) {
105 delete (icu::UVector
*) obj
;
109 * Deleter for OlsonToMetaMappingEntry
111 static void U_CALLCONV
112 deleteOlsonToMetaMappingEntry(void *obj
) {
113 icu::OlsonToMetaMappingEntry
*entry
= (icu::OlsonToMetaMappingEntry
*)obj
;
121 #define ZID_KEY_MAX 128
123 static const char gMetaZones
[] = "metaZones";
124 static const char gMetazoneInfo
[] = "metazoneInfo";
125 static const char gMapTimezonesTag
[] = "mapTimezones";
127 static const char gKeyTypeData
[] = "keyTypeData";
128 static const char gTypeAliasTag
[] = "typeAlias";
129 static const char gTypeMapTag
[] = "typeMap";
130 static const char gTimezoneTag
[] = "timezone";
132 static const char gPrimaryZonesTag
[] = "primaryZones";
134 static const char gWorldTag
[] = "001";
136 static const UChar gWorld
[] = {0x30, 0x30, 0x31, 0x00}; // "001"
138 static const UChar gDefaultFrom
[] = {0x31, 0x39, 0x37, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31,
139 0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x00}; // "1970-01-01 00:00"
140 static const UChar gDefaultTo
[] = {0x39, 0x39, 0x39, 0x39, 0x2D, 0x31, 0x32, 0x2D, 0x33, 0x31,
141 0x20, 0x32, 0x33, 0x3A, 0x35, 0x39, 0x00}; // "9999-12-31 23:59"
143 static const UChar gCustomTzPrefix
[] = {0x47, 0x4D, 0x54, 0}; // "GMT"
145 #define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1)
148 * Convert a date string used by metazone mappings to UDate.
149 * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
152 parseDate (const UChar
*text
, UErrorCode
&status
) {
153 if (U_FAILURE(status
)) {
156 int32_t len
= u_strlen(text
);
157 if (len
!= 16 && len
!= 10) {
158 // It must be yyyy-MM-dd HH:mm (length 16) or yyyy-MM-dd (length 10)
159 status
= U_INVALID_FORMAT_ERROR
;
163 int32_t year
= 0, month
= 0, day
= 0, hour
= 0, min
= 0, n
;
167 for (idx
= 0; idx
<= 3 && U_SUCCESS(status
); idx
++) {
168 n
= ASCII_DIGIT((int32_t)text
[idx
]);
172 status
= U_INVALID_FORMAT_ERROR
;
176 for (idx
= 5; idx
<= 6 && U_SUCCESS(status
); idx
++) {
177 n
= ASCII_DIGIT((int32_t)text
[idx
]);
179 month
= 10*month
+ n
;
181 status
= U_INVALID_FORMAT_ERROR
;
185 for (idx
= 8; idx
<= 9 && U_SUCCESS(status
); idx
++) {
186 n
= ASCII_DIGIT((int32_t)text
[idx
]);
190 status
= U_INVALID_FORMAT_ERROR
;
195 for (idx
= 11; idx
<= 12 && U_SUCCESS(status
); idx
++) {
196 n
= ASCII_DIGIT((int32_t)text
[idx
]);
200 status
= U_INVALID_FORMAT_ERROR
;
204 for (idx
= 14; idx
<= 15 && U_SUCCESS(status
); idx
++) {
205 n
= ASCII_DIGIT((int32_t)text
[idx
]);
209 status
= U_INVALID_FORMAT_ERROR
;
214 if (U_SUCCESS(status
)) {
215 UDate date
= Grego::fieldsToDay(year
, month
- 1, day
) * U_MILLIS_PER_DAY
216 + hour
* U_MILLIS_PER_HOUR
+ min
* U_MILLIS_PER_MINUTE
;
222 static void U_CALLCONV
initCanonicalIDCache(UErrorCode
&status
) {
223 gCanonicalIDCache
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
224 if (gCanonicalIDCache
== NULL
) {
225 status
= U_MEMORY_ALLOCATION_ERROR
;
227 if (U_FAILURE(status
)) {
228 gCanonicalIDCache
= NULL
;
230 // No key/value deleters - keys/values are from a resource bundle
231 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
235 const UChar
* U_EXPORT2
236 ZoneMeta::getCanonicalCLDRID(const UnicodeString
&tzid
, UErrorCode
& status
) {
237 if (U_FAILURE(status
)) {
241 if (tzid
.isBogus() || tzid
.length() > ZID_KEY_MAX
) {
242 status
= U_ILLEGAL_ARGUMENT_ERROR
;
246 // Checking the cached results
247 umtx_initOnce(gCanonicalIDCacheInitOnce
, &initCanonicalIDCache
, status
);
248 if (U_FAILURE(status
)) {
252 const UChar
*canonicalID
= NULL
;
254 UErrorCode tmpStatus
= U_ZERO_ERROR
;
255 UChar utzid
[ZID_KEY_MAX
+ 1];
256 tzid
.extract(utzid
, ZID_KEY_MAX
+ 1, tmpStatus
);
257 U_ASSERT(tmpStatus
== U_ZERO_ERROR
); // we checked the length of tzid already
259 if (!uprv_isInvariantUString(utzid
, -1)) {
260 // All of known tz IDs are only containing ASCII invariant characters.
261 status
= U_ILLEGAL_ARGUMENT_ERROR
;
265 // Check if it was already cached
266 umtx_lock(&gZoneMetaLock
);
268 canonicalID
= (const UChar
*)uhash_get(gCanonicalIDCache
, utzid
);
270 umtx_unlock(&gZoneMetaLock
);
272 if (canonicalID
!= NULL
) {
276 // If not, resolve CLDR canonical ID with resource data
277 UBool isInputCanonical
= FALSE
;
278 char id
[ZID_KEY_MAX
+ 1];
279 tzid
.extract(0, 0x7fffffff, id
, UPRV_LENGTHOF(id
), US_INV
);
281 // replace '/' with ':'
289 UResourceBundle
*top
= ures_openDirect(NULL
, gKeyTypeData
, &tmpStatus
);
290 UResourceBundle
*rb
= ures_getByKey(top
, gTypeMapTag
, NULL
, &tmpStatus
);
291 ures_getByKey(rb
, gTimezoneTag
, rb
, &tmpStatus
);
292 ures_getByKey(rb
, id
, rb
, &tmpStatus
);
293 if (U_SUCCESS(tmpStatus
)) {
294 // type entry (canonical) found
295 // the input is the canonical ID. resolve to const UChar*
296 canonicalID
= TimeZone::findID(tzid
);
297 isInputCanonical
= TRUE
;
300 if (canonicalID
== NULL
) {
301 // If a map element not found, then look for an alias
302 tmpStatus
= U_ZERO_ERROR
;
303 ures_getByKey(top
, gTypeAliasTag
, rb
, &tmpStatus
);
304 ures_getByKey(rb
, gTimezoneTag
, rb
, &tmpStatus
);
305 const UChar
*canonical
= ures_getStringByKey(rb
,id
,NULL
,&tmpStatus
);
306 if (U_SUCCESS(tmpStatus
)) {
307 // canonical map found
308 canonicalID
= canonical
;
311 if (canonicalID
== NULL
) {
312 // Dereference the input ID using the tz data
313 const UChar
*derefer
= TimeZone::dereferOlsonLink(tzid
);
314 if (derefer
== NULL
) {
315 status
= U_ILLEGAL_ARGUMENT_ERROR
;
317 int32_t len
= u_strlen(derefer
);
318 u_UCharsToChars(derefer
,id
,len
);
319 id
[len
] = (char) 0; // Make sure it is null terminated.
321 // replace '/' with ':'
329 // If a dereference turned something up then look for an alias.
330 // rb still points to the alias table, so we don't have to go looking
332 tmpStatus
= U_ZERO_ERROR
;
333 canonical
= ures_getStringByKey(rb
,id
,NULL
,&tmpStatus
);
334 if (U_SUCCESS(tmpStatus
)) {
335 // canonical map for the dereferenced ID found
336 canonicalID
= canonical
;
338 canonicalID
= derefer
;
339 isInputCanonical
= TRUE
;
347 if (U_SUCCESS(status
)) {
348 U_ASSERT(canonicalID
!= NULL
); // canocanilD must be non-NULL here
350 // Put the resolved canonical ID to the cache
351 umtx_lock(&gZoneMetaLock
);
353 const UChar
* idInCache
= (const UChar
*)uhash_get(gCanonicalIDCache
, utzid
);
354 if (idInCache
== NULL
) {
355 const UChar
* key
= ZoneMeta::findTimeZoneID(tzid
);
356 U_ASSERT(key
!= NULL
);
358 idInCache
= (const UChar
*)uhash_put(gCanonicalIDCache
, (void *)key
, (void *)canonicalID
, &status
);
359 U_ASSERT(idInCache
== NULL
);
362 if (U_SUCCESS(status
) && isInputCanonical
) {
363 // Also put canonical ID itself into the cache if not exist
364 const UChar
*canonicalInCache
= (const UChar
*)uhash_get(gCanonicalIDCache
, canonicalID
);
365 if (canonicalInCache
== NULL
) {
366 canonicalInCache
= (const UChar
*)uhash_put(gCanonicalIDCache
, (void *)canonicalID
, (void *)canonicalID
, &status
);
367 U_ASSERT(canonicalInCache
== NULL
);
371 umtx_unlock(&gZoneMetaLock
);
377 UnicodeString
& U_EXPORT2
378 ZoneMeta::getCanonicalCLDRID(const UnicodeString
&tzid
, UnicodeString
&systemID
, UErrorCode
& status
) {
379 const UChar
*canonicalID
= getCanonicalCLDRID(tzid
, status
);
380 if (U_FAILURE(status
) || canonicalID
== NULL
) {
381 systemID
.setToBogus();
384 systemID
.setTo(TRUE
, canonicalID
, -1);
388 const UChar
* U_EXPORT2
389 ZoneMeta::getCanonicalCLDRID(const TimeZone
& tz
) {
390 if (dynamic_cast<const OlsonTimeZone
*>(&tz
) != NULL
) {
391 // short cut for OlsonTimeZone
392 const OlsonTimeZone
*otz
= (const OlsonTimeZone
*)&tz
;
393 return otz
->getCanonicalID();
395 UErrorCode status
= U_ZERO_ERROR
;
397 return getCanonicalCLDRID(tz
.getID(tzID
), status
);
400 static void U_CALLCONV
countryInfoVectorsInit(UErrorCode
&status
) {
401 // Create empty vectors
402 // No deleters for these UVectors, it's a reference to a resource bundle string.
403 gSingleZoneCountries
= new UVector(NULL
, uhash_compareUChars
, status
);
404 if (gSingleZoneCountries
== NULL
) {
405 status
= U_MEMORY_ALLOCATION_ERROR
;
407 gMultiZonesCountries
= new UVector(NULL
, uhash_compareUChars
, status
);
408 if (gMultiZonesCountries
== NULL
) {
409 status
= U_MEMORY_ALLOCATION_ERROR
;
412 if (U_FAILURE(status
)) {
413 delete gSingleZoneCountries
;
414 delete gMultiZonesCountries
;
415 gSingleZoneCountries
= NULL
;
416 gMultiZonesCountries
= NULL
;
418 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
422 UnicodeString
& U_EXPORT2
423 ZoneMeta::getCanonicalCountry(const UnicodeString
&tzid
, UnicodeString
&country
, UBool
*isPrimary
/* = NULL */) {
424 if (isPrimary
!= NULL
) {
428 const UChar
*region
= TimeZone::getRegion(tzid
);
429 if (region
!= NULL
&& u_strcmp(gWorld
, region
) != 0) {
430 country
.setTo(region
, -1);
432 country
.setToBogus();
436 if (isPrimary
!= NULL
) {
437 char regionBuf
[] = {0, 0, 0};
439 // Checking the cached results
440 UErrorCode status
= U_ZERO_ERROR
;
441 umtx_initOnce(gCountryInfoVectorsInitOnce
, &countryInfoVectorsInit
, status
);
442 if (U_FAILURE(status
)) {
446 // Check if it was already cached
447 UBool cached
= FALSE
;
448 UBool singleZone
= FALSE
;
449 umtx_lock(&gZoneMetaLock
);
451 singleZone
= cached
= gSingleZoneCountries
->contains((void*)region
);
453 cached
= gMultiZonesCountries
->contains((void*)region
);
456 umtx_unlock(&gZoneMetaLock
);
459 // We need to go through all zones associated with the region.
460 // This is relatively heavy operation.
462 U_ASSERT(u_strlen(region
) == 2);
464 u_UCharsToChars(region
, regionBuf
, 2);
466 StringEnumeration
*ids
= TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION
, regionBuf
, NULL
, status
);
467 int32_t idsLen
= ids
->count(status
);
468 if (U_SUCCESS(status
) && idsLen
== 1) {
469 // only the single zone is available for the region
475 umtx_lock(&gZoneMetaLock
);
477 UErrorCode ec
= U_ZERO_ERROR
;
479 if (!gSingleZoneCountries
->contains((void*)region
)) {
480 gSingleZoneCountries
->addElement((void*)region
, ec
);
483 if (!gMultiZonesCountries
->contains((void*)region
)) {
484 gMultiZonesCountries
->addElement((void*)region
, ec
);
488 umtx_unlock(&gZoneMetaLock
);
494 // Note: We may cache the primary zone map in future.
496 // Even a country has multiple zones, one of them might be
497 // dominant and treated as a primary zone
499 if (regionBuf
[0] == 0) {
500 u_UCharsToChars(region
, regionBuf
, 2);
503 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
504 ures_getByKey(rb
, gPrimaryZonesTag
, rb
, &status
);
505 const UChar
*primaryZone
= ures_getStringByKey(rb
, regionBuf
, &idLen
, &status
);
506 if (U_SUCCESS(status
)) {
507 if (tzid
.compare(primaryZone
, idLen
) == 0) {
510 // The given ID might not be a canonical ID
511 UnicodeString canonicalID
;
512 TimeZone::getCanonicalID(tzid
, canonicalID
, status
);
513 if (U_SUCCESS(status
) && canonicalID
.compare(primaryZone
, idLen
) == 0) {
525 UnicodeString
& U_EXPORT2
526 ZoneMeta::getMetazoneID(const UnicodeString
&tzid
, UDate date
, UnicodeString
&result
) {
528 const UVector
*mappings
= getMetazoneMappings(tzid
);
529 if (mappings
!= NULL
) {
530 for (int32_t i
= 0; i
< mappings
->size(); i
++) {
531 OlsonToMetaMappingEntry
*mzm
= (OlsonToMetaMappingEntry
*)mappings
->elementAt(i
);
532 if (mzm
->from
<= date
&& mzm
->to
> date
) {
533 result
.setTo(mzm
->mzid
, -1);
545 static void U_CALLCONV
olsonToMetaInit(UErrorCode
&status
) {
546 U_ASSERT(gOlsonToMeta
== NULL
);
547 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
548 gOlsonToMeta
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
549 if (U_FAILURE(status
)) {
552 uhash_setKeyDeleter(gOlsonToMeta
, deleteUCharString
);
553 uhash_setValueDeleter(gOlsonToMeta
, deleteUVector
);
558 const UVector
* U_EXPORT2
559 ZoneMeta::getMetazoneMappings(const UnicodeString
&tzid
) {
560 UErrorCode status
= U_ZERO_ERROR
;
561 UChar tzidUChars
[ZID_KEY_MAX
+ 1];
562 tzid
.extract(tzidUChars
, ZID_KEY_MAX
+ 1, status
);
563 if (U_FAILURE(status
) || status
== U_STRING_NOT_TERMINATED_WARNING
) {
567 umtx_initOnce(gOlsonToMetaInitOnce
, &olsonToMetaInit
, status
);
568 if (U_FAILURE(status
)) {
572 // get the mapping from cache
573 const UVector
*result
= NULL
;
575 umtx_lock(&gZoneMetaLock
);
577 result
= (UVector
*) uhash_get(gOlsonToMeta
, tzidUChars
);
579 umtx_unlock(&gZoneMetaLock
);
581 if (result
!= NULL
) {
585 // miss the cache - create new one
586 UVector
*tmpResult
= createMetazoneMappings(tzid
);
587 if (tmpResult
== NULL
) {
592 // put the new one into the cache
593 umtx_lock(&gZoneMetaLock
);
595 // make sure it's already created
596 result
= (UVector
*) uhash_get(gOlsonToMeta
, tzidUChars
);
597 if (result
== NULL
) {
598 // add the one just created
599 int32_t tzidLen
= tzid
.length() + 1;
600 UChar
*key
= (UChar
*)uprv_malloc(tzidLen
* sizeof(UChar
));
602 // memory allocation error.. just return NULL
606 tzid
.extract(key
, tzidLen
, status
);
607 uhash_put(gOlsonToMeta
, key
, tmpResult
, &status
);
608 if (U_FAILURE(status
)) {
609 // delete the mapping
617 // another thread already put the one
621 umtx_unlock(&gZoneMetaLock
);
627 ZoneMeta::createMetazoneMappings(const UnicodeString
&tzid
) {
628 UVector
*mzMappings
= NULL
;
629 UErrorCode status
= U_ZERO_ERROR
;
631 UnicodeString canonicalID
;
632 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
633 ures_getByKey(rb
, gMetazoneInfo
, rb
, &status
);
634 getCanonicalCLDRID(tzid
, canonicalID
, status
);
636 if (U_SUCCESS(status
)) {
637 char tzKey
[ZID_KEY_MAX
+ 1];
638 int32_t tzKeyLen
= canonicalID
.extract(0, canonicalID
.length(), tzKey
, sizeof(tzKey
), US_INV
);
641 // tzid keys are using ':' as separators
650 ures_getByKey(rb
, tzKey
, rb
, &status
);
652 if (U_SUCCESS(status
)) {
653 UResourceBundle
*mz
= NULL
;
654 while (ures_hasNext(rb
)) {
655 mz
= ures_getNextResource(rb
, mz
, &status
);
657 const UChar
*mz_name
= ures_getStringByIndex(mz
, 0, NULL
, &status
);
658 const UChar
*mz_from
= gDefaultFrom
;
659 const UChar
*mz_to
= gDefaultTo
;
661 if (ures_getSize(mz
) == 3) {
662 mz_from
= ures_getStringByIndex(mz
, 1, NULL
, &status
);
663 mz_to
= ures_getStringByIndex(mz
, 2, NULL
, &status
);
666 if(U_FAILURE(status
)){
667 status
= U_ZERO_ERROR
;
670 // We do not want to use SimpleDateformat to parse boundary dates,
671 // because this code could be triggered by the initialization code
672 // used by SimpleDateFormat.
673 UDate from
= parseDate(mz_from
, status
);
674 UDate to
= parseDate(mz_to
, status
);
675 if (U_FAILURE(status
)) {
676 status
= U_ZERO_ERROR
;
680 OlsonToMetaMappingEntry
*entry
= (OlsonToMetaMappingEntry
*)uprv_malloc(sizeof(OlsonToMetaMappingEntry
));
682 status
= U_MEMORY_ALLOCATION_ERROR
;
685 entry
->mzid
= mz_name
;
689 if (mzMappings
== NULL
) {
690 mzMappings
= new UVector(deleteOlsonToMetaMappingEntry
, NULL
, status
);
691 if (U_FAILURE(status
)) {
699 mzMappings
->addElement(entry
, status
);
700 if (U_FAILURE(status
)) {
705 if (U_FAILURE(status
)) {
706 if (mzMappings
!= NULL
) {
717 UnicodeString
& U_EXPORT2
718 ZoneMeta::getZoneIdByMetazone(const UnicodeString
&mzid
, const UnicodeString
®ion
, UnicodeString
&result
) {
719 UErrorCode status
= U_ZERO_ERROR
;
720 const UChar
*tzid
= NULL
;
722 char keyBuf
[ZID_KEY_MAX
+ 1];
725 if (mzid
.isBogus() || mzid
.length() > ZID_KEY_MAX
) {
730 keyLen
= mzid
.extract(0, mzid
.length(), keyBuf
, ZID_KEY_MAX
+ 1, US_INV
);
733 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
734 ures_getByKey(rb
, gMapTimezonesTag
, rb
, &status
);
735 ures_getByKey(rb
, keyBuf
, rb
, &status
);
737 if (U_SUCCESS(status
)) {
738 // check region mapping
739 if (region
.length() == 2 || region
.length() == 3) {
740 keyLen
= region
.extract(0, region
.length(), keyBuf
, ZID_KEY_MAX
+ 1, US_INV
);
742 tzid
= ures_getStringByKey(rb
, keyBuf
, &tzidLen
, &status
);
743 if (status
== U_MISSING_RESOURCE_ERROR
) {
744 status
= U_ZERO_ERROR
;
747 if (U_SUCCESS(status
) && tzid
== NULL
) {
749 tzid
= ures_getStringByKey(rb
, gWorldTag
, &tzidLen
, &status
);
757 result
.setTo(tzid
, tzidLen
);
763 static void U_CALLCONV
initAvailableMetaZoneIDs () {
764 U_ASSERT(gMetaZoneIDs
== NULL
);
765 U_ASSERT(gMetaZoneIDTable
== NULL
);
766 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
768 UErrorCode status
= U_ZERO_ERROR
;
769 gMetaZoneIDTable
= uhash_open(uhash_hashUnicodeString
, uhash_compareUnicodeString
, NULL
, &status
);
770 if (U_FAILURE(status
) || gMetaZoneIDTable
== NULL
) {
771 gMetaZoneIDTable
= NULL
;
774 uhash_setKeyDeleter(gMetaZoneIDTable
, uprv_deleteUObject
);
775 // No valueDeleter, because the vector maintain the value objects
776 gMetaZoneIDs
= new UVector(NULL
, uhash_compareUChars
, status
);
777 if (U_FAILURE(status
) || gMetaZoneIDs
== NULL
) {
779 uhash_close(gMetaZoneIDTable
);
780 gMetaZoneIDTable
= NULL
;
783 gMetaZoneIDs
->setDeleter(uprv_free
);
785 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
786 UResourceBundle
*bundle
= ures_getByKey(rb
, gMapTimezonesTag
, NULL
, &status
);
788 ures_initStackObject(&res
);
789 while (U_SUCCESS(status
) && ures_hasNext(bundle
)) {
790 ures_getNextResource(bundle
, &res
, &status
);
791 if (U_FAILURE(status
)) {
794 const char *mzID
= ures_getKey(&res
);
795 int32_t len
= static_cast<int32_t>(uprv_strlen(mzID
));
796 UChar
*uMzID
= (UChar
*)uprv_malloc(sizeof(UChar
) * (len
+ 1));
798 status
= U_MEMORY_ALLOCATION_ERROR
;
801 u_charsToUChars(mzID
, uMzID
, len
);
803 UnicodeString
*usMzID
= new UnicodeString(uMzID
);
804 if (uhash_get(gMetaZoneIDTable
, usMzID
) == NULL
) {
805 gMetaZoneIDs
->addElement((void *)uMzID
, status
);
806 uhash_put(gMetaZoneIDTable
, (void *)usMzID
, (void *)uMzID
, &status
);
816 if (U_FAILURE(status
)) {
817 uhash_close(gMetaZoneIDTable
);
819 gMetaZoneIDTable
= NULL
;
825 ZoneMeta::getAvailableMetazoneIDs() {
826 umtx_initOnce(gMetaZoneIDsInitOnce
, &initAvailableMetaZoneIDs
);
831 ZoneMeta::findMetaZoneID(const UnicodeString
& mzid
) {
832 umtx_initOnce(gMetaZoneIDsInitOnce
, &initAvailableMetaZoneIDs
);
833 if (gMetaZoneIDTable
== NULL
) {
836 return (const UChar
*)uhash_get(gMetaZoneIDTable
, &mzid
);
840 ZoneMeta::findTimeZoneID(const UnicodeString
& tzid
) {
841 return TimeZone::findID(tzid
);
846 ZoneMeta::createCustomTimeZone(int32_t offset
) {
847 UBool negative
= FALSE
;
848 int32_t tmp
= offset
;
853 int32_t hour
, min
, sec
;
862 formatCustomID(hour
, min
, sec
, negative
, zid
);
863 return new SimpleTimeZone(offset
, zid
);
867 ZoneMeta::formatCustomID(uint8_t hour
, uint8_t min
, uint8_t sec
, UBool negative
, UnicodeString
& id
) {
868 // Create normalized time zone ID - GMT[+|-]HH:mm[:ss]
869 id
.setTo(gCustomTzPrefix
, -1);
870 if (hour
!= 0 || min
!= 0) {
872 id
.append((UChar
)0x2D); // '-'
874 id
.append((UChar
)0x2B); // '+'
876 // Always use US-ASCII digits
877 id
.append((UChar
)(0x30 + (hour%100
)/10));
878 id
.append((UChar
)(0x30 + (hour%10
)));
879 id
.append((UChar
)0x3A); // ':'
880 id
.append((UChar
)(0x30 + (min%100
)/10));
881 id
.append((UChar
)(0x30 + (min%10
)));
883 id
.append((UChar
)0x3A); // ':'
884 id
.append((UChar
)(0x30 + (sec%100
)/10));
885 id
.append((UChar
)(0x30 + (sec%10
)));
892 ZoneMeta::getShortID(const TimeZone
& tz
) {
893 const UChar
* canonicalID
= NULL
;
894 if (dynamic_cast<const OlsonTimeZone
*>(&tz
) != NULL
) {
895 // short cut for OlsonTimeZone
896 const OlsonTimeZone
*otz
= (const OlsonTimeZone
*)&tz
;
897 canonicalID
= otz
->getCanonicalID();
899 if (canonicalID
== NULL
) {
902 return getShortIDFromCanonical(canonicalID
);
906 ZoneMeta::getShortID(const UnicodeString
& id
) {
907 UErrorCode status
= U_ZERO_ERROR
;
908 const UChar
* canonicalID
= ZoneMeta::getCanonicalCLDRID(id
, status
);
909 if (U_FAILURE(status
) || canonicalID
== NULL
) {
912 return ZoneMeta::getShortIDFromCanonical(canonicalID
);
916 ZoneMeta::getShortIDFromCanonical(const UChar
* canonicalID
) {
917 const UChar
* shortID
= NULL
;
918 int32_t len
= u_strlen(canonicalID
);
919 char tzidKey
[ZID_KEY_MAX
+ 1];
921 u_UCharsToChars(canonicalID
, tzidKey
, len
);
922 tzidKey
[len
] = (char) 0; // Make sure it is null terminated.
924 // replace '/' with ':'
932 UErrorCode status
= U_ZERO_ERROR
;
933 UResourceBundle
*rb
= ures_openDirect(NULL
, gKeyTypeData
, &status
);
934 ures_getByKey(rb
, gTypeMapTag
, rb
, &status
);
935 ures_getByKey(rb
, gTimezoneTag
, rb
, &status
);
936 shortID
= ures_getStringByKey(rb
, tzidKey
, NULL
, &status
);
944 #endif /* #if !UCONFIG_NO_FORMATTING */