2 *******************************************************************************
3 * Copyright (C) 2007-2014, 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 icu::UInitOnce gCanonicalIDCacheInitOnce
= U_INITONCE_INITIALIZER
;
36 // Metazone mapping table
37 static UHashtable
*gOlsonToMeta
= NULL
;
38 static icu::UInitOnce gOlsonToMetaInitOnce
= U_INITONCE_INITIALIZER
;
40 // Available metazone IDs vector and table
41 static icu::UVector
*gMetaZoneIDs
= NULL
;
42 static UHashtable
*gMetaZoneIDTable
= NULL
;
43 static icu::UInitOnce gMetaZoneIDsInitOnce
= U_INITONCE_INITIALIZER
;
45 // Country info vectors
46 static icu::UVector
*gSingleZoneCountries
= NULL
;
47 static icu::UVector
*gMultiZonesCountries
= NULL
;
48 static icu::UInitOnce gCountryInfoVectorsInitOnce
= U_INITONCE_INITIALIZER
;
53 * Cleanup callback func
55 static UBool U_CALLCONV
zoneMeta_cleanup(void)
57 if (gCanonicalIDCache
!= NULL
) {
58 uhash_close(gCanonicalIDCache
);
59 gCanonicalIDCache
= NULL
;
61 gCanonicalIDCacheInitOnce
.reset();
63 if (gOlsonToMeta
!= NULL
) {
64 uhash_close(gOlsonToMeta
);
67 gOlsonToMetaInitOnce
.reset();
69 if (gMetaZoneIDTable
!= NULL
) {
70 uhash_close(gMetaZoneIDTable
);
71 gMetaZoneIDTable
= NULL
;
73 // delete after closing gMetaZoneIDTable, because it holds
74 // value objects held by the hashtable
77 gMetaZoneIDsInitOnce
.reset();
79 delete gSingleZoneCountries
;
80 gSingleZoneCountries
= NULL
;
81 delete gMultiZonesCountries
;
82 gMultiZonesCountries
= NULL
;
83 gCountryInfoVectorsInitOnce
.reset();
89 * Deleter for UChar* string
91 static void U_CALLCONV
92 deleteUCharString(void *obj
) {
93 UChar
*entry
= (UChar
*)obj
;
100 static void U_CALLCONV
101 deleteUVector(void *obj
) {
102 delete (icu::UVector
*) obj
;
106 * Deleter for OlsonToMetaMappingEntry
108 static void U_CALLCONV
109 deleteOlsonToMetaMappingEntry(void *obj
) {
110 icu::OlsonToMetaMappingEntry
*entry
= (icu::OlsonToMetaMappingEntry
*)obj
;
118 #define ZID_KEY_MAX 128
120 static const char gMetaZones
[] = "metaZones";
121 static const char gMetazoneInfo
[] = "metazoneInfo";
122 static const char gMapTimezonesTag
[] = "mapTimezones";
124 static const char gKeyTypeData
[] = "keyTypeData";
125 static const char gTypeAliasTag
[] = "typeAlias";
126 static const char gTypeMapTag
[] = "typeMap";
127 static const char gTimezoneTag
[] = "timezone";
129 static const char gPrimaryZonesTag
[] = "primaryZones";
131 static const char gWorldTag
[] = "001";
133 static const UChar gWorld
[] = {0x30, 0x30, 0x31, 0x00}; // "001"
135 static const UChar gDefaultFrom
[] = {0x31, 0x39, 0x37, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31,
136 0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x00}; // "1970-01-01 00:00"
137 static const UChar gDefaultTo
[] = {0x39, 0x39, 0x39, 0x39, 0x2D, 0x31, 0x32, 0x2D, 0x33, 0x31,
138 0x20, 0x32, 0x33, 0x3A, 0x35, 0x39, 0x00}; // "9999-12-31 23:59"
140 static const UChar gCustomTzPrefix
[] = {0x47, 0x4D, 0x54, 0}; // "GMT"
142 #define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1)
145 * Convert a date string used by metazone mappings to UDate.
146 * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
149 parseDate (const UChar
*text
, UErrorCode
&status
) {
150 if (U_FAILURE(status
)) {
153 int32_t len
= u_strlen(text
);
154 if (len
!= 16 && len
!= 10) {
155 // It must be yyyy-MM-dd HH:mm (length 16) or yyyy-MM-dd (length 10)
156 status
= U_INVALID_FORMAT_ERROR
;
160 int32_t year
= 0, month
= 0, day
= 0, hour
= 0, min
= 0, n
;
164 for (idx
= 0; idx
<= 3 && U_SUCCESS(status
); idx
++) {
165 n
= ASCII_DIGIT((int32_t)text
[idx
]);
169 status
= U_INVALID_FORMAT_ERROR
;
173 for (idx
= 5; idx
<= 6 && U_SUCCESS(status
); idx
++) {
174 n
= ASCII_DIGIT((int32_t)text
[idx
]);
176 month
= 10*month
+ n
;
178 status
= U_INVALID_FORMAT_ERROR
;
182 for (idx
= 8; idx
<= 9 && U_SUCCESS(status
); idx
++) {
183 n
= ASCII_DIGIT((int32_t)text
[idx
]);
187 status
= U_INVALID_FORMAT_ERROR
;
192 for (idx
= 11; idx
<= 12 && U_SUCCESS(status
); idx
++) {
193 n
= ASCII_DIGIT((int32_t)text
[idx
]);
197 status
= U_INVALID_FORMAT_ERROR
;
201 for (idx
= 14; idx
<= 15 && U_SUCCESS(status
); idx
++) {
202 n
= ASCII_DIGIT((int32_t)text
[idx
]);
206 status
= U_INVALID_FORMAT_ERROR
;
211 if (U_SUCCESS(status
)) {
212 UDate date
= Grego::fieldsToDay(year
, month
- 1, day
) * U_MILLIS_PER_DAY
213 + hour
* U_MILLIS_PER_HOUR
+ min
* U_MILLIS_PER_MINUTE
;
219 static void U_CALLCONV
initCanonicalIDCache(UErrorCode
&status
) {
220 gCanonicalIDCache
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
221 if (gCanonicalIDCache
== NULL
) {
222 status
= U_MEMORY_ALLOCATION_ERROR
;
224 if (U_FAILURE(status
)) {
225 gCanonicalIDCache
= NULL
;
227 // No key/value deleters - keys/values are from a resource bundle
228 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
232 const UChar
* U_EXPORT2
233 ZoneMeta::getCanonicalCLDRID(const UnicodeString
&tzid
, UErrorCode
& status
) {
234 if (U_FAILURE(status
)) {
238 if (tzid
.isBogus() || tzid
.length() > ZID_KEY_MAX
) {
239 status
= U_ILLEGAL_ARGUMENT_ERROR
;
243 // Checking the cached results
244 umtx_initOnce(gCanonicalIDCacheInitOnce
, &initCanonicalIDCache
, status
);
245 if (U_FAILURE(status
)) {
249 const UChar
*canonicalID
= NULL
;
251 UErrorCode tmpStatus
= U_ZERO_ERROR
;
252 UChar utzid
[ZID_KEY_MAX
+ 1];
253 tzid
.extract(utzid
, ZID_KEY_MAX
+ 1, tmpStatus
);
254 U_ASSERT(tmpStatus
== U_ZERO_ERROR
); // we checked the length of tzid already
256 // Check if it was already cached
257 umtx_lock(&gZoneMetaLock
);
259 canonicalID
= (const UChar
*)uhash_get(gCanonicalIDCache
, utzid
);
261 umtx_unlock(&gZoneMetaLock
);
263 if (canonicalID
!= NULL
) {
267 // If not, resolve CLDR canonical ID with resource data
268 UBool isInputCanonical
= FALSE
;
269 char id
[ZID_KEY_MAX
+ 1];
270 tzid
.extract(0, 0x7fffffff, id
, UPRV_LENGTHOF(id
), US_INV
);
272 // replace '/' with ':'
280 UResourceBundle
*top
= ures_openDirect(NULL
, gKeyTypeData
, &tmpStatus
);
281 UResourceBundle
*rb
= ures_getByKey(top
, gTypeMapTag
, NULL
, &tmpStatus
);
282 ures_getByKey(rb
, gTimezoneTag
, rb
, &tmpStatus
);
283 ures_getByKey(rb
, id
, rb
, &tmpStatus
);
284 if (U_SUCCESS(tmpStatus
)) {
285 // type entry (canonical) found
286 // the input is the canonical ID. resolve to const UChar*
287 canonicalID
= TimeZone::findID(tzid
);
288 isInputCanonical
= TRUE
;
291 if (canonicalID
== NULL
) {
292 // If a map element not found, then look for an alias
293 tmpStatus
= U_ZERO_ERROR
;
294 ures_getByKey(top
, gTypeAliasTag
, rb
, &tmpStatus
);
295 ures_getByKey(rb
, gTimezoneTag
, rb
, &tmpStatus
);
296 const UChar
*canonical
= ures_getStringByKey(rb
,id
,NULL
,&tmpStatus
);
297 if (U_SUCCESS(tmpStatus
)) {
298 // canonical map found
299 canonicalID
= canonical
;
302 if (canonicalID
== NULL
) {
303 // Dereference the input ID using the tz data
304 const UChar
*derefer
= TimeZone::dereferOlsonLink(tzid
);
305 if (derefer
== NULL
) {
306 status
= U_ILLEGAL_ARGUMENT_ERROR
;
308 int32_t len
= u_strlen(derefer
);
309 u_UCharsToChars(derefer
,id
,len
);
310 id
[len
] = (char) 0; // Make sure it is null terminated.
312 // replace '/' with ':'
320 // If a dereference turned something up then look for an alias.
321 // rb still points to the alias table, so we don't have to go looking
323 tmpStatus
= U_ZERO_ERROR
;
324 canonical
= ures_getStringByKey(rb
,id
,NULL
,&tmpStatus
);
325 if (U_SUCCESS(tmpStatus
)) {
326 // canonical map for the dereferenced ID found
327 canonicalID
= canonical
;
329 canonicalID
= derefer
;
330 isInputCanonical
= TRUE
;
338 if (U_SUCCESS(status
)) {
339 U_ASSERT(canonicalID
!= NULL
); // canocanilD must be non-NULL here
341 // Put the resolved canonical ID to the cache
342 umtx_lock(&gZoneMetaLock
);
344 const UChar
* idInCache
= (const UChar
*)uhash_get(gCanonicalIDCache
, utzid
);
345 if (idInCache
== NULL
) {
346 const UChar
* key
= ZoneMeta::findTimeZoneID(tzid
);
347 U_ASSERT(key
!= NULL
);
349 idInCache
= (const UChar
*)uhash_put(gCanonicalIDCache
, (void *)key
, (void *)canonicalID
, &status
);
350 U_ASSERT(idInCache
== NULL
);
353 if (U_SUCCESS(status
) && isInputCanonical
) {
354 // Also put canonical ID itself into the cache if not exist
355 const UChar
*canonicalInCache
= (const UChar
*)uhash_get(gCanonicalIDCache
, canonicalID
);
356 if (canonicalInCache
== NULL
) {
357 canonicalInCache
= (const UChar
*)uhash_put(gCanonicalIDCache
, (void *)canonicalID
, (void *)canonicalID
, &status
);
358 U_ASSERT(canonicalInCache
== NULL
);
362 umtx_unlock(&gZoneMetaLock
);
368 UnicodeString
& U_EXPORT2
369 ZoneMeta::getCanonicalCLDRID(const UnicodeString
&tzid
, UnicodeString
&systemID
, UErrorCode
& status
) {
370 const UChar
*canonicalID
= getCanonicalCLDRID(tzid
, status
);
371 if (U_FAILURE(status
) || canonicalID
== NULL
) {
372 systemID
.setToBogus();
375 systemID
.setTo(TRUE
, canonicalID
, -1);
379 const UChar
* U_EXPORT2
380 ZoneMeta::getCanonicalCLDRID(const TimeZone
& tz
) {
381 if (dynamic_cast<const OlsonTimeZone
*>(&tz
) != NULL
) {
382 // short cut for OlsonTimeZone
383 const OlsonTimeZone
*otz
= (const OlsonTimeZone
*)&tz
;
384 return otz
->getCanonicalID();
386 UErrorCode status
= U_ZERO_ERROR
;
388 return getCanonicalCLDRID(tz
.getID(tzID
), status
);
391 static void U_CALLCONV
countryInfoVectorsInit(UErrorCode
&status
) {
392 // Create empty vectors
393 // No deleters for these UVectors, it's a reference to a resource bundle string.
394 gSingleZoneCountries
= new UVector(NULL
, uhash_compareUChars
, status
);
395 if (gSingleZoneCountries
== NULL
) {
396 status
= U_MEMORY_ALLOCATION_ERROR
;
398 gMultiZonesCountries
= new UVector(NULL
, uhash_compareUChars
, status
);
399 if (gMultiZonesCountries
== NULL
) {
400 status
= U_MEMORY_ALLOCATION_ERROR
;
403 if (U_FAILURE(status
)) {
404 delete gSingleZoneCountries
;
405 delete gMultiZonesCountries
;
406 gSingleZoneCountries
= NULL
;
407 gMultiZonesCountries
= NULL
;
409 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
413 UnicodeString
& U_EXPORT2
414 ZoneMeta::getCanonicalCountry(const UnicodeString
&tzid
, UnicodeString
&country
, UBool
*isPrimary
/* = NULL */) {
415 if (isPrimary
!= NULL
) {
419 const UChar
*region
= TimeZone::getRegion(tzid
);
420 if (region
!= NULL
&& u_strcmp(gWorld
, region
) != 0) {
421 country
.setTo(region
, -1);
423 country
.setToBogus();
427 if (isPrimary
!= NULL
) {
428 char regionBuf
[] = {0, 0, 0};
430 // Checking the cached results
431 UErrorCode status
= U_ZERO_ERROR
;
432 umtx_initOnce(gCountryInfoVectorsInitOnce
, &countryInfoVectorsInit
, status
);
433 if (U_FAILURE(status
)) {
437 // Check if it was already cached
438 UBool cached
= FALSE
;
439 UBool singleZone
= FALSE
;
440 umtx_lock(&gZoneMetaLock
);
442 singleZone
= cached
= gSingleZoneCountries
->contains((void*)region
);
444 cached
= gMultiZonesCountries
->contains((void*)region
);
447 umtx_unlock(&gZoneMetaLock
);
450 // We need to go through all zones associated with the region.
451 // This is relatively heavy operation.
453 U_ASSERT(u_strlen(region
) == 2);
455 u_UCharsToChars(region
, regionBuf
, 2);
457 StringEnumeration
*ids
= TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION
, regionBuf
, NULL
, status
);
458 int32_t idsLen
= ids
->count(status
);
459 if (U_SUCCESS(status
) && idsLen
== 1) {
460 // only the single zone is available for the region
466 umtx_lock(&gZoneMetaLock
);
468 UErrorCode ec
= U_ZERO_ERROR
;
470 if (!gSingleZoneCountries
->contains((void*)region
)) {
471 gSingleZoneCountries
->addElement((void*)region
, ec
);
474 if (!gMultiZonesCountries
->contains((void*)region
)) {
475 gMultiZonesCountries
->addElement((void*)region
, ec
);
479 umtx_unlock(&gZoneMetaLock
);
485 // Note: We may cache the primary zone map in future.
487 // Even a country has multiple zones, one of them might be
488 // dominant and treated as a primary zone
490 if (regionBuf
[0] == 0) {
491 u_UCharsToChars(region
, regionBuf
, 2);
494 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
495 ures_getByKey(rb
, gPrimaryZonesTag
, rb
, &status
);
496 const UChar
*primaryZone
= ures_getStringByKey(rb
, regionBuf
, &idLen
, &status
);
497 if (U_SUCCESS(status
)) {
498 if (tzid
.compare(primaryZone
, idLen
) == 0) {
501 // The given ID might not be a canonical ID
502 UnicodeString canonicalID
;
503 TimeZone::getCanonicalID(tzid
, canonicalID
, status
);
504 if (U_SUCCESS(status
) && canonicalID
.compare(primaryZone
, idLen
) == 0) {
516 UnicodeString
& U_EXPORT2
517 ZoneMeta::getMetazoneID(const UnicodeString
&tzid
, UDate date
, UnicodeString
&result
) {
519 const UVector
*mappings
= getMetazoneMappings(tzid
);
520 if (mappings
!= NULL
) {
521 for (int32_t i
= 0; i
< mappings
->size(); i
++) {
522 OlsonToMetaMappingEntry
*mzm
= (OlsonToMetaMappingEntry
*)mappings
->elementAt(i
);
523 if (mzm
->from
<= date
&& mzm
->to
> date
) {
524 result
.setTo(mzm
->mzid
, -1);
536 static void U_CALLCONV
olsonToMetaInit(UErrorCode
&status
) {
537 U_ASSERT(gOlsonToMeta
== NULL
);
538 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
539 gOlsonToMeta
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
540 if (U_FAILURE(status
)) {
543 uhash_setKeyDeleter(gOlsonToMeta
, deleteUCharString
);
544 uhash_setValueDeleter(gOlsonToMeta
, deleteUVector
);
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
) {
558 umtx_initOnce(gOlsonToMetaInitOnce
, &olsonToMetaInit
, status
);
559 if (U_FAILURE(status
)) {
563 // get the mapping from cache
564 const UVector
*result
= NULL
;
566 umtx_lock(&gZoneMetaLock
);
568 result
= (UVector
*) uhash_get(gOlsonToMeta
, tzidUChars
);
570 umtx_unlock(&gZoneMetaLock
);
572 if (result
!= NULL
) {
576 // miss the cache - create new one
577 UVector
*tmpResult
= createMetazoneMappings(tzid
);
578 if (tmpResult
== NULL
) {
583 // put the new one into the cache
584 umtx_lock(&gZoneMetaLock
);
586 // make sure it's already created
587 result
= (UVector
*) uhash_get(gOlsonToMeta
, tzidUChars
);
588 if (result
== NULL
) {
589 // add the one just created
590 int32_t tzidLen
= tzid
.length() + 1;
591 UChar
*key
= (UChar
*)uprv_malloc(tzidLen
* sizeof(UChar
));
593 // memory allocation error.. just return NULL
597 tzid
.extract(key
, tzidLen
, status
);
598 uhash_put(gOlsonToMeta
, key
, tmpResult
, &status
);
599 if (U_FAILURE(status
)) {
600 // delete the mapping
608 // another thread already put the one
612 umtx_unlock(&gZoneMetaLock
);
618 ZoneMeta::createMetazoneMappings(const UnicodeString
&tzid
) {
619 UVector
*mzMappings
= NULL
;
620 UErrorCode status
= U_ZERO_ERROR
;
622 UnicodeString canonicalID
;
623 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
624 ures_getByKey(rb
, gMetazoneInfo
, rb
, &status
);
625 getCanonicalCLDRID(tzid
, canonicalID
, status
);
627 if (U_SUCCESS(status
)) {
628 char tzKey
[ZID_KEY_MAX
+ 1];
629 int32_t tzKeyLen
= canonicalID
.extract(0, canonicalID
.length(), tzKey
, sizeof(tzKey
), US_INV
);
632 // tzid keys are using ':' as separators
641 ures_getByKey(rb
, tzKey
, rb
, &status
);
643 if (U_SUCCESS(status
)) {
644 UResourceBundle
*mz
= NULL
;
645 while (ures_hasNext(rb
)) {
646 mz
= ures_getNextResource(rb
, mz
, &status
);
648 const UChar
*mz_name
= ures_getStringByIndex(mz
, 0, NULL
, &status
);
649 const UChar
*mz_from
= gDefaultFrom
;
650 const UChar
*mz_to
= gDefaultTo
;
652 if (ures_getSize(mz
) == 3) {
653 mz_from
= ures_getStringByIndex(mz
, 1, NULL
, &status
);
654 mz_to
= ures_getStringByIndex(mz
, 2, NULL
, &status
);
657 if(U_FAILURE(status
)){
658 status
= U_ZERO_ERROR
;
661 // We do not want to use SimpleDateformat to parse boundary dates,
662 // because this code could be triggered by the initialization code
663 // used by SimpleDateFormat.
664 UDate from
= parseDate(mz_from
, status
);
665 UDate to
= parseDate(mz_to
, status
);
666 if (U_FAILURE(status
)) {
667 status
= U_ZERO_ERROR
;
671 OlsonToMetaMappingEntry
*entry
= (OlsonToMetaMappingEntry
*)uprv_malloc(sizeof(OlsonToMetaMappingEntry
));
673 status
= U_MEMORY_ALLOCATION_ERROR
;
676 entry
->mzid
= mz_name
;
680 if (mzMappings
== NULL
) {
681 mzMappings
= new UVector(deleteOlsonToMetaMappingEntry
, NULL
, status
);
682 if (U_FAILURE(status
)) {
684 deleteOlsonToMetaMappingEntry(entry
);
690 mzMappings
->addElement(entry
, status
);
691 if (U_FAILURE(status
)) {
696 if (U_FAILURE(status
)) {
697 if (mzMappings
!= NULL
) {
708 UnicodeString
& U_EXPORT2
709 ZoneMeta::getZoneIdByMetazone(const UnicodeString
&mzid
, const UnicodeString
®ion
, UnicodeString
&result
) {
710 UErrorCode status
= U_ZERO_ERROR
;
711 const UChar
*tzid
= NULL
;
713 char keyBuf
[ZID_KEY_MAX
+ 1];
716 if (mzid
.isBogus() || mzid
.length() > ZID_KEY_MAX
) {
721 keyLen
= mzid
.extract(0, mzid
.length(), keyBuf
, ZID_KEY_MAX
+ 1, US_INV
);
724 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
725 ures_getByKey(rb
, gMapTimezonesTag
, rb
, &status
);
726 ures_getByKey(rb
, keyBuf
, rb
, &status
);
728 if (U_SUCCESS(status
)) {
729 // check region mapping
730 if (region
.length() == 2 || region
.length() == 3) {
731 keyLen
= region
.extract(0, region
.length(), keyBuf
, ZID_KEY_MAX
+ 1, US_INV
);
733 tzid
= ures_getStringByKey(rb
, keyBuf
, &tzidLen
, &status
);
734 if (status
== U_MISSING_RESOURCE_ERROR
) {
735 status
= U_ZERO_ERROR
;
738 if (U_SUCCESS(status
) && tzid
== NULL
) {
740 tzid
= ures_getStringByKey(rb
, gWorldTag
, &tzidLen
, &status
);
748 result
.setTo(tzid
, tzidLen
);
754 static void U_CALLCONV
initAvailableMetaZoneIDs () {
755 U_ASSERT(gMetaZoneIDs
== NULL
);
756 U_ASSERT(gMetaZoneIDTable
== NULL
);
757 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
759 UErrorCode status
= U_ZERO_ERROR
;
760 gMetaZoneIDTable
= uhash_open(uhash_hashUnicodeString
, uhash_compareUnicodeString
, NULL
, &status
);
761 if (U_FAILURE(status
) || gMetaZoneIDTable
== NULL
) {
762 gMetaZoneIDTable
= NULL
;
765 uhash_setKeyDeleter(gMetaZoneIDTable
, uprv_deleteUObject
);
766 // No valueDeleter, because the vector maintain the value objects
767 gMetaZoneIDs
= new UVector(NULL
, uhash_compareUChars
, status
);
768 if (U_FAILURE(status
) || gMetaZoneIDs
== NULL
) {
770 uhash_close(gMetaZoneIDTable
);
771 gMetaZoneIDTable
= NULL
;
774 gMetaZoneIDs
->setDeleter(uprv_free
);
776 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
777 UResourceBundle
*bundle
= ures_getByKey(rb
, gMapTimezonesTag
, NULL
, &status
);
779 ures_initStackObject(&res
);
780 while (U_SUCCESS(status
) && ures_hasNext(bundle
)) {
781 ures_getNextResource(bundle
, &res
, &status
);
782 if (U_FAILURE(status
)) {
785 const char *mzID
= ures_getKey(&res
);
786 int32_t len
= uprv_strlen(mzID
);
787 UChar
*uMzID
= (UChar
*)uprv_malloc(sizeof(UChar
) * (len
+ 1));
789 status
= U_MEMORY_ALLOCATION_ERROR
;
792 u_charsToUChars(mzID
, uMzID
, len
);
794 UnicodeString
*usMzID
= new UnicodeString(uMzID
);
795 if (uhash_get(gMetaZoneIDTable
, usMzID
) == NULL
) {
796 gMetaZoneIDs
->addElement((void *)uMzID
, status
);
797 uhash_put(gMetaZoneIDTable
, (void *)usMzID
, (void *)uMzID
, &status
);
807 if (U_FAILURE(status
)) {
808 uhash_close(gMetaZoneIDTable
);
810 gMetaZoneIDTable
= NULL
;
816 ZoneMeta::getAvailableMetazoneIDs() {
817 umtx_initOnce(gMetaZoneIDsInitOnce
, &initAvailableMetaZoneIDs
);
822 ZoneMeta::findMetaZoneID(const UnicodeString
& mzid
) {
823 umtx_initOnce(gMetaZoneIDsInitOnce
, &initAvailableMetaZoneIDs
);
824 if (gMetaZoneIDTable
== NULL
) {
827 return (const UChar
*)uhash_get(gMetaZoneIDTable
, &mzid
);
831 ZoneMeta::findTimeZoneID(const UnicodeString
& tzid
) {
832 return TimeZone::findID(tzid
);
837 ZoneMeta::createCustomTimeZone(int32_t offset
) {
838 UBool negative
= FALSE
;
839 int32_t tmp
= offset
;
844 int32_t hour
, min
, sec
;
853 formatCustomID(hour
, min
, sec
, negative
, zid
);
854 return new SimpleTimeZone(offset
, zid
);
858 ZoneMeta::formatCustomID(uint8_t hour
, uint8_t min
, uint8_t sec
, UBool negative
, UnicodeString
& id
) {
859 // Create normalized time zone ID - GMT[+|-]HH:mm[:ss]
860 id
.setTo(gCustomTzPrefix
, -1);
861 if (hour
!= 0 || min
!= 0) {
863 id
.append((UChar
)0x2D); // '-'
865 id
.append((UChar
)0x2B); // '+'
867 // Always use US-ASCII digits
868 id
.append((UChar
)(0x30 + (hour%100
)/10));
869 id
.append((UChar
)(0x30 + (hour%10
)));
870 id
.append((UChar
)0x3A); // ':'
871 id
.append((UChar
)(0x30 + (min%100
)/10));
872 id
.append((UChar
)(0x30 + (min%10
)));
874 id
.append((UChar
)0x3A); // ':'
875 id
.append((UChar
)(0x30 + (sec%100
)/10));
876 id
.append((UChar
)(0x30 + (sec%10
)));
883 ZoneMeta::getShortID(const TimeZone
& tz
) {
884 const UChar
* canonicalID
= NULL
;
885 if (dynamic_cast<const OlsonTimeZone
*>(&tz
) != NULL
) {
886 // short cut for OlsonTimeZone
887 const OlsonTimeZone
*otz
= (const OlsonTimeZone
*)&tz
;
888 canonicalID
= otz
->getCanonicalID();
890 if (canonicalID
== NULL
) {
893 return getShortIDFromCanonical(canonicalID
);
897 ZoneMeta::getShortID(const UnicodeString
& id
) {
898 UErrorCode status
= U_ZERO_ERROR
;
899 const UChar
* canonicalID
= ZoneMeta::getCanonicalCLDRID(id
, status
);
900 if (U_FAILURE(status
) || canonicalID
== NULL
) {
903 return ZoneMeta::getShortIDFromCanonical(canonicalID
);
907 ZoneMeta::getShortIDFromCanonical(const UChar
* canonicalID
) {
908 const UChar
* shortID
= NULL
;
909 int32_t len
= u_strlen(canonicalID
);
910 char tzidKey
[ZID_KEY_MAX
+ 1];
912 u_UCharsToChars(canonicalID
, tzidKey
, len
);
913 tzidKey
[len
] = (char) 0; // Make sure it is null terminated.
915 // replace '/' with ':'
923 UErrorCode status
= U_ZERO_ERROR
;
924 UResourceBundle
*rb
= ures_openDirect(NULL
, gKeyTypeData
, &status
);
925 ures_getByKey(rb
, gTypeMapTag
, rb
, &status
);
926 ures_getByKey(rb
, gTimezoneTag
, rb
, &status
);
927 shortID
= ures_getStringByKey(rb
, tzidKey
, NULL
, &status
);
935 #endif /* #if !UCONFIG_NO_FORMATTING */