2 *******************************************************************************
3 * Copyright (C) 2007-2012, 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 UMTX gZoneMetaLock
= NULL
;
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 umtx_destroy(&gZoneMetaLock
);
59 if (gCanonicalIDCache
!= NULL
) {
60 uhash_close(gCanonicalIDCache
);
61 gCanonicalIDCache
= NULL
;
63 gCanonicalIDCacheInitialized
= FALSE
;
65 if (gOlsonToMeta
!= NULL
) {
66 uhash_close(gOlsonToMeta
);
69 gOlsonToMetaInitialized
= FALSE
;
71 if (gMetaZoneIDTable
!= NULL
) {
72 uhash_close(gMetaZoneIDTable
);
74 // delete after closing gMetaZoneIDTable, because it holds
75 // value objects held by the hashtable
77 gMetaZoneIDsInitialized
= FALSE
;
79 delete gSingleZoneCountries
;
80 delete gMultiZonesCountries
;
81 gCountryInfoVectorsInitialized
= FALSE
;
87 * Deleter for UChar* string
89 static void U_CALLCONV
90 deleteUCharString(void *obj
) {
91 UChar
*entry
= (UChar
*)obj
;
98 static void U_CALLCONV
99 deleteUVector(void *obj
) {
100 delete (icu::UVector
*) obj
;
104 * Deleter for OlsonToMetaMappingEntry
106 static void U_CALLCONV
107 deleteOlsonToMetaMappingEntry(void *obj
) {
108 icu::OlsonToMetaMappingEntry
*entry
= (icu::OlsonToMetaMappingEntry
*)obj
;
116 #define ZID_KEY_MAX 128
118 static const char gMetaZones
[] = "metaZones";
119 static const char gMetazoneInfo
[] = "metazoneInfo";
120 static const char gMapTimezonesTag
[] = "mapTimezones";
122 static const char gTimeZoneTypes
[] = "timezoneTypes";
123 static const char gTypeAliasTag
[] = "typeAlias";
124 static const char gTypeMapTag
[] = "typeMap";
125 static const char gTimezoneTag
[] = "timezone";
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
, gTimeZoneTypes
, &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
);
398 UnicodeString
& U_EXPORT2
399 ZoneMeta::getCanonicalCountry(const UnicodeString
&tzid
, UnicodeString
&canonicalCountry
) {
400 const UChar
*region
= TimeZone::getRegion(tzid
);
401 if (region
!= NULL
&& u_strcmp(gWorld
, region
) != 0) {
402 canonicalCountry
.setTo(region
, -1);
404 canonicalCountry
.setToBogus();
406 return canonicalCountry
;
409 UnicodeString
& U_EXPORT2
410 ZoneMeta::getSingleCountry(const UnicodeString
&tzid
, UnicodeString
&country
) {
411 // Get canonical country for the zone
412 const UChar
*region
= TimeZone::getRegion(tzid
);
413 if (region
== NULL
|| u_strcmp(gWorld
, region
) == 0) {
414 // special case - unknown or "001"
415 country
.setToBogus();
419 // Checking the cached results
420 UErrorCode status
= U_ZERO_ERROR
;
422 UMTX_CHECK(&gZoneMetaLock
, gCountryInfoVectorsInitialized
, initialized
);
424 // Create empty vectors
425 umtx_lock(&gZoneMetaLock
);
427 if (!gCountryInfoVectorsInitialized
) {
428 // No deleters for these UVectors, it's a reference to a resource bundle string.
429 gSingleZoneCountries
= new UVector(NULL
, uhash_compareUChars
, status
);
430 if (gSingleZoneCountries
== NULL
) {
431 status
= U_MEMORY_ALLOCATION_ERROR
;
433 gMultiZonesCountries
= new UVector(NULL
, uhash_compareUChars
, status
);
434 if (gMultiZonesCountries
== NULL
) {
435 status
= U_MEMORY_ALLOCATION_ERROR
;
438 if (U_SUCCESS(status
)) {
439 gCountryInfoVectorsInitialized
= TRUE
;
441 delete gSingleZoneCountries
;
442 delete gMultiZonesCountries
;
446 umtx_unlock(&gZoneMetaLock
);
448 if (U_FAILURE(status
)) {
449 country
.setToBogus();
452 U_ASSERT(gSingleZoneCountries
!= NULL
);
453 U_ASSERT(gMultiZonesCountries
!= NULL
);
456 // Check if it was already cached
457 UBool cached
= FALSE
;
458 UBool multiZones
= FALSE
;
459 umtx_lock(&gZoneMetaLock
);
461 multiZones
= cached
= gMultiZonesCountries
->contains((void*)region
);
463 cached
= gSingleZoneCountries
->contains((void*)region
);
466 umtx_unlock(&gZoneMetaLock
);
469 // We need to go through all zones associated with the region.
470 // This is relatively heavy operation.
472 U_ASSERT(u_strlen(region
) == 2);
474 char buf
[] = {0, 0, 0};
475 u_UCharsToChars(region
, buf
, 2);
477 StringEnumeration
*ids
= TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION
, buf
, NULL
, status
);
478 int32_t idsLen
= ids
->count(status
);
479 if (U_SUCCESS(status
) && idsLen
> 1) {
480 // multiple canonical zones are available for the region
483 if (U_FAILURE(status
)) {
484 // no single country by default for any error cases
490 umtx_lock(&gZoneMetaLock
);
492 UErrorCode ec
= U_ZERO_ERROR
;
494 if (!gMultiZonesCountries
->contains((void*)region
)) {
495 gMultiZonesCountries
->addElement((void*)region
, ec
);
498 if (!gSingleZoneCountries
->contains((void*)region
)) {
499 gSingleZoneCountries
->addElement((void*)region
, ec
);
503 umtx_unlock(&gZoneMetaLock
);
507 country
.setToBogus();
509 country
.setTo(region
, -1);
514 UnicodeString
& U_EXPORT2
515 ZoneMeta::getMetazoneID(const UnicodeString
&tzid
, UDate date
, UnicodeString
&result
) {
517 const UVector
*mappings
= getMetazoneMappings(tzid
);
518 if (mappings
!= NULL
) {
519 for (int32_t i
= 0; i
< mappings
->size(); i
++) {
520 OlsonToMetaMappingEntry
*mzm
= (OlsonToMetaMappingEntry
*)mappings
->elementAt(i
);
521 if (mzm
->from
<= date
&& mzm
->to
> date
) {
522 result
.setTo(mzm
->mzid
, -1);
534 const UVector
* U_EXPORT2
535 ZoneMeta::getMetazoneMappings(const UnicodeString
&tzid
) {
536 UErrorCode status
= U_ZERO_ERROR
;
537 UChar tzidUChars
[ZID_KEY_MAX
+ 1];
538 tzid
.extract(tzidUChars
, ZID_KEY_MAX
+ 1, status
);
539 if (U_FAILURE(status
) || status
== U_STRING_NOT_TERMINATED_WARNING
) {
544 UMTX_CHECK(&gZoneMetaLock
, gOlsonToMetaInitialized
, initialized
);
546 UHashtable
*tmpOlsonToMeta
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
547 if (U_FAILURE(status
)) {
550 uhash_setKeyDeleter(tmpOlsonToMeta
, deleteUCharString
);
551 uhash_setValueDeleter(tmpOlsonToMeta
, deleteUVector
);
553 umtx_lock(&gZoneMetaLock
);
555 if (!gOlsonToMetaInitialized
) {
556 gOlsonToMeta
= tmpOlsonToMeta
;
557 tmpOlsonToMeta
= NULL
;
558 gOlsonToMetaInitialized
= TRUE
;
561 umtx_unlock(&gZoneMetaLock
);
563 // OK to call the following multiple times with the same function
564 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
565 if (tmpOlsonToMeta
!= NULL
) {
566 uhash_close(tmpOlsonToMeta
);
570 // get the mapping from cache
571 const UVector
*result
= NULL
;
573 umtx_lock(&gZoneMetaLock
);
575 result
= (UVector
*) uhash_get(gOlsonToMeta
, tzidUChars
);
577 umtx_unlock(&gZoneMetaLock
);
579 if (result
!= NULL
) {
583 // miss the cache - create new one
584 UVector
*tmpResult
= createMetazoneMappings(tzid
);
585 if (tmpResult
== NULL
) {
590 // put the new one into the cache
591 umtx_lock(&gZoneMetaLock
);
593 // make sure it's already created
594 result
= (UVector
*) uhash_get(gOlsonToMeta
, tzidUChars
);
595 if (result
== NULL
) {
596 // add the one just created
597 int32_t tzidLen
= tzid
.length() + 1;
598 UChar
*key
= (UChar
*)uprv_malloc(tzidLen
* sizeof(UChar
));
600 // memory allocation error.. just return NULL
604 tzid
.extract(key
, tzidLen
, status
);
605 uhash_put(gOlsonToMeta
, key
, tmpResult
, &status
);
606 if (U_FAILURE(status
)) {
607 // delete the mapping
615 // another thread already put the one
619 umtx_unlock(&gZoneMetaLock
);
625 ZoneMeta::createMetazoneMappings(const UnicodeString
&tzid
) {
626 UVector
*mzMappings
= NULL
;
627 UErrorCode status
= U_ZERO_ERROR
;
629 UnicodeString canonicalID
;
630 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
631 ures_getByKey(rb
, gMetazoneInfo
, rb
, &status
);
632 getCanonicalCLDRID(tzid
, canonicalID
, status
);
634 if (U_SUCCESS(status
)) {
635 char tzKey
[ZID_KEY_MAX
+ 1];
636 int32_t tzKeyLen
= canonicalID
.extract(0, canonicalID
.length(), tzKey
, sizeof(tzKey
), US_INV
);
639 // tzid keys are using ':' as separators
648 ures_getByKey(rb
, tzKey
, rb
, &status
);
650 if (U_SUCCESS(status
)) {
651 UResourceBundle
*mz
= NULL
;
652 while (ures_hasNext(rb
)) {
653 mz
= ures_getNextResource(rb
, mz
, &status
);
655 const UChar
*mz_name
= ures_getStringByIndex(mz
, 0, NULL
, &status
);
656 const UChar
*mz_from
= gDefaultFrom
;
657 const UChar
*mz_to
= gDefaultTo
;
659 if (ures_getSize(mz
) == 3) {
660 mz_from
= ures_getStringByIndex(mz
, 1, NULL
, &status
);
661 mz_to
= ures_getStringByIndex(mz
, 2, NULL
, &status
);
664 if(U_FAILURE(status
)){
665 status
= U_ZERO_ERROR
;
668 // We do not want to use SimpleDateformat to parse boundary dates,
669 // because this code could be triggered by the initialization code
670 // used by SimpleDateFormat.
671 UDate from
= parseDate(mz_from
, status
);
672 UDate to
= parseDate(mz_to
, status
);
673 if (U_FAILURE(status
)) {
674 status
= U_ZERO_ERROR
;
678 OlsonToMetaMappingEntry
*entry
= (OlsonToMetaMappingEntry
*)uprv_malloc(sizeof(OlsonToMetaMappingEntry
));
680 status
= U_MEMORY_ALLOCATION_ERROR
;
683 entry
->mzid
= mz_name
;
687 if (mzMappings
== NULL
) {
688 mzMappings
= new UVector(deleteOlsonToMetaMappingEntry
, NULL
, status
);
689 if (U_FAILURE(status
)) {
691 deleteOlsonToMetaMappingEntry(entry
);
697 mzMappings
->addElement(entry
, status
);
698 if (U_FAILURE(status
)) {
703 if (U_FAILURE(status
)) {
704 if (mzMappings
!= NULL
) {
715 UnicodeString
& U_EXPORT2
716 ZoneMeta::getZoneIdByMetazone(const UnicodeString
&mzid
, const UnicodeString
®ion
, UnicodeString
&result
) {
717 UErrorCode status
= U_ZERO_ERROR
;
718 const UChar
*tzid
= NULL
;
720 char keyBuf
[ZID_KEY_MAX
+ 1];
723 if (mzid
.length() > ZID_KEY_MAX
) {
728 keyLen
= mzid
.extract(0, mzid
.length(), keyBuf
, ZID_KEY_MAX
+ 1, US_INV
);
731 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
732 ures_getByKey(rb
, gMapTimezonesTag
, rb
, &status
);
733 ures_getByKey(rb
, keyBuf
, rb
, &status
);
735 if (U_SUCCESS(status
)) {
736 // check region mapping
737 if (region
.length() == 2 || region
.length() == 3) {
738 keyLen
= region
.extract(0, region
.length(), keyBuf
, ZID_KEY_MAX
+ 1, US_INV
);
740 tzid
= ures_getStringByKey(rb
, keyBuf
, &tzidLen
, &status
);
741 if (status
== U_MISSING_RESOURCE_ERROR
) {
742 status
= U_ZERO_ERROR
;
745 if (U_SUCCESS(status
) && tzid
== NULL
) {
747 tzid
= ures_getStringByKey(rb
, gWorldTag
, &tzidLen
, &status
);
755 result
.setTo(tzid
, tzidLen
);
762 ZoneMeta::initAvailableMetaZoneIDs () {
764 UMTX_CHECK(&gZoneMetaLock
, gMetaZoneIDsInitialized
, initialized
);
766 umtx_lock(&gZoneMetaLock
);
768 if (!gMetaZoneIDsInitialized
) {
769 UErrorCode status
= U_ZERO_ERROR
;
770 UHashtable
*metaZoneIDTable
= uhash_open(uhash_hashUnicodeString
, uhash_compareUnicodeString
, NULL
, &status
);
771 uhash_setKeyDeleter(metaZoneIDTable
, uprv_deleteUObject
);
772 // No valueDeleter, because the vector maintain the value objects
773 UVector
*metaZoneIDs
= NULL
;
774 if (U_SUCCESS(status
)) {
775 metaZoneIDs
= new UVector(NULL
, uhash_compareUChars
, status
);
776 if (metaZoneIDs
== NULL
) {
777 status
= U_MEMORY_ALLOCATION_ERROR
;
780 uhash_close(metaZoneIDTable
);
782 if (U_SUCCESS(status
)) {
783 U_ASSERT(metaZoneIDs
!= NULL
);
784 metaZoneIDs
->setDeleter(uprv_free
);
786 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
787 UResourceBundle
*bundle
= ures_getByKey(rb
, gMapTimezonesTag
, NULL
, &status
);
789 ures_initStackObject(&res
);
790 while (U_SUCCESS(status
) && ures_hasNext(bundle
)) {
791 ures_getNextResource(bundle
, &res
, &status
);
792 if (U_FAILURE(status
)) {
795 const char *mzID
= ures_getKey(&res
);
796 int32_t len
= uprv_strlen(mzID
);
797 UChar
*uMzID
= (UChar
*)uprv_malloc(sizeof(UChar
) * (len
+ 1));
799 status
= U_MEMORY_ALLOCATION_ERROR
;
802 u_charsToUChars(mzID
, uMzID
, len
);
804 UnicodeString
*usMzID
= new UnicodeString(uMzID
);
805 if (uhash_get(metaZoneIDTable
, usMzID
) == NULL
) {
806 metaZoneIDs
->addElement((void *)uMzID
, status
);
807 uhash_put(metaZoneIDTable
, (void *)usMzID
, (void *)uMzID
, &status
);
813 if (U_SUCCESS(status
)) {
814 gMetaZoneIDs
= metaZoneIDs
;
815 gMetaZoneIDTable
= metaZoneIDTable
;
816 gMetaZoneIDsInitialized
= TRUE
;
818 uhash_close(metaZoneIDTable
);
827 umtx_unlock(&gZoneMetaLock
);
832 ZoneMeta::getAvailableMetazoneIDs() {
833 initAvailableMetaZoneIDs();
838 ZoneMeta::findMetaZoneID(const UnicodeString
& mzid
) {
839 initAvailableMetaZoneIDs();
840 return (const UChar
*)uhash_get(gMetaZoneIDTable
, &mzid
);
844 ZoneMeta::findTimeZoneID(const UnicodeString
& tzid
) {
845 return TimeZone::findID(tzid
);
850 ZoneMeta::createCustomTimeZone(int32_t offset
) {
851 UBool negative
= FALSE
;
852 int32_t tmp
= offset
;
857 int32_t hour
, min
, sec
;
866 formatCustomID(hour
, min
, sec
, negative
, zid
);
867 return new SimpleTimeZone(offset
, zid
);
871 ZoneMeta::formatCustomID(uint8_t hour
, uint8_t min
, uint8_t sec
, UBool negative
, UnicodeString
& id
) {
872 // Create normalized time zone ID - GMT[+|-]HH:mm[:ss]
873 id
.setTo(gCustomTzPrefix
, -1);
874 if (hour
!= 0 || min
!= 0) {
876 id
.append(0x2D); // '-'
878 id
.append(0x2B); // '+'
880 // Always use US-ASCII digits
881 id
.append(0x30 + (hour%100
)/10);
882 id
.append(0x30 + (hour%10
));
883 id
.append(0x3A); // ':'
884 id
.append(0x30 + (min%100
)/10);
885 id
.append(0x30 + (min%10
));
887 id
.append(0x3A); // ':'
888 id
.append(0x30 + (sec%100
)/10);
889 id
.append(0x30 + (sec%10
));
898 #endif /* #if !UCONFIG_NO_FORMATTING */