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 icu::UMutex
*gZoneMetaLock() {
34 static icu::UMutex
*m
= STATIC_NEW(icu::UMutex
);
38 // CLDR Canonical ID mapping table
39 static UHashtable
*gCanonicalIDCache
= NULL
;
40 static icu::UInitOnce gCanonicalIDCacheInitOnce
= U_INITONCE_INITIALIZER
;
42 // Metazone mapping table
43 static UHashtable
*gOlsonToMeta
= NULL
;
44 static icu::UInitOnce gOlsonToMetaInitOnce
= U_INITONCE_INITIALIZER
;
46 // Available metazone IDs vector and table
47 static icu::UVector
*gMetaZoneIDs
= NULL
;
48 static UHashtable
*gMetaZoneIDTable
= NULL
;
49 static icu::UInitOnce gMetaZoneIDsInitOnce
= U_INITONCE_INITIALIZER
;
51 // Country info vectors
52 static icu::UVector
*gSingleZoneCountries
= NULL
;
53 static icu::UVector
*gMultiZonesCountries
= NULL
;
54 static icu::UInitOnce gCountryInfoVectorsInitOnce
= U_INITONCE_INITIALIZER
;
59 * Cleanup callback func
61 static UBool U_CALLCONV
zoneMeta_cleanup(void)
63 if (gCanonicalIDCache
!= NULL
) {
64 uhash_close(gCanonicalIDCache
);
65 gCanonicalIDCache
= NULL
;
67 gCanonicalIDCacheInitOnce
.reset();
69 if (gOlsonToMeta
!= NULL
) {
70 uhash_close(gOlsonToMeta
);
73 gOlsonToMetaInitOnce
.reset();
75 if (gMetaZoneIDTable
!= NULL
) {
76 uhash_close(gMetaZoneIDTable
);
77 gMetaZoneIDTable
= NULL
;
79 // delete after closing gMetaZoneIDTable, because it holds
80 // value objects held by the hashtable
83 gMetaZoneIDsInitOnce
.reset();
85 delete gSingleZoneCountries
;
86 gSingleZoneCountries
= NULL
;
87 delete gMultiZonesCountries
;
88 gMultiZonesCountries
= NULL
;
89 gCountryInfoVectorsInitOnce
.reset();
95 * Deleter for UChar* string
97 static void U_CALLCONV
98 deleteUCharString(void *obj
) {
99 UChar
*entry
= (UChar
*)obj
;
104 * Deleter for UVector
106 static void U_CALLCONV
107 deleteUVector(void *obj
) {
108 delete (icu::UVector
*) obj
;
112 * Deleter for OlsonToMetaMappingEntry
114 static void U_CALLCONV
115 deleteOlsonToMetaMappingEntry(void *obj
) {
116 icu::OlsonToMetaMappingEntry
*entry
= (icu::OlsonToMetaMappingEntry
*)obj
;
124 #define ZID_KEY_MAX 128
126 static const char gMetaZones
[] = "metaZones";
127 static const char gMetazoneInfo
[] = "metazoneInfo";
128 static const char gMapTimezonesTag
[] = "mapTimezones";
130 static const char gKeyTypeData
[] = "keyTypeData";
131 static const char gTypeAliasTag
[] = "typeAlias";
132 static const char gTypeMapTag
[] = "typeMap";
133 static const char gTimezoneTag
[] = "timezone";
135 static const char gPrimaryZonesTag
[] = "primaryZones";
137 static const char gWorldTag
[] = "001";
139 static const UChar gWorld
[] = {0x30, 0x30, 0x31, 0x00}; // "001"
141 static const UChar gDefaultFrom
[] = {0x31, 0x39, 0x37, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31,
142 0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x00}; // "1970-01-01 00:00"
143 static const UChar gDefaultTo
[] = {0x39, 0x39, 0x39, 0x39, 0x2D, 0x31, 0x32, 0x2D, 0x33, 0x31,
144 0x20, 0x32, 0x33, 0x3A, 0x35, 0x39, 0x00}; // "9999-12-31 23:59"
146 static const UChar gCustomTzPrefix
[] = {0x47, 0x4D, 0x54, 0}; // "GMT"
148 #define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1)
151 * Convert a date string used by metazone mappings to UDate.
152 * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
155 parseDate (const UChar
*text
, UErrorCode
&status
) {
156 if (U_FAILURE(status
)) {
159 int32_t len
= u_strlen(text
);
160 if (len
!= 16 && len
!= 10) {
161 // It must be yyyy-MM-dd HH:mm (length 16) or yyyy-MM-dd (length 10)
162 status
= U_INVALID_FORMAT_ERROR
;
166 int32_t year
= 0, month
= 0, day
= 0, hour
= 0, min
= 0, n
;
170 for (idx
= 0; idx
<= 3 && U_SUCCESS(status
); idx
++) {
171 n
= ASCII_DIGIT((int32_t)text
[idx
]);
175 status
= U_INVALID_FORMAT_ERROR
;
179 for (idx
= 5; idx
<= 6 && U_SUCCESS(status
); idx
++) {
180 n
= ASCII_DIGIT((int32_t)text
[idx
]);
182 month
= 10*month
+ n
;
184 status
= U_INVALID_FORMAT_ERROR
;
188 for (idx
= 8; idx
<= 9 && U_SUCCESS(status
); idx
++) {
189 n
= ASCII_DIGIT((int32_t)text
[idx
]);
193 status
= U_INVALID_FORMAT_ERROR
;
198 for (idx
= 11; idx
<= 12 && U_SUCCESS(status
); idx
++) {
199 n
= ASCII_DIGIT((int32_t)text
[idx
]);
203 status
= U_INVALID_FORMAT_ERROR
;
207 for (idx
= 14; idx
<= 15 && U_SUCCESS(status
); idx
++) {
208 n
= ASCII_DIGIT((int32_t)text
[idx
]);
212 status
= U_INVALID_FORMAT_ERROR
;
217 if (U_SUCCESS(status
)) {
218 UDate date
= Grego::fieldsToDay(year
, month
- 1, day
) * U_MILLIS_PER_DAY
219 + hour
* U_MILLIS_PER_HOUR
+ min
* U_MILLIS_PER_MINUTE
;
225 static void U_CALLCONV
initCanonicalIDCache(UErrorCode
&status
) {
226 gCanonicalIDCache
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
227 if (gCanonicalIDCache
== NULL
) {
228 status
= U_MEMORY_ALLOCATION_ERROR
;
230 if (U_FAILURE(status
)) {
231 gCanonicalIDCache
= NULL
;
233 // No key/value deleters - keys/values are from a resource bundle
234 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
238 const UChar
* U_EXPORT2
239 ZoneMeta::getCanonicalCLDRID(const UnicodeString
&tzid
, UErrorCode
& status
) {
240 if (U_FAILURE(status
)) {
244 if (tzid
.isBogus() || tzid
.length() > ZID_KEY_MAX
) {
245 status
= U_ILLEGAL_ARGUMENT_ERROR
;
249 // Checking the cached results
250 umtx_initOnce(gCanonicalIDCacheInitOnce
, &initCanonicalIDCache
, status
);
251 if (U_FAILURE(status
)) {
255 const UChar
*canonicalID
= NULL
;
257 UErrorCode tmpStatus
= U_ZERO_ERROR
;
258 UChar utzid
[ZID_KEY_MAX
+ 1];
259 tzid
.extract(utzid
, ZID_KEY_MAX
+ 1, tmpStatus
);
260 U_ASSERT(tmpStatus
== U_ZERO_ERROR
); // we checked the length of tzid already
262 if (!uprv_isInvariantUString(utzid
, -1)) {
263 // All of known tz IDs are only containing ASCII invariant characters.
264 status
= U_ILLEGAL_ARGUMENT_ERROR
;
268 // Check if it was already cached
269 umtx_lock(gZoneMetaLock());
271 canonicalID
= (const UChar
*)uhash_get(gCanonicalIDCache
, utzid
);
273 umtx_unlock(gZoneMetaLock());
275 if (canonicalID
!= NULL
) {
279 // If not, resolve CLDR canonical ID with resource data
280 UBool isInputCanonical
= FALSE
;
281 char id
[ZID_KEY_MAX
+ 1];
282 tzid
.extract(0, 0x7fffffff, id
, UPRV_LENGTHOF(id
), US_INV
);
284 // replace '/' with ':'
292 UResourceBundle
*top
= ures_openDirect(NULL
, gKeyTypeData
, &tmpStatus
);
293 UResourceBundle
*rb
= ures_getByKey(top
, gTypeMapTag
, NULL
, &tmpStatus
);
294 ures_getByKey(rb
, gTimezoneTag
, rb
, &tmpStatus
);
295 ures_getByKey(rb
, id
, rb
, &tmpStatus
);
296 if (U_SUCCESS(tmpStatus
)) {
297 // type entry (canonical) found
298 // the input is the canonical ID. resolve to const UChar*
299 canonicalID
= TimeZone::findID(tzid
);
300 isInputCanonical
= TRUE
;
303 if (canonicalID
== NULL
) {
304 // If a map element not found, then look for an alias
305 tmpStatus
= U_ZERO_ERROR
;
306 ures_getByKey(top
, gTypeAliasTag
, rb
, &tmpStatus
);
307 ures_getByKey(rb
, gTimezoneTag
, rb
, &tmpStatus
);
308 const UChar
*canonical
= ures_getStringByKey(rb
,id
,NULL
,&tmpStatus
);
309 if (U_SUCCESS(tmpStatus
)) {
310 // canonical map found
311 canonicalID
= canonical
;
314 if (canonicalID
== NULL
) {
315 // Dereference the input ID using the tz data
316 const UChar
*derefer
= TimeZone::dereferOlsonLink(tzid
);
317 if (derefer
== NULL
) {
318 status
= U_ILLEGAL_ARGUMENT_ERROR
;
320 int32_t len
= u_strlen(derefer
);
321 u_UCharsToChars(derefer
,id
,len
);
322 id
[len
] = (char) 0; // Make sure it is null terminated.
324 // replace '/' with ':'
332 // If a dereference turned something up then look for an alias.
333 // rb still points to the alias table, so we don't have to go looking
335 tmpStatus
= U_ZERO_ERROR
;
336 canonical
= ures_getStringByKey(rb
,id
,NULL
,&tmpStatus
);
337 if (U_SUCCESS(tmpStatus
)) {
338 // canonical map for the dereferenced ID found
339 canonicalID
= canonical
;
341 canonicalID
= derefer
;
342 isInputCanonical
= TRUE
;
350 if (U_SUCCESS(status
)) {
351 U_ASSERT(canonicalID
!= NULL
); // canocanilD must be non-NULL here
353 // Put the resolved canonical ID to the cache
354 umtx_lock(gZoneMetaLock());
356 const UChar
* idInCache
= (const UChar
*)uhash_get(gCanonicalIDCache
, utzid
);
357 if (idInCache
== NULL
) {
358 const UChar
* key
= ZoneMeta::findTimeZoneID(tzid
);
359 U_ASSERT(key
!= NULL
);
361 idInCache
= (const UChar
*)uhash_put(gCanonicalIDCache
, (void *)key
, (void *)canonicalID
, &status
);
362 U_ASSERT(idInCache
== NULL
);
365 if (U_SUCCESS(status
) && isInputCanonical
) {
366 // Also put canonical ID itself into the cache if not exist
367 const UChar
*canonicalInCache
= (const UChar
*)uhash_get(gCanonicalIDCache
, canonicalID
);
368 if (canonicalInCache
== NULL
) {
369 canonicalInCache
= (const UChar
*)uhash_put(gCanonicalIDCache
, (void *)canonicalID
, (void *)canonicalID
, &status
);
370 U_ASSERT(canonicalInCache
== NULL
);
374 umtx_unlock(gZoneMetaLock());
380 UnicodeString
& U_EXPORT2
381 ZoneMeta::getCanonicalCLDRID(const UnicodeString
&tzid
, UnicodeString
&systemID
, UErrorCode
& status
) {
382 const UChar
*canonicalID
= getCanonicalCLDRID(tzid
, status
);
383 if (U_FAILURE(status
) || canonicalID
== NULL
) {
384 systemID
.setToBogus();
387 systemID
.setTo(TRUE
, canonicalID
, -1);
391 const UChar
* U_EXPORT2
392 ZoneMeta::getCanonicalCLDRID(const TimeZone
& tz
) {
393 if (dynamic_cast<const OlsonTimeZone
*>(&tz
) != NULL
) {
394 // short cut for OlsonTimeZone
395 const OlsonTimeZone
*otz
= (const OlsonTimeZone
*)&tz
;
396 return otz
->getCanonicalID();
398 UErrorCode status
= U_ZERO_ERROR
;
400 return getCanonicalCLDRID(tz
.getID(tzID
), status
);
403 static void U_CALLCONV
countryInfoVectorsInit(UErrorCode
&status
) {
404 // Create empty vectors
405 // No deleters for these UVectors, it's a reference to a resource bundle string.
406 gSingleZoneCountries
= new UVector(NULL
, uhash_compareUChars
, status
);
407 if (gSingleZoneCountries
== NULL
) {
408 status
= U_MEMORY_ALLOCATION_ERROR
;
410 gMultiZonesCountries
= new UVector(NULL
, uhash_compareUChars
, status
);
411 if (gMultiZonesCountries
== NULL
) {
412 status
= U_MEMORY_ALLOCATION_ERROR
;
415 if (U_FAILURE(status
)) {
416 delete gSingleZoneCountries
;
417 delete gMultiZonesCountries
;
418 gSingleZoneCountries
= NULL
;
419 gMultiZonesCountries
= NULL
;
421 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
425 UnicodeString
& U_EXPORT2
426 ZoneMeta::getCanonicalCountry(const UnicodeString
&tzid
, UnicodeString
&country
, UBool
*isPrimary
/* = NULL */) {
427 if (isPrimary
!= NULL
) {
431 const UChar
*region
= TimeZone::getRegion(tzid
);
432 if (region
!= NULL
&& u_strcmp(gWorld
, region
) != 0) {
433 country
.setTo(region
, -1);
435 country
.setToBogus();
439 if (isPrimary
!= NULL
) {
440 char regionBuf
[] = {0, 0, 0};
442 // Checking the cached results
443 UErrorCode status
= U_ZERO_ERROR
;
444 umtx_initOnce(gCountryInfoVectorsInitOnce
, &countryInfoVectorsInit
, status
);
445 if (U_FAILURE(status
)) {
449 // Check if it was already cached
450 UBool cached
= FALSE
;
451 UBool singleZone
= FALSE
;
452 umtx_lock(gZoneMetaLock());
454 singleZone
= cached
= gSingleZoneCountries
->contains((void*)region
);
456 cached
= gMultiZonesCountries
->contains((void*)region
);
459 umtx_unlock(gZoneMetaLock());
462 // We need to go through all zones associated with the region.
463 // This is relatively heavy operation.
465 U_ASSERT(u_strlen(region
) == 2);
467 u_UCharsToChars(region
, regionBuf
, 2);
469 StringEnumeration
*ids
= TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION
, regionBuf
, NULL
, status
);
470 int32_t idsLen
= ids
->count(status
);
471 if (U_SUCCESS(status
) && idsLen
== 1) {
472 // only the single zone is available for the region
478 umtx_lock(gZoneMetaLock());
480 UErrorCode ec
= U_ZERO_ERROR
;
482 if (!gSingleZoneCountries
->contains((void*)region
)) {
483 gSingleZoneCountries
->addElement((void*)region
, ec
);
486 if (!gMultiZonesCountries
->contains((void*)region
)) {
487 gMultiZonesCountries
->addElement((void*)region
, ec
);
491 umtx_unlock(gZoneMetaLock());
497 // Note: We may cache the primary zone map in future.
499 // Even a country has multiple zones, one of them might be
500 // dominant and treated as a primary zone
502 if (regionBuf
[0] == 0) {
503 u_UCharsToChars(region
, regionBuf
, 2);
506 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
507 ures_getByKey(rb
, gPrimaryZonesTag
, rb
, &status
);
508 const UChar
*primaryZone
= ures_getStringByKey(rb
, regionBuf
, &idLen
, &status
);
509 if (U_SUCCESS(status
)) {
510 if (tzid
.compare(primaryZone
, idLen
) == 0) {
513 // The given ID might not be a canonical ID
514 UnicodeString canonicalID
;
515 TimeZone::getCanonicalID(tzid
, canonicalID
, status
);
516 if (U_SUCCESS(status
) && canonicalID
.compare(primaryZone
, idLen
) == 0) {
528 UnicodeString
& U_EXPORT2
529 ZoneMeta::getMetazoneID(const UnicodeString
&tzid
, UDate date
, UnicodeString
&result
) {
531 const UVector
*mappings
= getMetazoneMappings(tzid
);
532 if (mappings
!= NULL
) {
533 for (int32_t i
= 0; i
< mappings
->size(); i
++) {
534 OlsonToMetaMappingEntry
*mzm
= (OlsonToMetaMappingEntry
*)mappings
->elementAt(i
);
535 if (mzm
->from
<= date
&& mzm
->to
> date
) {
536 result
.setTo(mzm
->mzid
, -1);
548 static void U_CALLCONV
olsonToMetaInit(UErrorCode
&status
) {
549 U_ASSERT(gOlsonToMeta
== NULL
);
550 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
551 gOlsonToMeta
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
552 if (U_FAILURE(status
)) {
555 uhash_setKeyDeleter(gOlsonToMeta
, deleteUCharString
);
556 uhash_setValueDeleter(gOlsonToMeta
, deleteUVector
);
561 const UVector
* U_EXPORT2
562 ZoneMeta::getMetazoneMappings(const UnicodeString
&tzid
) {
563 UErrorCode status
= U_ZERO_ERROR
;
564 UChar tzidUChars
[ZID_KEY_MAX
+ 1];
565 tzid
.extract(tzidUChars
, ZID_KEY_MAX
+ 1, status
);
566 if (U_FAILURE(status
) || status
== U_STRING_NOT_TERMINATED_WARNING
) {
570 umtx_initOnce(gOlsonToMetaInitOnce
, &olsonToMetaInit
, status
);
571 if (U_FAILURE(status
)) {
575 // get the mapping from cache
576 const UVector
*result
= NULL
;
578 umtx_lock(gZoneMetaLock());
580 result
= (UVector
*) uhash_get(gOlsonToMeta
, tzidUChars
);
582 umtx_unlock(gZoneMetaLock());
584 if (result
!= NULL
) {
588 // miss the cache - create new one
589 UVector
*tmpResult
= createMetazoneMappings(tzid
);
590 if (tmpResult
== NULL
) {
595 // put the new one into the cache
596 umtx_lock(gZoneMetaLock());
598 // make sure it's already created
599 result
= (UVector
*) uhash_get(gOlsonToMeta
, tzidUChars
);
600 if (result
== NULL
) {
601 // add the one just created
602 int32_t tzidLen
= tzid
.length() + 1;
603 UChar
*key
= (UChar
*)uprv_malloc(tzidLen
* sizeof(UChar
));
605 // memory allocation error.. just return NULL
609 tzid
.extract(key
, tzidLen
, status
);
610 uhash_put(gOlsonToMeta
, key
, tmpResult
, &status
);
611 if (U_FAILURE(status
)) {
612 // delete the mapping
620 // another thread already put the one
624 umtx_unlock(gZoneMetaLock());
630 ZoneMeta::createMetazoneMappings(const UnicodeString
&tzid
) {
631 UVector
*mzMappings
= NULL
;
632 UErrorCode status
= U_ZERO_ERROR
;
634 UnicodeString canonicalID
;
635 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
636 ures_getByKey(rb
, gMetazoneInfo
, rb
, &status
);
637 getCanonicalCLDRID(tzid
, canonicalID
, status
);
639 if (U_SUCCESS(status
)) {
640 char tzKey
[ZID_KEY_MAX
+ 1];
641 int32_t tzKeyLen
= canonicalID
.extract(0, canonicalID
.length(), tzKey
, sizeof(tzKey
), US_INV
);
644 // tzid keys are using ':' as separators
653 ures_getByKey(rb
, tzKey
, rb
, &status
);
655 if (U_SUCCESS(status
)) {
656 UResourceBundle
*mz
= NULL
;
657 while (ures_hasNext(rb
)) {
658 mz
= ures_getNextResource(rb
, mz
, &status
);
660 const UChar
*mz_name
= ures_getStringByIndex(mz
, 0, NULL
, &status
);
661 const UChar
*mz_from
= gDefaultFrom
;
662 const UChar
*mz_to
= gDefaultTo
;
664 if (ures_getSize(mz
) == 3) {
665 mz_from
= ures_getStringByIndex(mz
, 1, NULL
, &status
);
666 mz_to
= ures_getStringByIndex(mz
, 2, NULL
, &status
);
669 if(U_FAILURE(status
)){
670 status
= U_ZERO_ERROR
;
673 // We do not want to use SimpleDateformat to parse boundary dates,
674 // because this code could be triggered by the initialization code
675 // used by SimpleDateFormat.
676 UDate from
= parseDate(mz_from
, status
);
677 UDate to
= parseDate(mz_to
, status
);
678 if (U_FAILURE(status
)) {
679 status
= U_ZERO_ERROR
;
683 OlsonToMetaMappingEntry
*entry
= (OlsonToMetaMappingEntry
*)uprv_malloc(sizeof(OlsonToMetaMappingEntry
));
685 status
= U_MEMORY_ALLOCATION_ERROR
;
688 entry
->mzid
= mz_name
;
692 if (mzMappings
== NULL
) {
693 mzMappings
= new UVector(deleteOlsonToMetaMappingEntry
, NULL
, status
);
694 if (U_FAILURE(status
)) {
702 mzMappings
->addElement(entry
, status
);
703 if (U_FAILURE(status
)) {
708 if (U_FAILURE(status
)) {
709 if (mzMappings
!= NULL
) {
720 UnicodeString
& U_EXPORT2
721 ZoneMeta::getZoneIdByMetazone(const UnicodeString
&mzid
, const UnicodeString
®ion
, UnicodeString
&result
) {
722 UErrorCode status
= U_ZERO_ERROR
;
723 const UChar
*tzid
= NULL
;
725 char keyBuf
[ZID_KEY_MAX
+ 1];
728 if (mzid
.isBogus() || mzid
.length() > ZID_KEY_MAX
) {
733 keyLen
= mzid
.extract(0, mzid
.length(), keyBuf
, ZID_KEY_MAX
+ 1, US_INV
);
736 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
737 ures_getByKey(rb
, gMapTimezonesTag
, rb
, &status
);
738 ures_getByKey(rb
, keyBuf
, rb
, &status
);
740 if (U_SUCCESS(status
)) {
741 // check region mapping
742 if (region
.length() == 2 || region
.length() == 3) {
743 keyLen
= region
.extract(0, region
.length(), keyBuf
, ZID_KEY_MAX
+ 1, US_INV
);
745 tzid
= ures_getStringByKey(rb
, keyBuf
, &tzidLen
, &status
);
746 if (status
== U_MISSING_RESOURCE_ERROR
) {
747 status
= U_ZERO_ERROR
;
750 if (U_SUCCESS(status
) && tzid
== NULL
) {
752 tzid
= ures_getStringByKey(rb
, gWorldTag
, &tzidLen
, &status
);
760 result
.setTo(tzid
, tzidLen
);
766 static void U_CALLCONV
initAvailableMetaZoneIDs () {
767 U_ASSERT(gMetaZoneIDs
== NULL
);
768 U_ASSERT(gMetaZoneIDTable
== NULL
);
769 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
771 UErrorCode status
= U_ZERO_ERROR
;
772 gMetaZoneIDTable
= uhash_open(uhash_hashUnicodeString
, uhash_compareUnicodeString
, NULL
, &status
);
773 if (U_FAILURE(status
) || gMetaZoneIDTable
== NULL
) {
774 gMetaZoneIDTable
= NULL
;
777 uhash_setKeyDeleter(gMetaZoneIDTable
, uprv_deleteUObject
);
778 // No valueDeleter, because the vector maintain the value objects
779 gMetaZoneIDs
= new UVector(NULL
, uhash_compareUChars
, status
);
780 if (U_FAILURE(status
) || gMetaZoneIDs
== NULL
) {
782 uhash_close(gMetaZoneIDTable
);
783 gMetaZoneIDTable
= NULL
;
786 gMetaZoneIDs
->setDeleter(uprv_free
);
788 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
789 UResourceBundle
*bundle
= ures_getByKey(rb
, gMapTimezonesTag
, NULL
, &status
);
790 StackUResourceBundle res
;
791 while (U_SUCCESS(status
) && ures_hasNext(bundle
)) {
792 ures_getNextResource(bundle
, res
.getAlias(), &status
);
793 if (U_FAILURE(status
)) {
796 const char *mzID
= ures_getKey(res
.getAlias());
797 int32_t len
= static_cast<int32_t>(uprv_strlen(mzID
));
798 UChar
*uMzID
= (UChar
*)uprv_malloc(sizeof(UChar
) * (len
+ 1));
800 status
= U_MEMORY_ALLOCATION_ERROR
;
803 u_charsToUChars(mzID
, uMzID
, len
);
805 UnicodeString
*usMzID
= new UnicodeString(uMzID
);
806 if (uhash_get(gMetaZoneIDTable
, usMzID
) == NULL
) {
807 gMetaZoneIDs
->addElement((void *)uMzID
, status
);
808 uhash_put(gMetaZoneIDTable
, (void *)usMzID
, (void *)uMzID
, &status
);
817 if (U_FAILURE(status
)) {
818 uhash_close(gMetaZoneIDTable
);
820 gMetaZoneIDTable
= NULL
;
826 ZoneMeta::getAvailableMetazoneIDs() {
827 umtx_initOnce(gMetaZoneIDsInitOnce
, &initAvailableMetaZoneIDs
);
832 ZoneMeta::findMetaZoneID(const UnicodeString
& mzid
) {
833 umtx_initOnce(gMetaZoneIDsInitOnce
, &initAvailableMetaZoneIDs
);
834 if (gMetaZoneIDTable
== NULL
) {
837 return (const UChar
*)uhash_get(gMetaZoneIDTable
, &mzid
);
841 ZoneMeta::findTimeZoneID(const UnicodeString
& tzid
) {
842 return TimeZone::findID(tzid
);
847 ZoneMeta::createCustomTimeZone(int32_t offset
) {
848 UBool negative
= FALSE
;
849 int32_t tmp
= offset
;
854 uint8_t hour
, min
, sec
;
857 sec
= static_cast<uint8_t>(tmp
% 60);
859 min
= static_cast<uint8_t>(tmp
% 60);
860 hour
= static_cast<uint8_t>(tmp
/ 60);
863 formatCustomID(hour
, min
, sec
, negative
, zid
);
864 return new SimpleTimeZone(offset
, zid
);
868 ZoneMeta::formatCustomID(uint8_t hour
, uint8_t min
, uint8_t sec
, UBool negative
, UnicodeString
& id
) {
869 // Create normalized time zone ID - GMT[+|-]HH:mm[:ss]
870 id
.setTo(gCustomTzPrefix
, -1);
871 if (hour
!= 0 || min
!= 0) {
873 id
.append((UChar
)0x2D); // '-'
875 id
.append((UChar
)0x2B); // '+'
877 // Always use US-ASCII digits
878 id
.append((UChar
)(0x30 + (hour%100
)/10));
879 id
.append((UChar
)(0x30 + (hour%10
)));
880 id
.append((UChar
)0x3A); // ':'
881 id
.append((UChar
)(0x30 + (min%100
)/10));
882 id
.append((UChar
)(0x30 + (min%10
)));
884 id
.append((UChar
)0x3A); // ':'
885 id
.append((UChar
)(0x30 + (sec%100
)/10));
886 id
.append((UChar
)(0x30 + (sec%10
)));
893 ZoneMeta::getShortID(const TimeZone
& tz
) {
894 const UChar
* canonicalID
= NULL
;
895 if (dynamic_cast<const OlsonTimeZone
*>(&tz
) != NULL
) {
896 // short cut for OlsonTimeZone
897 const OlsonTimeZone
*otz
= (const OlsonTimeZone
*)&tz
;
898 canonicalID
= otz
->getCanonicalID();
900 if (canonicalID
== NULL
) {
903 return getShortIDFromCanonical(canonicalID
);
907 ZoneMeta::getShortID(const UnicodeString
& id
) {
908 UErrorCode status
= U_ZERO_ERROR
;
909 const UChar
* canonicalID
= ZoneMeta::getCanonicalCLDRID(id
, status
);
910 if (U_FAILURE(status
) || canonicalID
== NULL
) {
913 return ZoneMeta::getShortIDFromCanonical(canonicalID
);
917 ZoneMeta::getShortIDFromCanonical(const UChar
* canonicalID
) {
918 const UChar
* shortID
= NULL
;
919 int32_t len
= u_strlen(canonicalID
);
920 char tzidKey
[ZID_KEY_MAX
+ 1];
922 u_UCharsToChars(canonicalID
, tzidKey
, len
);
923 tzidKey
[len
] = (char) 0; // Make sure it is null terminated.
925 // replace '/' with ':'
933 UErrorCode status
= U_ZERO_ERROR
;
934 UResourceBundle
*rb
= ures_openDirect(NULL
, gKeyTypeData
, &status
);
935 ures_getByKey(rb
, gTypeMapTag
, rb
, &status
);
936 ures_getByKey(rb
, gTimezoneTag
, rb
, &status
);
937 shortID
= ures_getStringByKey(rb
, tzidKey
, NULL
, &status
);
945 #endif /* #if !UCONFIG_NO_FORMATTING */