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 #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
32 static UMutex gZoneMetaLock
= U_MUTEX_INITIALIZER
;
34 // CLDR Canonical ID mapping table
35 static UHashtable
*gCanonicalIDCache
= NULL
;
36 static icu::UInitOnce gCanonicalIDCacheInitOnce
= U_INITONCE_INITIALIZER
;
38 // Metazone mapping table
39 static UHashtable
*gOlsonToMeta
= NULL
;
40 static icu::UInitOnce gOlsonToMetaInitOnce
= U_INITONCE_INITIALIZER
;
42 // Available metazone IDs vector and table
43 static icu::UVector
*gMetaZoneIDs
= NULL
;
44 static UHashtable
*gMetaZoneIDTable
= NULL
;
45 static icu::UInitOnce gMetaZoneIDsInitOnce
= U_INITONCE_INITIALIZER
;
47 // Country info vectors
48 static icu::UVector
*gSingleZoneCountries
= NULL
;
49 static icu::UVector
*gMultiZonesCountries
= NULL
;
50 static icu::UInitOnce gCountryInfoVectorsInitOnce
= U_INITONCE_INITIALIZER
;
55 * Cleanup callback func
57 static UBool U_CALLCONV
zoneMeta_cleanup(void)
59 if (gCanonicalIDCache
!= NULL
) {
60 uhash_close(gCanonicalIDCache
);
61 gCanonicalIDCache
= NULL
;
63 gCanonicalIDCacheInitOnce
.reset();
65 if (gOlsonToMeta
!= NULL
) {
66 uhash_close(gOlsonToMeta
);
69 gOlsonToMetaInitOnce
.reset();
71 if (gMetaZoneIDTable
!= NULL
) {
72 uhash_close(gMetaZoneIDTable
);
73 gMetaZoneIDTable
= NULL
;
75 // delete after closing gMetaZoneIDTable, because it holds
76 // value objects held by the hashtable
79 gMetaZoneIDsInitOnce
.reset();
81 delete gSingleZoneCountries
;
82 gSingleZoneCountries
= NULL
;
83 delete gMultiZonesCountries
;
84 gMultiZonesCountries
= NULL
;
85 gCountryInfoVectorsInitOnce
.reset();
91 * Deleter for UChar* string
93 static void U_CALLCONV
94 deleteUCharString(void *obj
) {
95 UChar
*entry
= (UChar
*)obj
;
100 * Deleter for UVector
102 static void U_CALLCONV
103 deleteUVector(void *obj
) {
104 delete (icu::UVector
*) obj
;
108 * Deleter for OlsonToMetaMappingEntry
110 static void U_CALLCONV
111 deleteOlsonToMetaMappingEntry(void *obj
) {
112 icu::OlsonToMetaMappingEntry
*entry
= (icu::OlsonToMetaMappingEntry
*)obj
;
120 #define ZID_KEY_MAX 128
122 static const char gMetaZones
[] = "metaZones";
123 static const char gMetazoneInfo
[] = "metazoneInfo";
124 static const char gMapTimezonesTag
[] = "mapTimezones";
126 static const char gKeyTypeData
[] = "keyTypeData";
127 static const char gTypeAliasTag
[] = "typeAlias";
128 static const char gTypeMapTag
[] = "typeMap";
129 static const char gTimezoneTag
[] = "timezone";
131 static const char gPrimaryZonesTag
[] = "primaryZones";
133 static const char gWorldTag
[] = "001";
135 static const UChar gWorld
[] = {0x30, 0x30, 0x31, 0x00}; // "001"
137 static const UChar gDefaultFrom
[] = {0x31, 0x39, 0x37, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31,
138 0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x00}; // "1970-01-01 00:00"
139 static const UChar gDefaultTo
[] = {0x39, 0x39, 0x39, 0x39, 0x2D, 0x31, 0x32, 0x2D, 0x33, 0x31,
140 0x20, 0x32, 0x33, 0x3A, 0x35, 0x39, 0x00}; // "9999-12-31 23:59"
142 static const UChar gCustomTzPrefix
[] = {0x47, 0x4D, 0x54, 0}; // "GMT"
144 #define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1)
147 * Convert a date string used by metazone mappings to UDate.
148 * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
151 parseDate (const UChar
*text
, UErrorCode
&status
) {
152 if (U_FAILURE(status
)) {
155 int32_t len
= u_strlen(text
);
156 if (len
!= 16 && len
!= 10) {
157 // It must be yyyy-MM-dd HH:mm (length 16) or yyyy-MM-dd (length 10)
158 status
= U_INVALID_FORMAT_ERROR
;
162 int32_t year
= 0, month
= 0, day
= 0, hour
= 0, min
= 0, n
;
166 for (idx
= 0; idx
<= 3 && U_SUCCESS(status
); idx
++) {
167 n
= ASCII_DIGIT((int32_t)text
[idx
]);
171 status
= U_INVALID_FORMAT_ERROR
;
175 for (idx
= 5; idx
<= 6 && U_SUCCESS(status
); idx
++) {
176 n
= ASCII_DIGIT((int32_t)text
[idx
]);
178 month
= 10*month
+ n
;
180 status
= U_INVALID_FORMAT_ERROR
;
184 for (idx
= 8; idx
<= 9 && U_SUCCESS(status
); idx
++) {
185 n
= ASCII_DIGIT((int32_t)text
[idx
]);
189 status
= U_INVALID_FORMAT_ERROR
;
194 for (idx
= 11; idx
<= 12 && U_SUCCESS(status
); idx
++) {
195 n
= ASCII_DIGIT((int32_t)text
[idx
]);
199 status
= U_INVALID_FORMAT_ERROR
;
203 for (idx
= 14; idx
<= 15 && U_SUCCESS(status
); idx
++) {
204 n
= ASCII_DIGIT((int32_t)text
[idx
]);
208 status
= U_INVALID_FORMAT_ERROR
;
213 if (U_SUCCESS(status
)) {
214 UDate date
= Grego::fieldsToDay(year
, month
- 1, day
) * U_MILLIS_PER_DAY
215 + hour
* U_MILLIS_PER_HOUR
+ min
* U_MILLIS_PER_MINUTE
;
221 static void U_CALLCONV
initCanonicalIDCache(UErrorCode
&status
) {
222 gCanonicalIDCache
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
223 if (gCanonicalIDCache
== NULL
) {
224 status
= U_MEMORY_ALLOCATION_ERROR
;
226 if (U_FAILURE(status
)) {
227 gCanonicalIDCache
= NULL
;
229 // No key/value deleters - keys/values are from a resource bundle
230 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
234 const UChar
* U_EXPORT2
235 ZoneMeta::getCanonicalCLDRID(const UnicodeString
&tzid
, UErrorCode
& status
) {
236 if (U_FAILURE(status
)) {
240 if (tzid
.isBogus() || tzid
.length() > ZID_KEY_MAX
) {
241 status
= U_ILLEGAL_ARGUMENT_ERROR
;
245 // Checking the cached results
246 umtx_initOnce(gCanonicalIDCacheInitOnce
, &initCanonicalIDCache
, status
);
247 if (U_FAILURE(status
)) {
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 tzid
.extract(0, 0x7fffffff, id
, LENGTHOF(id
), US_INV
);
274 // replace '/' with ':'
282 UResourceBundle
*top
= ures_openDirect(NULL
, gKeyTypeData
, &tmpStatus
);
283 UResourceBundle
*rb
= ures_getByKey(top
, gTypeMapTag
, NULL
, &tmpStatus
);
284 ures_getByKey(rb
, gTimezoneTag
, rb
, &tmpStatus
);
285 ures_getByKey(rb
, id
, rb
, &tmpStatus
);
286 if (U_SUCCESS(tmpStatus
)) {
287 // type entry (canonical) found
288 // the input is the canonical ID. resolve to const UChar*
289 canonicalID
= TimeZone::findID(tzid
);
290 isInputCanonical
= TRUE
;
293 if (canonicalID
== NULL
) {
294 // If a map element not found, then look for an alias
295 tmpStatus
= U_ZERO_ERROR
;
296 ures_getByKey(top
, gTypeAliasTag
, rb
, &tmpStatus
);
297 ures_getByKey(rb
, gTimezoneTag
, rb
, &tmpStatus
);
298 const UChar
*canonical
= ures_getStringByKey(rb
,id
,NULL
,&tmpStatus
);
299 if (U_SUCCESS(tmpStatus
)) {
300 // canonical map found
301 canonicalID
= canonical
;
304 if (canonicalID
== NULL
) {
305 // Dereference the input ID using the tz data
306 const UChar
*derefer
= TimeZone::dereferOlsonLink(tzid
);
307 if (derefer
== NULL
) {
308 status
= U_ILLEGAL_ARGUMENT_ERROR
;
310 int32_t len
= u_strlen(derefer
);
311 u_UCharsToChars(derefer
,id
,len
);
312 id
[len
] = (char) 0; // Make sure it is null terminated.
314 // replace '/' with ':'
322 // If a dereference turned something up then look for an alias.
323 // rb still points to the alias table, so we don't have to go looking
325 tmpStatus
= U_ZERO_ERROR
;
326 canonical
= ures_getStringByKey(rb
,id
,NULL
,&tmpStatus
);
327 if (U_SUCCESS(tmpStatus
)) {
328 // canonical map for the dereferenced ID found
329 canonicalID
= canonical
;
331 canonicalID
= derefer
;
332 isInputCanonical
= TRUE
;
340 if (U_SUCCESS(status
)) {
341 U_ASSERT(canonicalID
!= NULL
); // canocanilD must be non-NULL here
343 // Put the resolved canonical ID to the cache
344 umtx_lock(&gZoneMetaLock
);
346 const UChar
* idInCache
= (const UChar
*)uhash_get(gCanonicalIDCache
, utzid
);
347 if (idInCache
== NULL
) {
348 const UChar
* key
= ZoneMeta::findTimeZoneID(tzid
);
349 U_ASSERT(key
!= NULL
);
351 idInCache
= (const UChar
*)uhash_put(gCanonicalIDCache
, (void *)key
, (void *)canonicalID
, &status
);
352 U_ASSERT(idInCache
== NULL
);
355 if (U_SUCCESS(status
) && isInputCanonical
) {
356 // Also put canonical ID itself into the cache if not exist
357 const UChar
*canonicalInCache
= (const UChar
*)uhash_get(gCanonicalIDCache
, canonicalID
);
358 if (canonicalInCache
== NULL
) {
359 canonicalInCache
= (const UChar
*)uhash_put(gCanonicalIDCache
, (void *)canonicalID
, (void *)canonicalID
, &status
);
360 U_ASSERT(canonicalInCache
== NULL
);
364 umtx_unlock(&gZoneMetaLock
);
370 UnicodeString
& U_EXPORT2
371 ZoneMeta::getCanonicalCLDRID(const UnicodeString
&tzid
, UnicodeString
&systemID
, UErrorCode
& status
) {
372 const UChar
*canonicalID
= getCanonicalCLDRID(tzid
, status
);
373 if (U_FAILURE(status
) || canonicalID
== NULL
) {
374 systemID
.setToBogus();
377 systemID
.setTo(TRUE
, canonicalID
, -1);
381 const UChar
* U_EXPORT2
382 ZoneMeta::getCanonicalCLDRID(const TimeZone
& tz
) {
383 if (dynamic_cast<const OlsonTimeZone
*>(&tz
) != NULL
) {
384 // short cut for OlsonTimeZone
385 const OlsonTimeZone
*otz
= (const OlsonTimeZone
*)&tz
;
386 return otz
->getCanonicalID();
388 UErrorCode status
= U_ZERO_ERROR
;
390 return getCanonicalCLDRID(tz
.getID(tzID
), status
);
393 static void U_CALLCONV
countryInfoVectorsInit(UErrorCode
&status
) {
394 // Create empty vectors
395 // No deleters for these UVectors, it's a reference to a resource bundle string.
396 gSingleZoneCountries
= new UVector(NULL
, uhash_compareUChars
, status
);
397 if (gSingleZoneCountries
== NULL
) {
398 status
= U_MEMORY_ALLOCATION_ERROR
;
400 gMultiZonesCountries
= new UVector(NULL
, uhash_compareUChars
, status
);
401 if (gMultiZonesCountries
== NULL
) {
402 status
= U_MEMORY_ALLOCATION_ERROR
;
405 if (U_FAILURE(status
)) {
406 delete gSingleZoneCountries
;
407 delete gMultiZonesCountries
;
408 gSingleZoneCountries
= NULL
;
409 gMultiZonesCountries
= NULL
;
411 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
415 UnicodeString
& U_EXPORT2
416 ZoneMeta::getCanonicalCountry(const UnicodeString
&tzid
, UnicodeString
&country
, UBool
*isPrimary
/* = NULL */) {
417 if (isPrimary
!= NULL
) {
421 const UChar
*region
= TimeZone::getRegion(tzid
);
422 if (region
!= NULL
&& u_strcmp(gWorld
, region
) != 0) {
423 country
.setTo(region
, -1);
425 country
.setToBogus();
429 if (isPrimary
!= NULL
) {
430 char regionBuf
[] = {0, 0, 0};
432 // Checking the cached results
433 UErrorCode status
= U_ZERO_ERROR
;
434 umtx_initOnce(gCountryInfoVectorsInitOnce
, &countryInfoVectorsInit
, status
);
435 if (U_FAILURE(status
)) {
439 // Check if it was already cached
440 UBool cached
= FALSE
;
441 UBool singleZone
= FALSE
;
442 umtx_lock(&gZoneMetaLock
);
444 singleZone
= cached
= gSingleZoneCountries
->contains((void*)region
);
446 cached
= gMultiZonesCountries
->contains((void*)region
);
449 umtx_unlock(&gZoneMetaLock
);
452 // We need to go through all zones associated with the region.
453 // This is relatively heavy operation.
455 U_ASSERT(u_strlen(region
) == 2);
457 u_UCharsToChars(region
, regionBuf
, 2);
459 StringEnumeration
*ids
= TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION
, regionBuf
, NULL
, status
);
460 int32_t idsLen
= ids
->count(status
);
461 if (U_SUCCESS(status
) && idsLen
== 1) {
462 // only the single zone is available for the region
468 umtx_lock(&gZoneMetaLock
);
470 UErrorCode ec
= U_ZERO_ERROR
;
472 if (!gSingleZoneCountries
->contains((void*)region
)) {
473 gSingleZoneCountries
->addElement((void*)region
, ec
);
476 if (!gMultiZonesCountries
->contains((void*)region
)) {
477 gMultiZonesCountries
->addElement((void*)region
, ec
);
481 umtx_unlock(&gZoneMetaLock
);
487 // Note: We may cache the primary zone map in future.
489 // Even a country has multiple zones, one of them might be
490 // dominant and treated as a primary zone
492 if (regionBuf
[0] == 0) {
493 u_UCharsToChars(region
, regionBuf
, 2);
496 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
497 ures_getByKey(rb
, gPrimaryZonesTag
, rb
, &status
);
498 const UChar
*primaryZone
= ures_getStringByKey(rb
, regionBuf
, &idLen
, &status
);
499 if (U_SUCCESS(status
)) {
500 if (tzid
.compare(primaryZone
, idLen
) == 0) {
503 // The given ID might not be a canonical ID
504 UnicodeString canonicalID
;
505 TimeZone::getCanonicalID(tzid
, canonicalID
, status
);
506 if (U_SUCCESS(status
) && canonicalID
.compare(primaryZone
, idLen
) == 0) {
518 UnicodeString
& U_EXPORT2
519 ZoneMeta::getMetazoneID(const UnicodeString
&tzid
, UDate date
, UnicodeString
&result
) {
521 const UVector
*mappings
= getMetazoneMappings(tzid
);
522 if (mappings
!= NULL
) {
523 for (int32_t i
= 0; i
< mappings
->size(); i
++) {
524 OlsonToMetaMappingEntry
*mzm
= (OlsonToMetaMappingEntry
*)mappings
->elementAt(i
);
525 if (mzm
->from
<= date
&& mzm
->to
> date
) {
526 result
.setTo(mzm
->mzid
, -1);
538 static void U_CALLCONV
olsonToMetaInit(UErrorCode
&status
) {
539 U_ASSERT(gOlsonToMeta
== NULL
);
540 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
541 gOlsonToMeta
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
542 if (U_FAILURE(status
)) {
545 uhash_setKeyDeleter(gOlsonToMeta
, deleteUCharString
);
546 uhash_setValueDeleter(gOlsonToMeta
, deleteUVector
);
551 const UVector
* U_EXPORT2
552 ZoneMeta::getMetazoneMappings(const UnicodeString
&tzid
) {
553 UErrorCode status
= U_ZERO_ERROR
;
554 UChar tzidUChars
[ZID_KEY_MAX
+ 1];
555 tzid
.extract(tzidUChars
, ZID_KEY_MAX
+ 1, status
);
556 if (U_FAILURE(status
) || status
== U_STRING_NOT_TERMINATED_WARNING
) {
560 umtx_initOnce(gOlsonToMetaInitOnce
, &olsonToMetaInit
, status
);
561 if (U_FAILURE(status
)) {
565 // get the mapping from cache
566 const UVector
*result
= NULL
;
568 umtx_lock(&gZoneMetaLock
);
570 result
= (UVector
*) uhash_get(gOlsonToMeta
, tzidUChars
);
572 umtx_unlock(&gZoneMetaLock
);
574 if (result
!= NULL
) {
578 // miss the cache - create new one
579 UVector
*tmpResult
= createMetazoneMappings(tzid
);
580 if (tmpResult
== NULL
) {
585 // put the new one into the cache
586 umtx_lock(&gZoneMetaLock
);
588 // make sure it's already created
589 result
= (UVector
*) uhash_get(gOlsonToMeta
, tzidUChars
);
590 if (result
== NULL
) {
591 // add the one just created
592 int32_t tzidLen
= tzid
.length() + 1;
593 UChar
*key
= (UChar
*)uprv_malloc(tzidLen
* sizeof(UChar
));
595 // memory allocation error.. just return NULL
599 tzid
.extract(key
, tzidLen
, status
);
600 uhash_put(gOlsonToMeta
, key
, tmpResult
, &status
);
601 if (U_FAILURE(status
)) {
602 // delete the mapping
610 // another thread already put the one
614 umtx_unlock(&gZoneMetaLock
);
620 ZoneMeta::createMetazoneMappings(const UnicodeString
&tzid
) {
621 UVector
*mzMappings
= NULL
;
622 UErrorCode status
= U_ZERO_ERROR
;
624 UnicodeString canonicalID
;
625 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
626 ures_getByKey(rb
, gMetazoneInfo
, rb
, &status
);
627 getCanonicalCLDRID(tzid
, canonicalID
, status
);
629 if (U_SUCCESS(status
)) {
630 char tzKey
[ZID_KEY_MAX
+ 1];
631 int32_t tzKeyLen
= canonicalID
.extract(0, canonicalID
.length(), tzKey
, sizeof(tzKey
), US_INV
);
634 // tzid keys are using ':' as separators
643 ures_getByKey(rb
, tzKey
, rb
, &status
);
645 if (U_SUCCESS(status
)) {
646 UResourceBundle
*mz
= NULL
;
647 while (ures_hasNext(rb
)) {
648 mz
= ures_getNextResource(rb
, mz
, &status
);
650 const UChar
*mz_name
= ures_getStringByIndex(mz
, 0, NULL
, &status
);
651 const UChar
*mz_from
= gDefaultFrom
;
652 const UChar
*mz_to
= gDefaultTo
;
654 if (ures_getSize(mz
) == 3) {
655 mz_from
= ures_getStringByIndex(mz
, 1, NULL
, &status
);
656 mz_to
= ures_getStringByIndex(mz
, 2, NULL
, &status
);
659 if(U_FAILURE(status
)){
660 status
= U_ZERO_ERROR
;
663 // We do not want to use SimpleDateformat to parse boundary dates,
664 // because this code could be triggered by the initialization code
665 // used by SimpleDateFormat.
666 UDate from
= parseDate(mz_from
, status
);
667 UDate to
= parseDate(mz_to
, status
);
668 if (U_FAILURE(status
)) {
669 status
= U_ZERO_ERROR
;
673 OlsonToMetaMappingEntry
*entry
= (OlsonToMetaMappingEntry
*)uprv_malloc(sizeof(OlsonToMetaMappingEntry
));
675 status
= U_MEMORY_ALLOCATION_ERROR
;
678 entry
->mzid
= mz_name
;
682 if (mzMappings
== NULL
) {
683 mzMappings
= new UVector(deleteOlsonToMetaMappingEntry
, NULL
, status
);
684 if (U_FAILURE(status
)) {
686 deleteOlsonToMetaMappingEntry(entry
);
692 mzMappings
->addElement(entry
, status
);
693 if (U_FAILURE(status
)) {
698 if (U_FAILURE(status
)) {
699 if (mzMappings
!= NULL
) {
710 UnicodeString
& U_EXPORT2
711 ZoneMeta::getZoneIdByMetazone(const UnicodeString
&mzid
, const UnicodeString
®ion
, UnicodeString
&result
) {
712 UErrorCode status
= U_ZERO_ERROR
;
713 const UChar
*tzid
= NULL
;
715 char keyBuf
[ZID_KEY_MAX
+ 1];
718 if (mzid
.isBogus() || mzid
.length() > ZID_KEY_MAX
) {
723 keyLen
= mzid
.extract(0, mzid
.length(), keyBuf
, ZID_KEY_MAX
+ 1, US_INV
);
726 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
727 ures_getByKey(rb
, gMapTimezonesTag
, rb
, &status
);
728 ures_getByKey(rb
, keyBuf
, rb
, &status
);
730 if (U_SUCCESS(status
)) {
731 // check region mapping
732 if (region
.length() == 2 || region
.length() == 3) {
733 keyLen
= region
.extract(0, region
.length(), keyBuf
, ZID_KEY_MAX
+ 1, US_INV
);
735 tzid
= ures_getStringByKey(rb
, keyBuf
, &tzidLen
, &status
);
736 if (status
== U_MISSING_RESOURCE_ERROR
) {
737 status
= U_ZERO_ERROR
;
740 if (U_SUCCESS(status
) && tzid
== NULL
) {
742 tzid
= ures_getStringByKey(rb
, gWorldTag
, &tzidLen
, &status
);
750 result
.setTo(tzid
, tzidLen
);
756 static void U_CALLCONV
initAvailableMetaZoneIDs () {
757 U_ASSERT(gMetaZoneIDs
== NULL
);
758 U_ASSERT(gMetaZoneIDTable
== NULL
);
759 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
761 UErrorCode status
= U_ZERO_ERROR
;
762 gMetaZoneIDTable
= uhash_open(uhash_hashUnicodeString
, uhash_compareUnicodeString
, NULL
, &status
);
763 if (U_FAILURE(status
) || gMetaZoneIDTable
== NULL
) {
764 gMetaZoneIDTable
= NULL
;
767 uhash_setKeyDeleter(gMetaZoneIDTable
, uprv_deleteUObject
);
768 // No valueDeleter, because the vector maintain the value objects
769 gMetaZoneIDs
= new UVector(NULL
, uhash_compareUChars
, status
);
770 if (U_FAILURE(status
) || gMetaZoneIDs
== NULL
) {
772 uhash_close(gMetaZoneIDTable
);
773 gMetaZoneIDTable
= NULL
;
776 gMetaZoneIDs
->setDeleter(uprv_free
);
778 UResourceBundle
*rb
= ures_openDirect(NULL
, gMetaZones
, &status
);
779 UResourceBundle
*bundle
= ures_getByKey(rb
, gMapTimezonesTag
, NULL
, &status
);
781 ures_initStackObject(&res
);
782 while (U_SUCCESS(status
) && ures_hasNext(bundle
)) {
783 ures_getNextResource(bundle
, &res
, &status
);
784 if (U_FAILURE(status
)) {
787 const char *mzID
= ures_getKey(&res
);
788 int32_t len
= uprv_strlen(mzID
);
789 UChar
*uMzID
= (UChar
*)uprv_malloc(sizeof(UChar
) * (len
+ 1));
791 status
= U_MEMORY_ALLOCATION_ERROR
;
794 u_charsToUChars(mzID
, uMzID
, len
);
796 UnicodeString
*usMzID
= new UnicodeString(uMzID
);
797 if (uhash_get(gMetaZoneIDTable
, usMzID
) == NULL
) {
798 gMetaZoneIDs
->addElement((void *)uMzID
, status
);
799 uhash_put(gMetaZoneIDTable
, (void *)usMzID
, (void *)uMzID
, &status
);
809 if (U_FAILURE(status
)) {
810 uhash_close(gMetaZoneIDTable
);
812 gMetaZoneIDTable
= NULL
;
818 ZoneMeta::getAvailableMetazoneIDs() {
819 umtx_initOnce(gMetaZoneIDsInitOnce
, &initAvailableMetaZoneIDs
);
824 ZoneMeta::findMetaZoneID(const UnicodeString
& mzid
) {
825 umtx_initOnce(gMetaZoneIDsInitOnce
, &initAvailableMetaZoneIDs
);
826 if (gMetaZoneIDTable
== NULL
) {
829 return (const UChar
*)uhash_get(gMetaZoneIDTable
, &mzid
);
833 ZoneMeta::findTimeZoneID(const UnicodeString
& tzid
) {
834 return TimeZone::findID(tzid
);
839 ZoneMeta::createCustomTimeZone(int32_t offset
) {
840 UBool negative
= FALSE
;
841 int32_t tmp
= offset
;
846 int32_t hour
, min
, sec
;
855 formatCustomID(hour
, min
, sec
, negative
, zid
);
856 return new SimpleTimeZone(offset
, zid
);
860 ZoneMeta::formatCustomID(uint8_t hour
, uint8_t min
, uint8_t sec
, UBool negative
, UnicodeString
& id
) {
861 // Create normalized time zone ID - GMT[+|-]HH:mm[:ss]
862 id
.setTo(gCustomTzPrefix
, -1);
863 if (hour
!= 0 || min
!= 0) {
865 id
.append((UChar
)0x2D); // '-'
867 id
.append((UChar
)0x2B); // '+'
869 // Always use US-ASCII digits
870 id
.append((UChar
)(0x30 + (hour%100
)/10));
871 id
.append((UChar
)(0x30 + (hour%10
)));
872 id
.append((UChar
)0x3A); // ':'
873 id
.append((UChar
)(0x30 + (min%100
)/10));
874 id
.append((UChar
)(0x30 + (min%10
)));
876 id
.append((UChar
)0x3A); // ':'
877 id
.append((UChar
)(0x30 + (sec%100
)/10));
878 id
.append((UChar
)(0x30 + (sec%10
)));
885 ZoneMeta::getShortID(const TimeZone
& tz
) {
886 const UChar
* canonicalID
= NULL
;
887 if (dynamic_cast<const OlsonTimeZone
*>(&tz
) != NULL
) {
888 // short cut for OlsonTimeZone
889 const OlsonTimeZone
*otz
= (const OlsonTimeZone
*)&tz
;
890 canonicalID
= otz
->getCanonicalID();
892 if (canonicalID
== NULL
) {
895 return getShortIDFromCanonical(canonicalID
);
899 ZoneMeta::getShortID(const UnicodeString
& id
) {
900 UErrorCode status
= U_ZERO_ERROR
;
901 const UChar
* canonicalID
= ZoneMeta::getCanonicalCLDRID(id
, status
);
902 if (U_FAILURE(status
) || canonicalID
== NULL
) {
905 return ZoneMeta::getShortIDFromCanonical(canonicalID
);
909 ZoneMeta::getShortIDFromCanonical(const UChar
* canonicalID
) {
910 const UChar
* shortID
= NULL
;
911 int32_t len
= u_strlen(canonicalID
);
912 char tzidKey
[ZID_KEY_MAX
+ 1];
914 u_UCharsToChars(canonicalID
, tzidKey
, len
);
915 tzidKey
[len
] = (char) 0; // Make sure it is null terminated.
917 // replace '/' with ':'
925 UErrorCode status
= U_ZERO_ERROR
;
926 UResourceBundle
*rb
= ures_openDirect(NULL
, gKeyTypeData
, &status
);
927 ures_getByKey(rb
, gTypeMapTag
, rb
, &status
);
928 ures_getByKey(rb
, gTimezoneTag
, rb
, &status
);
929 shortID
= ures_getStringByKey(rb
, tzidKey
, NULL
, &status
);
937 #endif /* #if !UCONFIG_NO_FORMATTING */