2 *******************************************************************************
3 * Copyright (C) 2007-2013, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
8 #include "unicode/utypes.h"
10 #if !UCONFIG_NO_FORMATTING
14 #include "unicode/timezone.h"
15 #include "unicode/ustring.h"
16 #include "unicode/putil.h"
17 #include "unicode/simpletz.h"
30 static UMutex gZoneMetaLock
= U_MUTEX_INITIALIZER
;
32 // CLDR Canonical ID mapping table
33 static UHashtable
*gCanonicalIDCache
= NULL
;
34 static UBool gCanonicalIDCacheInitialized
= FALSE
;
36 // Metazone mapping table
37 static UHashtable
*gOlsonToMeta
= NULL
;
38 static UBool gOlsonToMetaInitialized
= FALSE
;
40 // Available metazone IDs vector and table
41 static icu::UVector
*gMetaZoneIDs
= NULL
;
42 static UHashtable
*gMetaZoneIDTable
= NULL
;
43 static UBool gMetaZoneIDsInitialized
= FALSE
;
45 // Country info vectors
46 static icu::UVector
*gSingleZoneCountries
= NULL
;
47 static icu::UVector
*gMultiZonesCountries
= NULL
;
48 static UBool gCountryInfoVectorsInitialized
= FALSE
;
53 * Cleanup callback func
55 static UBool U_CALLCONV
zoneMeta_cleanup(void)
57 if (gCanonicalIDCache
!= NULL
) {
58 uhash_close(gCanonicalIDCache
);
59 gCanonicalIDCache
= NULL
;
61 gCanonicalIDCacheInitialized
= FALSE
;
63 if (gOlsonToMeta
!= NULL
) {
64 uhash_close(gOlsonToMeta
);
67 gOlsonToMetaInitialized
= FALSE
;
69 if (gMetaZoneIDTable
!= NULL
) {
70 uhash_close(gMetaZoneIDTable
);
72 // delete after closing gMetaZoneIDTable, because it holds
73 // value objects held by the hashtable
75 gMetaZoneIDsInitialized
= FALSE
;
77 delete gSingleZoneCountries
;
78 delete gMultiZonesCountries
;
79 gCountryInfoVectorsInitialized
= FALSE
;
85 * Deleter for UChar* string
87 static void U_CALLCONV
88 deleteUCharString(void *obj
) {
89 UChar
*entry
= (UChar
*)obj
;
96 static void U_CALLCONV
97 deleteUVector(void *obj
) {
98 delete (icu::UVector
*) obj
;
102 * Deleter for OlsonToMetaMappingEntry
104 static void U_CALLCONV
105 deleteOlsonToMetaMappingEntry(void *obj
) {
106 icu::OlsonToMetaMappingEntry
*entry
= (icu::OlsonToMetaMappingEntry
*)obj
;
114 #define ZID_KEY_MAX 128
116 static const char gMetaZones
[] = "metaZones";
117 static const char gMetazoneInfo
[] = "metazoneInfo";
118 static const char gMapTimezonesTag
[] = "mapTimezones";
120 static const char gKeyTypeData
[] = "keyTypeData";
121 static const char gTypeAliasTag
[] = "typeAlias";
122 static const char gTypeMapTag
[] = "typeMap";
123 static const char gTimezoneTag
[] = "timezone";
125 static const char gPrimaryZonesTag
[] = "primaryZones";
127 static const char gWorldTag
[] = "001";
129 static const UChar gWorld
[] = {0x30, 0x30, 0x31, 0x00}; // "001"
131 static const UChar gDefaultFrom
[] = {0x31, 0x39, 0x37, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31,
132 0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x00}; // "1970-01-01 00:00"
133 static const UChar gDefaultTo
[] = {0x39, 0x39, 0x39, 0x39, 0x2D, 0x31, 0x32, 0x2D, 0x33, 0x31,
134 0x20, 0x32, 0x33, 0x3A, 0x35, 0x39, 0x00}; // "9999-12-31 23:59"
136 static const UChar gCustomTzPrefix
[] = {0x47, 0x4D, 0x54, 0}; // "GMT"
138 #define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1)
141 * Convert a date string used by metazone mappings to UDate.
142 * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
145 parseDate (const UChar
*text
, UErrorCode
&status
) {
146 if (U_FAILURE(status
)) {
149 int32_t len
= u_strlen(text
);
150 if (len
!= 16 && len
!= 10) {
151 // It must be yyyy-MM-dd HH:mm (length 16) or yyyy-MM-dd (length 10)
152 status
= U_INVALID_FORMAT_ERROR
;
156 int32_t year
= 0, month
= 0, day
= 0, hour
= 0, min
= 0, n
;
160 for (idx
= 0; idx
<= 3 && U_SUCCESS(status
); idx
++) {
161 n
= ASCII_DIGIT((int32_t)text
[idx
]);
165 status
= U_INVALID_FORMAT_ERROR
;
169 for (idx
= 5; idx
<= 6 && U_SUCCESS(status
); idx
++) {
170 n
= ASCII_DIGIT((int32_t)text
[idx
]);
172 month
= 10*month
+ n
;
174 status
= U_INVALID_FORMAT_ERROR
;
178 for (idx
= 8; idx
<= 9 && U_SUCCESS(status
); idx
++) {
179 n
= ASCII_DIGIT((int32_t)text
[idx
]);
183 status
= U_INVALID_FORMAT_ERROR
;
188 for (idx
= 11; idx
<= 12 && U_SUCCESS(status
); idx
++) {
189 n
= ASCII_DIGIT((int32_t)text
[idx
]);
193 status
= U_INVALID_FORMAT_ERROR
;
197 for (idx
= 14; idx
<= 15 && U_SUCCESS(status
); idx
++) {
198 n
= ASCII_DIGIT((int32_t)text
[idx
]);
202 status
= U_INVALID_FORMAT_ERROR
;
207 if (U_SUCCESS(status
)) {
208 UDate date
= Grego::fieldsToDay(year
, month
- 1, day
) * U_MILLIS_PER_DAY
209 + hour
* U_MILLIS_PER_HOUR
+ min
* U_MILLIS_PER_MINUTE
;
215 const UChar
* U_EXPORT2
216 ZoneMeta::getCanonicalCLDRID(const UnicodeString
&tzid
, UErrorCode
& status
) {
217 if (U_FAILURE(status
)) {
221 int32_t len
= tzid
.length();
222 if (len
> ZID_KEY_MAX
) {
223 status
= U_ILLEGAL_ARGUMENT_ERROR
;
227 // Checking the cached results
229 UMTX_CHECK(&gZoneMetaLock
, gCanonicalIDCacheInitialized
, initialized
);
231 // Create empty hashtable
232 umtx_lock(&gZoneMetaLock
);
234 if (!gCanonicalIDCacheInitialized
) {
235 gCanonicalIDCache
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
236 if (gCanonicalIDCache
== NULL
) {
237 status
= U_MEMORY_ALLOCATION_ERROR
;
239 if (U_FAILURE(status
)) {
240 gCanonicalIDCache
= NULL
;
243 // No key/value deleters - keys/values are from a resource bundle
244 gCanonicalIDCacheInitialized
= TRUE
;
245 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
248 umtx_unlock(&gZoneMetaLock
);
251 const UChar
*canonicalID
= NULL
;
253 UErrorCode tmpStatus
= U_ZERO_ERROR
;
254 UChar utzid
[ZID_KEY_MAX
+ 1];
255 tzid
.extract(utzid
, ZID_KEY_MAX
+ 1, tmpStatus
);
256 U_ASSERT(tmpStatus
== U_ZERO_ERROR
); // we checked the length of tzid already
258 // Check if it was already cached
259 umtx_lock(&gZoneMetaLock
);
261 canonicalID
= (const UChar
*)uhash_get(gCanonicalIDCache
, utzid
);
263 umtx_unlock(&gZoneMetaLock
);
265 if (canonicalID
!= NULL
) {
269 // If not, resolve CLDR canonical ID with resource data
270 UBool isInputCanonical
= FALSE
;
271 char id
[ZID_KEY_MAX
+ 1];
272 const UChar
* idChars
= tzid
.getBuffer();
274 u_UCharsToChars(idChars
,id
,len
);
275 id
[len
] = (char) 0; // Make sure it is null terminated.
277 // replace '/' with ':'
285 UResourceBundle
*top
= ures_openDirect(NULL
, gKeyTypeData
, &tmpStatus
);
286 UResourceBundle
*rb
= ures_getByKey(top
, gTypeMapTag
, NULL
, &tmpStatus
);
287 ures_getByKey(rb
, gTimezoneTag
, rb
, &tmpStatus
);
288 ures_getByKey(rb
, id
, rb
, &tmpStatus
);
289 if (U_SUCCESS(tmpStatus
)) {
290 // type entry (canonical) found
291 // the input is the canonical ID. resolve to const UChar*
292 canonicalID
= TimeZone::findID(tzid
);
293 isInputCanonical
= TRUE
;
296 if (canonicalID
== NULL
) {
297 // If a map element not found, then look for an alias
298 tmpStatus
= U_ZERO_ERROR
;
299 ures_getByKey(top
, gTypeAliasTag
, rb
, &tmpStatus
);
300 ures_getByKey(rb
, gTimezoneTag
, rb
, &tmpStatus
);
301 const UChar
*canonical
= ures_getStringByKey(rb
,id
,NULL
,&tmpStatus
);
302 if (U_SUCCESS(tmpStatus
)) {
303 // canonical map found
304 canonicalID
= canonical
;
307 if (canonicalID
== NULL
) {
308 // Dereference the input ID using the tz data
309 const UChar
*derefer
= TimeZone::dereferOlsonLink(tzid
);
310 if (derefer
== NULL
) {
311 status
= U_ILLEGAL_ARGUMENT_ERROR
;
313 len
= u_strlen(derefer
);
314 u_UCharsToChars(derefer
,id
,len
);
315 id
[len
] = (char) 0; // Make sure it is null terminated.
317 // replace '/' with ':'
325 // If a dereference turned something up then look for an alias.
326 // rb still points to the alias table, so we don't have to go looking
328 tmpStatus
= U_ZERO_ERROR
;
329 canonical
= ures_getStringByKey(rb
,id
,NULL
,&tmpStatus
);
330 if (U_SUCCESS(tmpStatus
)) {
331 // canonical map for the dereferenced ID found
332 canonicalID
= canonical
;
334 canonicalID
= derefer
;
335 isInputCanonical
= TRUE
;
343 if (U_SUCCESS(status
)) {
344 U_ASSERT(canonicalID
!= NULL
); // canocanilD must be non-NULL here
346 // Put the resolved canonical ID to the cache
347 umtx_lock(&gZoneMetaLock
);
349 const UChar
* idInCache
= (const UChar
*)uhash_get(gCanonicalIDCache
, utzid
);
350 if (idInCache
== NULL
) {
351 const UChar
* key
= ZoneMeta::findTimeZoneID(tzid
);
352 U_ASSERT(key
!= NULL
);
354 idInCache
= (const UChar
*)uhash_put(gCanonicalIDCache
, (void *)key
, (void *)canonicalID
, &status
);
355 U_ASSERT(idInCache
== NULL
);
358 if (U_SUCCESS(status
) && isInputCanonical
) {
359 // Also put canonical ID itself into the cache if not exist
360 const UChar
*canonicalInCache
= (const UChar
*)uhash_get(gCanonicalIDCache
, canonicalID
);
361 if (canonicalInCache
== NULL
) {
362 canonicalInCache
= (const UChar
*)uhash_put(gCanonicalIDCache
, (void *)canonicalID
, (void *)canonicalID
, &status
);
363 U_ASSERT(canonicalInCache
== NULL
);
367 umtx_unlock(&gZoneMetaLock
);
373 UnicodeString
& U_EXPORT2
374 ZoneMeta::getCanonicalCLDRID(const UnicodeString
&tzid
, UnicodeString
&systemID
, UErrorCode
& status
) {
375 const UChar
*canonicalID
= getCanonicalCLDRID(tzid
, status
);
376 if (U_FAILURE(status
) || canonicalID
== NULL
) {
377 systemID
.setToBogus();
380 systemID
.setTo(TRUE
, canonicalID
, -1);
384 const UChar
* U_EXPORT2
385 ZoneMeta::getCanonicalCLDRID(const TimeZone
& tz
) {
386 if (dynamic_cast<const OlsonTimeZone
*>(&tz
) != NULL
) {
387 // short cut for OlsonTimeZone
388 const OlsonTimeZone
*otz
= (const OlsonTimeZone
*)&tz
;
389 return otz
->getCanonicalID();
391 UErrorCode status
= U_ZERO_ERROR
;
393 return getCanonicalCLDRID(tz
.getID(tzID
), status
);
396 UnicodeString
& U_EXPORT2
397 ZoneMeta::getCanonicalCountry(const UnicodeString
&tzid
, UnicodeString
&country
, UBool
*isPrimary
/* = NULL */) {
398 if (isPrimary
!= NULL
) {
402 const UChar
*region
= TimeZone::getRegion(tzid
);
403 if (region
!= NULL
&& u_strcmp(gWorld
, region
) != 0) {
404 country
.setTo(region
, -1);
406 country
.setToBogus();
410 if (isPrimary
!= NULL
) {
411 char regionBuf
[] = {0, 0, 0};
413 // Checking the cached results
414 UErrorCode status
= U_ZERO_ERROR
;
416 UMTX_CHECK(&gZoneMetaLock
, gCountryInfoVectorsInitialized
, initialized
);
418 // Create empty vectors
419 umtx_lock(&gZoneMetaLock
);
421 if (!gCountryInfoVectorsInitialized
) {
422 // No deleters for these UVectors, it's a reference to a resource bundle string.
423 gSingleZoneCountries
= new UVector(NULL
, uhash_compareUChars
, status
);
424 if (gSingleZoneCountries
== NULL
) {
425 status
= U_MEMORY_ALLOCATION_ERROR
;
427 gMultiZonesCountries
= new UVector(NULL
, uhash_compareUChars
, status
);
428 if (gMultiZonesCountries
== NULL
) {
429 status
= U_MEMORY_ALLOCATION_ERROR
;
432 if (U_SUCCESS(status
)) {
433 gCountryInfoVectorsInitialized
= TRUE
;
434 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
436 delete gSingleZoneCountries
;
437 delete gMultiZonesCountries
;
441 umtx_unlock(&gZoneMetaLock
);
443 if (U_FAILURE(status
)) {
446 U_ASSERT(gSingleZoneCountries
!= NULL
);
447 U_ASSERT(gMultiZonesCountries
!= NULL
);
450 // Check if it was already cached
451 UBool cached
= FALSE
;
452 UBool singleZone
= FALSE
;
453 umtx_lock(&gZoneMetaLock
);
455 singleZone
= cached
= gSingleZoneCountries
->contains((void*)region
);
457 cached
= gMultiZonesCountries
->contains((void*)region
);
460 umtx_unlock(&gZoneMetaLock
);
463 // We need to go through all zones associated with the region.
464 // This is relatively heavy operation.
466 U_ASSERT(u_strlen(region
) == 2);
468 u_UCharsToChars(region
, regionBuf
, 2);
470 StringEnumeration
*ids
= TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION
, regionBuf
, NULL
, status
);
471 int32_t idsLen
= ids
->count(status
);
472 if (U_SUCCESS(status
) && idsLen
== 1) {
473 // only the single zone is available for the region
479 umtx_lock(&gZoneMetaLock
);
481 UErrorCode ec
= U_ZERO_ERROR
;
483 if (!gSingleZoneCountries
->contains((void*)region
)) {
484 gSingleZoneCountries
->addElement((void*)region
, ec
);
487 if (!gMultiZonesCountries
->contains((void*)region
)) {
488 gMultiZonesCountries
->addElement((void*)region
, ec
);
492 umtx_unlock(&gZoneMetaLock
);
498 // Note: We may cache the primary zone map in future.
500 // Even a country has multiple zones, one of them might be
501 // dominant and treated as a primary zone
503 if (regionBuf
[0] == 0) {
504 u_UCharsToChars(region
, regionBuf
, 2);
507 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
508 ures_getByKey(rb
, gPrimaryZonesTag
, rb
, &status
);
509 const UChar
*primaryZone
= ures_getStringByKey(rb
, regionBuf
, &idLen
, &status
);
510 if (U_SUCCESS(status
)) {
511 if (tzid
.compare(primaryZone
, idLen
) == 0) {
514 // The given ID might not be a canonical ID
515 UnicodeString canonicalID
;
516 TimeZone::getCanonicalID(tzid
, canonicalID
, status
);
517 if (U_SUCCESS(status
) && canonicalID
.compare(primaryZone
, idLen
) == 0) {
529 UnicodeString
& U_EXPORT2
530 ZoneMeta::getMetazoneID(const UnicodeString
&tzid
, UDate date
, UnicodeString
&result
) {
532 const UVector
*mappings
= getMetazoneMappings(tzid
);
533 if (mappings
!= NULL
) {
534 for (int32_t i
= 0; i
< mappings
->size(); i
++) {
535 OlsonToMetaMappingEntry
*mzm
= (OlsonToMetaMappingEntry
*)mappings
->elementAt(i
);
536 if (mzm
->from
<= date
&& mzm
->to
> date
) {
537 result
.setTo(mzm
->mzid
, -1);
549 const UVector
* U_EXPORT2
550 ZoneMeta::getMetazoneMappings(const UnicodeString
&tzid
) {
551 UErrorCode status
= U_ZERO_ERROR
;
552 UChar tzidUChars
[ZID_KEY_MAX
+ 1];
553 tzid
.extract(tzidUChars
, ZID_KEY_MAX
+ 1, status
);
554 if (U_FAILURE(status
) || status
== U_STRING_NOT_TERMINATED_WARNING
) {
559 UMTX_CHECK(&gZoneMetaLock
, gOlsonToMetaInitialized
, initialized
);
561 UHashtable
*tmpOlsonToMeta
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
562 if (U_FAILURE(status
)) {
565 uhash_setKeyDeleter(tmpOlsonToMeta
, deleteUCharString
);
566 uhash_setValueDeleter(tmpOlsonToMeta
, deleteUVector
);
568 umtx_lock(&gZoneMetaLock
);
570 if (!gOlsonToMetaInitialized
) {
571 gOlsonToMeta
= tmpOlsonToMeta
;
572 tmpOlsonToMeta
= NULL
;
573 gOlsonToMetaInitialized
= TRUE
;
576 umtx_unlock(&gZoneMetaLock
);
578 // OK to call the following multiple times with the same function
579 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
580 if (tmpOlsonToMeta
!= NULL
) {
581 uhash_close(tmpOlsonToMeta
);
585 // get the mapping from cache
586 const UVector
*result
= NULL
;
588 umtx_lock(&gZoneMetaLock
);
590 result
= (UVector
*) uhash_get(gOlsonToMeta
, tzidUChars
);
592 umtx_unlock(&gZoneMetaLock
);
594 if (result
!= NULL
) {
598 // miss the cache - create new one
599 UVector
*tmpResult
= createMetazoneMappings(tzid
);
600 if (tmpResult
== NULL
) {
605 // put the new one into the cache
606 umtx_lock(&gZoneMetaLock
);
608 // make sure it's already created
609 result
= (UVector
*) uhash_get(gOlsonToMeta
, tzidUChars
);
610 if (result
== NULL
) {
611 // add the one just created
612 int32_t tzidLen
= tzid
.length() + 1;
613 UChar
*key
= (UChar
*)uprv_malloc(tzidLen
* sizeof(UChar
));
615 // memory allocation error.. just return NULL
619 tzid
.extract(key
, tzidLen
, status
);
620 uhash_put(gOlsonToMeta
, key
, tmpResult
, &status
);
621 if (U_FAILURE(status
)) {
622 // delete the mapping
630 // another thread already put the one
634 umtx_unlock(&gZoneMetaLock
);
640 ZoneMeta::createMetazoneMappings(const UnicodeString
&tzid
) {
641 UVector
*mzMappings
= NULL
;
642 UErrorCode status
= U_ZERO_ERROR
;
644 UnicodeString canonicalID
;
645 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
646 ures_getByKey(rb
, gMetazoneInfo
, rb
, &status
);
647 getCanonicalCLDRID(tzid
, canonicalID
, status
);
649 if (U_SUCCESS(status
)) {
650 char tzKey
[ZID_KEY_MAX
+ 1];
651 int32_t tzKeyLen
= canonicalID
.extract(0, canonicalID
.length(), tzKey
, sizeof(tzKey
), US_INV
);
654 // tzid keys are using ':' as separators
663 ures_getByKey(rb
, tzKey
, rb
, &status
);
665 if (U_SUCCESS(status
)) {
666 UResourceBundle
*mz
= NULL
;
667 while (ures_hasNext(rb
)) {
668 mz
= ures_getNextResource(rb
, mz
, &status
);
670 const UChar
*mz_name
= ures_getStringByIndex(mz
, 0, NULL
, &status
);
671 const UChar
*mz_from
= gDefaultFrom
;
672 const UChar
*mz_to
= gDefaultTo
;
674 if (ures_getSize(mz
) == 3) {
675 mz_from
= ures_getStringByIndex(mz
, 1, NULL
, &status
);
676 mz_to
= ures_getStringByIndex(mz
, 2, NULL
, &status
);
679 if(U_FAILURE(status
)){
680 status
= U_ZERO_ERROR
;
683 // We do not want to use SimpleDateformat to parse boundary dates,
684 // because this code could be triggered by the initialization code
685 // used by SimpleDateFormat.
686 UDate from
= parseDate(mz_from
, status
);
687 UDate to
= parseDate(mz_to
, status
);
688 if (U_FAILURE(status
)) {
689 status
= U_ZERO_ERROR
;
693 OlsonToMetaMappingEntry
*entry
= (OlsonToMetaMappingEntry
*)uprv_malloc(sizeof(OlsonToMetaMappingEntry
));
695 status
= U_MEMORY_ALLOCATION_ERROR
;
698 entry
->mzid
= mz_name
;
702 if (mzMappings
== NULL
) {
703 mzMappings
= new UVector(deleteOlsonToMetaMappingEntry
, NULL
, status
);
704 if (U_FAILURE(status
)) {
706 deleteOlsonToMetaMappingEntry(entry
);
712 mzMappings
->addElement(entry
, status
);
713 if (U_FAILURE(status
)) {
718 if (U_FAILURE(status
)) {
719 if (mzMappings
!= NULL
) {
730 UnicodeString
& U_EXPORT2
731 ZoneMeta::getZoneIdByMetazone(const UnicodeString
&mzid
, const UnicodeString
®ion
, UnicodeString
&result
) {
732 UErrorCode status
= U_ZERO_ERROR
;
733 const UChar
*tzid
= NULL
;
735 char keyBuf
[ZID_KEY_MAX
+ 1];
738 if (mzid
.length() > ZID_KEY_MAX
) {
743 keyLen
= mzid
.extract(0, mzid
.length(), keyBuf
, ZID_KEY_MAX
+ 1, US_INV
);
746 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
747 ures_getByKey(rb
, gMapTimezonesTag
, rb
, &status
);
748 ures_getByKey(rb
, keyBuf
, rb
, &status
);
750 if (U_SUCCESS(status
)) {
751 // check region mapping
752 if (region
.length() == 2 || region
.length() == 3) {
753 keyLen
= region
.extract(0, region
.length(), keyBuf
, ZID_KEY_MAX
+ 1, US_INV
);
755 tzid
= ures_getStringByKey(rb
, keyBuf
, &tzidLen
, &status
);
756 if (status
== U_MISSING_RESOURCE_ERROR
) {
757 status
= U_ZERO_ERROR
;
760 if (U_SUCCESS(status
) && tzid
== NULL
) {
762 tzid
= ures_getStringByKey(rb
, gWorldTag
, &tzidLen
, &status
);
770 result
.setTo(tzid
, tzidLen
);
777 ZoneMeta::initAvailableMetaZoneIDs () {
779 UMTX_CHECK(&gZoneMetaLock
, gMetaZoneIDsInitialized
, initialized
);
781 umtx_lock(&gZoneMetaLock
);
783 if (!gMetaZoneIDsInitialized
) {
784 UErrorCode status
= U_ZERO_ERROR
;
785 UHashtable
*metaZoneIDTable
= uhash_open(uhash_hashUnicodeString
, uhash_compareUnicodeString
, NULL
, &status
);
786 uhash_setKeyDeleter(metaZoneIDTable
, uprv_deleteUObject
);
787 // No valueDeleter, because the vector maintain the value objects
788 UVector
*metaZoneIDs
= NULL
;
789 if (U_SUCCESS(status
)) {
790 metaZoneIDs
= new UVector(NULL
, uhash_compareUChars
, status
);
791 if (metaZoneIDs
== NULL
) {
792 status
= U_MEMORY_ALLOCATION_ERROR
;
795 uhash_close(metaZoneIDTable
);
797 if (U_SUCCESS(status
)) {
798 U_ASSERT(metaZoneIDs
!= NULL
);
799 metaZoneIDs
->setDeleter(uprv_free
);
801 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
802 UResourceBundle
*bundle
= ures_getByKey(rb
, gMapTimezonesTag
, NULL
, &status
);
804 ures_initStackObject(&res
);
805 while (U_SUCCESS(status
) && ures_hasNext(bundle
)) {
806 ures_getNextResource(bundle
, &res
, &status
);
807 if (U_FAILURE(status
)) {
810 const char *mzID
= ures_getKey(&res
);
811 int32_t len
= uprv_strlen(mzID
);
812 UChar
*uMzID
= (UChar
*)uprv_malloc(sizeof(UChar
) * (len
+ 1));
814 status
= U_MEMORY_ALLOCATION_ERROR
;
817 u_charsToUChars(mzID
, uMzID
, len
);
819 UnicodeString
*usMzID
= new UnicodeString(uMzID
);
820 if (uhash_get(metaZoneIDTable
, usMzID
) == NULL
) {
821 metaZoneIDs
->addElement((void *)uMzID
, status
);
822 uhash_put(metaZoneIDTable
, (void *)usMzID
, (void *)uMzID
, &status
);
828 if (U_SUCCESS(status
)) {
829 gMetaZoneIDs
= metaZoneIDs
;
830 gMetaZoneIDTable
= metaZoneIDTable
;
831 gMetaZoneIDsInitialized
= TRUE
;
832 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
834 uhash_close(metaZoneIDTable
);
843 umtx_unlock(&gZoneMetaLock
);
848 ZoneMeta::getAvailableMetazoneIDs() {
849 initAvailableMetaZoneIDs();
854 ZoneMeta::findMetaZoneID(const UnicodeString
& mzid
) {
855 initAvailableMetaZoneIDs();
856 return (const UChar
*)uhash_get(gMetaZoneIDTable
, &mzid
);
860 ZoneMeta::findTimeZoneID(const UnicodeString
& tzid
) {
861 return TimeZone::findID(tzid
);
866 ZoneMeta::createCustomTimeZone(int32_t offset
) {
867 UBool negative
= FALSE
;
868 int32_t tmp
= offset
;
873 int32_t hour
, min
, sec
;
882 formatCustomID(hour
, min
, sec
, negative
, zid
);
883 return new SimpleTimeZone(offset
, zid
);
887 ZoneMeta::formatCustomID(uint8_t hour
, uint8_t min
, uint8_t sec
, UBool negative
, UnicodeString
& id
) {
888 // Create normalized time zone ID - GMT[+|-]HH:mm[:ss]
889 id
.setTo(gCustomTzPrefix
, -1);
890 if (hour
!= 0 || min
!= 0) {
892 id
.append((UChar
)0x2D); // '-'
894 id
.append((UChar
)0x2B); // '+'
896 // Always use US-ASCII digits
897 id
.append((UChar
)(0x30 + (hour%100
)/10));
898 id
.append((UChar
)(0x30 + (hour%10
)));
899 id
.append((UChar
)0x3A); // ':'
900 id
.append((UChar
)(0x30 + (min%100
)/10));
901 id
.append((UChar
)(0x30 + (min%10
)));
903 id
.append((UChar
)0x3A); // ':'
904 id
.append((UChar
)(0x30 + (sec%100
)/10));
905 id
.append((UChar
)(0x30 + (sec%10
)));
912 ZoneMeta::getShortID(const TimeZone
& tz
) {
913 const UChar
* canonicalID
= NULL
;
914 if (dynamic_cast<const OlsonTimeZone
*>(&tz
) != NULL
) {
915 // short cut for OlsonTimeZone
916 const OlsonTimeZone
*otz
= (const OlsonTimeZone
*)&tz
;
917 canonicalID
= otz
->getCanonicalID();
919 if (canonicalID
== NULL
) {
922 return getShortIDFromCanonical(canonicalID
);
926 ZoneMeta::getShortID(const UnicodeString
& id
) {
927 UErrorCode status
= U_ZERO_ERROR
;
928 const UChar
* canonicalID
= ZoneMeta::getCanonicalCLDRID(id
, status
);
929 if (U_FAILURE(status
) || canonicalID
== NULL
) {
932 return ZoneMeta::getShortIDFromCanonical(canonicalID
);
936 ZoneMeta::getShortIDFromCanonical(const UChar
* canonicalID
) {
937 const UChar
* shortID
= NULL
;
938 int32_t len
= u_strlen(canonicalID
);
939 char tzidKey
[ZID_KEY_MAX
+ 1];
941 u_UCharsToChars(canonicalID
, tzidKey
, len
);
942 tzidKey
[len
] = (char) 0; // Make sure it is null terminated.
944 // replace '/' with ':'
952 UErrorCode status
= U_ZERO_ERROR
;
953 UResourceBundle
*rb
= ures_openDirect(NULL
, gKeyTypeData
, &status
);
954 ures_getByKey(rb
, gTypeMapTag
, rb
, &status
);
955 ures_getByKey(rb
, gTimezoneTag
, rb
, &status
);
956 shortID
= ures_getStringByKey(rb
, tzidKey
, NULL
, &status
);
964 #endif /* #if !UCONFIG_NO_FORMATTING */