2 *******************************************************************************
3 * Copyright (C) 2007-2009, 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"
25 // Metazone mapping tables
26 static UMTX gZoneMetaLock
= NULL
;
27 static UHashtable
*gCanonicalMap
= NULL
;
28 static UHashtable
*gOlsonToMeta
= NULL
;
29 static UHashtable
*gMetaToOlson
= NULL
;
30 static UBool gCanonicalMapInitialized
= FALSE
;
31 static UBool gOlsonToMetaInitialized
= FALSE
;
32 static UBool gMetaToOlsonInitialized
= FALSE
;
33 static UChar
**gUStringTable
= NULL
;
34 static int32_t gUStringCount
= 0;
35 static int32_t gUStringAlloc
= 0;
37 // Currently (ICU 4.1.3+), gUStringTable only contains strings allocated in the section of
38 // createCanonicalMap that iterates over the enumerator created with TimeZone::createEnumeration.
39 // And currently, that allocates a total of 22 strings. So USTRING_ALLOC_START is defined to
40 // be adequate for that set, and USTRING_ALLOC_INCR is a reasonable expansion increment. In
41 // future versions of ICU, these numbers may need adjusting to avoid excessive reallocs, or to
42 // avoid allocating unused memory (but in any case the effects are small).
43 #define USTRING_ALLOC_START 24
44 #define USTRING_ALLOC_INCR 12
48 // We have switched CanonicalMap to use const UChar* strings for the key and for the id field of
49 // CanonicalMapEntry; that is because for the most part these now point into UChar strings in the
50 // shared data file, in order to reduce process-specific dynamically-allocated memory. Consequently,
51 // there is no longer a deleter for the key field, and the deleter for CanonicalMapEntry
52 // no longer frees the id field. However, for the few strings that are obtained from the
53 // TimeZone::createEnumeration() enumerator or from TimeZone::dereferOlsonLink instead of the
54 // data file, we do need to allocate copies. In order to ensure that these strings are freed by
55 // zoneMeta_cleanup(), we need to create a little memory manager for them; this is in the form of
56 // a table that tracks the strings allocated for this purpose. The following three functions
57 // (along with the gUStringXxxxx statics) are used to allocate and free such strings.
59 // The following allocs space for a UChar* string of the specified length, puts a pointer to the string
60 // in gUStringTable, and returns either a pointer to the allocated string space, or NULL for failure.
61 static UChar
* allocUStringInTable(int32_t uStringLen
) {
62 UChar
* uStringSpace
= NULL
;
63 // initialize the table if necessary
64 umtx_lock(&gZoneMetaLock
);
65 if (gUStringTable
== NULL
) {
66 gUStringTable
= (UChar
**)uprv_malloc(USTRING_ALLOC_START
*sizeof(UChar
*));
67 if (gUStringTable
!= NULL
) {
68 gUStringAlloc
= USTRING_ALLOC_START
;
71 if (gUStringTable
!= NULL
) {
72 // expand the table if necessary
73 if (gUStringCount
== gUStringAlloc
) {
74 UChar
** newTable
= (UChar
**)uprv_realloc(gUStringTable
, (gUStringAlloc
+USTRING_ALLOC_INCR
)*sizeof(UChar
*));
75 if (newTable
!= NULL
) {
76 gUStringTable
= newTable
;
77 gUStringAlloc
+= USTRING_ALLOC_INCR
;
80 // add the string if possible
81 if (gUStringCount
< gUStringAlloc
) {
82 uStringSpace
= (UChar
*)uprv_malloc(uStringLen
*sizeof(UChar
));
83 if (uStringSpace
!= NULL
) {
84 gUStringTable
[gUStringCount
++] = uStringSpace
;
88 umtx_unlock(&gZoneMetaLock
);
92 static void removeLastUStringFromTable(void) {
93 umtx_lock(&gZoneMetaLock
);
94 if (gUStringCount
> 0) {
95 free(gUStringTable
[--gUStringCount
]);
97 umtx_unlock(&gZoneMetaLock
);
100 static void freeUStringTable(void) {
101 int32_t uStringCount
= gUStringCount
;
104 if (gUStringTable
!= NULL
) {
105 while (uStringCount
> 0) {
106 free(gUStringTable
[--uStringCount
]);
109 gUStringTable
= NULL
;
114 * Cleanup callback func
116 static UBool U_CALLCONV
zoneMeta_cleanup(void)
118 umtx_destroy(&gZoneMetaLock
);
120 if (gCanonicalMap
!= NULL
) {
121 uhash_close(gCanonicalMap
);
122 gCanonicalMap
= NULL
;
124 gCanonicalMapInitialized
= FALSE
;
126 if (gOlsonToMeta
!= NULL
) {
127 uhash_close(gOlsonToMeta
);
130 gOlsonToMetaInitialized
= FALSE
;
132 if (gMetaToOlson
!= NULL
) {
133 uhash_close(gMetaToOlson
);
136 gMetaToOlsonInitialized
= FALSE
;
144 * Deleter for UChar* string
146 static void U_CALLCONV
147 deleteUCharString(void *obj
) {
148 UChar
*entry
= (UChar
*)obj
;
153 * Deleter for UVector
155 static void U_CALLCONV
156 deleteUVector(void *obj
) {
157 delete (U_NAMESPACE_QUALIFIER UVector
*) obj
;
161 * Deleter for CanonicalMapEntry
163 static void U_CALLCONV
164 deleteCanonicalMapEntry(void *obj
) {
165 U_NAMESPACE_QUALIFIER CanonicalMapEntry
*entry
= (U_NAMESPACE_QUALIFIER CanonicalMapEntry
*)obj
;
170 * Deleter for OlsonToMetaMappingEntry
172 static void U_CALLCONV
173 deleteOlsonToMetaMappingEntry(void *obj
) {
174 U_NAMESPACE_QUALIFIER OlsonToMetaMappingEntry
*entry
= (U_NAMESPACE_QUALIFIER OlsonToMetaMappingEntry
*)obj
;
179 * Deleter for MetaToOlsonMappingEntry
181 static void U_CALLCONV
182 deleteMetaToOlsonMappingEntry(void *obj
) {
183 U_NAMESPACE_QUALIFIER MetaToOlsonMappingEntry
*entry
= (U_NAMESPACE_QUALIFIER MetaToOlsonMappingEntry
*)obj
;
184 uprv_free(entry
->territory
);
191 #define ZID_KEY_MAX 128
192 static const char gZoneStringsTag
[] = "zoneStrings";
193 static const char gUseMetazoneTag
[] = "um";
195 static const char gSupplementalData
[] = "supplementalData";
196 static const char gMapTimezonesTag
[] = "mapTimezones";
197 static const char gMetazonesTag
[] = "metazones";
198 static const char gZoneFormattingTag
[] = "zoneFormatting";
199 static const char gCanonicalTag
[] = "canonical";
200 static const char gTerritoryTag
[] = "territory";
201 static const char gAliasesTag
[] = "aliases";
202 static const char gMultizoneTag
[] = "multizone";
204 static const char gMetazoneInfo
[] = "metazoneInfo";
205 static const char gMetazoneMappings
[] = "metazoneMappings";
207 #define MZID_PREFIX_LEN 5
208 static const char gMetazoneIdPrefix
[] = "meta:";
210 static const UChar gWorld
[] = {0x30, 0x30, 0x31, 0x00}; // "001"
212 #define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1)
215 * Convert a date string used by metazone mappings to UDate.
216 * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
219 parseDate (const UChar
*text
, UErrorCode
&status
) {
220 if (U_FAILURE(status
)) {
223 int32_t len
= u_strlen(text
);
224 if (len
!= 16 && len
!= 10) {
225 // It must be yyyy-MM-dd HH:mm (length 16) or yyyy-MM-dd (length 10)
226 status
= U_INVALID_FORMAT_ERROR
;
230 int32_t year
= 0, month
= 0, day
= 0, hour
= 0, min
= 0, n
;
234 for (idx
= 0; idx
<= 3 && U_SUCCESS(status
); idx
++) {
235 n
= ASCII_DIGIT((int32_t)text
[idx
]);
239 status
= U_INVALID_FORMAT_ERROR
;
243 for (idx
= 5; idx
<= 6 && U_SUCCESS(status
); idx
++) {
244 n
= ASCII_DIGIT((int32_t)text
[idx
]);
246 month
= 10*month
+ n
;
248 status
= U_INVALID_FORMAT_ERROR
;
252 for (idx
= 8; idx
<= 9 && U_SUCCESS(status
); idx
++) {
253 n
= ASCII_DIGIT((int32_t)text
[idx
]);
257 status
= U_INVALID_FORMAT_ERROR
;
262 for (idx
= 11; idx
<= 12 && U_SUCCESS(status
); idx
++) {
263 n
= ASCII_DIGIT((int32_t)text
[idx
]);
267 status
= U_INVALID_FORMAT_ERROR
;
271 for (idx
= 14; idx
<= 15 && U_SUCCESS(status
); idx
++) {
272 n
= ASCII_DIGIT((int32_t)text
[idx
]);
276 status
= U_INVALID_FORMAT_ERROR
;
281 if (U_SUCCESS(status
)) {
282 UDate date
= Grego::fieldsToDay(year
, month
- 1, day
) * U_MILLIS_PER_DAY
283 + hour
* U_MILLIS_PER_HOUR
+ min
* U_MILLIS_PER_MINUTE
;
290 ZoneMeta::createCanonicalMap(void) {
291 UErrorCode status
= U_ZERO_ERROR
;
293 UHashtable
*canonicalMap
= NULL
;
294 UResourceBundle
*zoneFormatting
= NULL
;
295 UResourceBundle
*tzitem
= NULL
;
296 UResourceBundle
*aliases
= NULL
;
298 StringEnumeration
* tzenum
= NULL
;
301 canonicalMap
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
302 if (U_FAILURE(status
)) {
306 uhash_setValueDeleter(canonicalMap
, deleteCanonicalMapEntry
);
308 zoneFormatting
= ures_openDirect(NULL
, gSupplementalData
, &status
);
309 zoneFormatting
= ures_getByKey(zoneFormatting
, gZoneFormattingTag
, zoneFormatting
, &status
);
310 if (U_FAILURE(status
)) {
314 while (ures_hasNext(zoneFormatting
)) {
315 tzitem
= ures_getNextResource(zoneFormatting
, tzitem
, &status
);
316 if (U_FAILURE(status
)) {
317 status
= U_ZERO_ERROR
;
320 if (ures_getType(tzitem
) != URES_TABLE
) {
324 int32_t canonicalLen
;
325 const UChar
*canonical
= ures_getStringByKey(tzitem
, gCanonicalTag
, &canonicalLen
, &status
);
326 if (U_FAILURE(status
)) {
327 status
= U_ZERO_ERROR
;
331 int32_t territoryLen
;
332 const UChar
*territory
= ures_getStringByKey(tzitem
, gTerritoryTag
, &territoryLen
, &status
);
333 if (U_FAILURE(status
)) {
335 status
= U_ZERO_ERROR
;
338 // Create canonical map entry
339 CanonicalMapEntry
*entry
= (CanonicalMapEntry
*)uprv_malloc(sizeof(CanonicalMapEntry
));
341 status
= U_MEMORY_ALLOCATION_ERROR
;
344 entry
->id
= canonical
;
345 if (territory
== NULL
|| u_strcmp(territory
, gWorld
) == 0) {
346 entry
->country
= NULL
;
348 entry
->country
= territory
;
351 // Put this entry in the hashtable. Since this hashtable has no key deleter,
352 // key is treated as const, but must be passed as non-const.
353 uhash_put(canonicalMap
, (UChar
*)canonical
, entry
, &status
);
354 if (U_FAILURE(status
)) {
359 aliases
= ures_getByKey(tzitem
, gAliasesTag
, aliases
, &status
);
360 if (U_FAILURE(status
)) {
362 status
= U_ZERO_ERROR
;
366 while (ures_hasNext(aliases
)) {
367 const UChar
* alias
= ures_getNextString(aliases
, NULL
, NULL
, &status
);
368 if (U_FAILURE(status
)) {
369 status
= U_ZERO_ERROR
;
372 // Create canonical map entry for this alias
373 entry
= (CanonicalMapEntry
*)uprv_malloc(sizeof(CanonicalMapEntry
));
375 status
= U_MEMORY_ALLOCATION_ERROR
;
378 entry
->id
= canonical
;
379 if (territory
== NULL
|| u_strcmp(territory
, gWorld
) == 0) {
380 entry
->country
= NULL
;
382 entry
->country
= territory
;
385 // Put this entry in the hashtable. Since this hashtable has no key deleter,
386 // key is treated as const, but must be passed as non-const.
387 uhash_put(canonicalMap
, (UChar
*)alias
, entry
, &status
);
388 if (U_FAILURE(status
)) {
394 // Some available Olson zones are not included in CLDR data (such as Asia/Riyadh87).
395 // Also, when we update Olson tzdata, new zones may be added.
396 // This code scans all available zones in zoneinfo.res, and if any of them are
397 // missing, add them to the map.
398 tzenum
= TimeZone::createEnumeration();
399 numZones
= tzenum
->count(status
);
400 if (U_SUCCESS(status
)) {
402 for (i
= 0; i
< numZones
; i
++) {
403 const UnicodeString
*zone
= tzenum
->snext(status
);
404 if (U_FAILURE(status
)) {
405 // We should not get here.
406 status
= U_ZERO_ERROR
;
409 UChar zoneUChars
[ZID_KEY_MAX
];
410 int32_t zoneUCharsLen
= zone
->extract(zoneUChars
, ZID_KEY_MAX
, status
) + 1; // Add one for NUL termination
411 if (U_FAILURE(status
) || status
==U_STRING_NOT_TERMINATED_WARNING
) {
412 status
= U_ZERO_ERROR
;
413 continue; // zone id is too long to extract
415 CanonicalMapEntry
*entry
= (CanonicalMapEntry
*)uhash_get(canonicalMap
, zoneUChars
);
417 // Already included in CLDR data
420 // Not in CLDR data, but it could be new one whose alias is available
422 int32_t nTzdataEquivalent
= TimeZone::countEquivalentIDs(*zone
);
424 for (j
= 0; j
< nTzdataEquivalent
; j
++) {
425 UnicodeString alias
= TimeZone::getEquivalentID(*zone
, j
);
426 if (alias
== *zone
) {
429 UChar aliasUChars
[ZID_KEY_MAX
];
430 alias
.extract(aliasUChars
, ZID_KEY_MAX
, status
);
431 if (U_FAILURE(status
) || status
==U_STRING_NOT_TERMINATED_WARNING
) {
432 status
= U_ZERO_ERROR
;
433 continue; // zone id is too long to extract
435 entry
= (CanonicalMapEntry
*)uhash_get(canonicalMap
, aliasUChars
);
440 // Create a new map entry
441 CanonicalMapEntry
* newEntry
= (CanonicalMapEntry
*)uprv_malloc(sizeof(CanonicalMapEntry
));
443 if (newEntry
== NULL
) {
444 status
= U_MEMORY_ALLOCATION_ERROR
;
448 // Set dereferenced zone ID as the canonical ID
449 UnicodeString derefZone
;
450 TimeZone::dereferOlsonLink(*zone
, derefZone
);
451 if (derefZone
.length() == 0) {
452 // It should never happen.. but just in case
455 idLen
= derefZone
.length() + 1;
456 newEntry
->id
= allocUStringInTable(idLen
);
457 if (newEntry
->id
== NULL
) {
458 status
= U_MEMORY_ALLOCATION_ERROR
;
462 // Copy NULL terminated string
463 derefZone
.extract((UChar
*)(newEntry
->id
), idLen
, status
);
464 if (U_FAILURE(status
)) {
465 removeLastUStringFromTable();
469 // No territory information available
470 newEntry
->country
= NULL
;
472 // Duplicate the entry
473 newEntry
->id
= entry
->id
;
474 newEntry
->country
= entry
->country
;
477 // Put this entry in the hashtable
478 UChar
*key
= allocUStringInTable(zoneUCharsLen
);
480 status
= U_MEMORY_ALLOCATION_ERROR
;
481 deleteCanonicalMapEntry(newEntry
);
484 u_strncpy(key
, zoneUChars
, zoneUCharsLen
);
485 uhash_put(canonicalMap
, key
, newEntry
, &status
);
486 if (U_FAILURE(status
)) {
495 ures_close(zoneFormatting
);
500 if (canonicalMap
!= NULL
) {
501 uhash_close(canonicalMap
);
508 * Creating Olson tzid to metazone mappings from resource (3.8.1 and beyond)
511 ZoneMeta::createOlsonToMetaMap(void) {
512 UErrorCode status
= U_ZERO_ERROR
;
514 UHashtable
*olsonToMeta
= NULL
;
515 UResourceBundle
*metazoneMappings
= NULL
;
516 UResourceBundle
*zoneItem
= NULL
;
517 UResourceBundle
*mz
= NULL
;
518 StringEnumeration
*tzids
= NULL
;
520 olsonToMeta
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
521 if (U_FAILURE(status
)) {
524 uhash_setKeyDeleter(olsonToMeta
, deleteUCharString
);
525 uhash_setValueDeleter(olsonToMeta
, deleteUVector
);
527 // Read metazone mappings from metazoneInfo bundle
528 metazoneMappings
= ures_openDirect(NULL
, gMetazoneInfo
, &status
);
529 metazoneMappings
= ures_getByKey(metazoneMappings
, gMetazoneMappings
, metazoneMappings
, &status
);
530 if (U_FAILURE(status
)) {
534 // Walk through all canonical tzids
535 char zidkey
[ZID_KEY_MAX
];
537 tzids
= TimeZone::createEnumeration();
538 const UnicodeString
*tzid
;
539 while ((tzid
= tzids
->snext(status
))) {
540 if (U_FAILURE(status
)) {
543 // We may skip aliases, because the bundle
544 // contains only canonical IDs. For now, try
546 tzid
->extract(0, tzid
->length(), zidkey
, sizeof(zidkey
), US_INV
);
547 zidkey
[sizeof(zidkey
)-1] = 0; // NULL terminate just in case.
549 // Replace '/' with ':'
550 UBool foundSep
= FALSE
;
560 // A valid time zone key has at least one separator
564 zoneItem
= ures_getByKey(metazoneMappings
, zidkey
, zoneItem
, &status
);
565 if (U_FAILURE(status
)) {
566 status
= U_ZERO_ERROR
;
570 UVector
*mzMappings
= NULL
;
571 while (ures_hasNext(zoneItem
)) {
572 mz
= ures_getNextResource(zoneItem
, mz
, &status
);
573 const UChar
*mz_name
= ures_getStringByIndex(mz
, 0, NULL
, &status
);
574 const UChar
*mz_from
= ures_getStringByIndex(mz
, 1, NULL
, &status
);
575 const UChar
*mz_to
= ures_getStringByIndex(mz
, 2, NULL
, &status
);
577 if(U_FAILURE(status
)){
578 status
= U_ZERO_ERROR
;
581 // We do not want to use SimpleDateformat to parse boundary dates,
582 // because this code could be triggered by the initialization code
583 // used by SimpleDateFormat.
584 UDate from
= parseDate(mz_from
, status
);
585 UDate to
= parseDate(mz_to
, status
);
586 if (U_FAILURE(status
)) {
587 status
= U_ZERO_ERROR
;
591 OlsonToMetaMappingEntry
*entry
= (OlsonToMetaMappingEntry
*)uprv_malloc(sizeof(OlsonToMetaMappingEntry
));
593 status
= U_MEMORY_ALLOCATION_ERROR
;
596 entry
->mzid
= mz_name
;
600 if (mzMappings
== NULL
) {
601 mzMappings
= new UVector(deleteOlsonToMetaMappingEntry
, NULL
, status
);
602 if (U_FAILURE(status
)) {
604 deleteOlsonToMetaMappingEntry(entry
);
610 mzMappings
->addElement(entry
, status
);
611 if (U_FAILURE(status
)) {
616 if (U_FAILURE(status
)) {
617 if (mzMappings
!= NULL
) {
622 if (mzMappings
!= NULL
) {
624 int32_t tzidLen
= tzid
->length() + 1; // Add one for NUL terminator
625 UChar
*key
= (UChar
*)uprv_malloc(tzidLen
* sizeof(UChar
));
627 status
= U_MEMORY_ALLOCATION_ERROR
;
631 tzid
->extract(key
, tzidLen
, status
);
632 uhash_put(olsonToMeta
, key
, mzMappings
, &status
);
633 if (U_FAILURE(status
)) {
643 ures_close(zoneItem
);
645 ures_close(metazoneMappings
);
649 if (olsonToMeta
!= NULL
) {
650 uhash_close(olsonToMeta
);
657 ZoneMeta::createMetaToOlsonMap(void) {
658 UErrorCode status
= U_ZERO_ERROR
;
660 UHashtable
*metaToOlson
= NULL
;
661 UResourceBundle
*metazones
= NULL
;
662 UResourceBundle
*mz
= NULL
;
664 metaToOlson
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
665 if (U_FAILURE(status
)) {
668 uhash_setKeyDeleter(metaToOlson
, deleteUCharString
);
669 uhash_setValueDeleter(metaToOlson
, deleteUVector
);
671 metazones
= ures_openDirect(NULL
, gSupplementalData
, &status
);
672 metazones
= ures_getByKey(metazones
, gMapTimezonesTag
, metazones
, &status
);
673 metazones
= ures_getByKey(metazones
, gMetazonesTag
, metazones
, &status
);
674 if (U_FAILURE(status
)) {
678 while (ures_hasNext(metazones
)) {
679 mz
= ures_getNextResource(metazones
, mz
, &status
);
680 if (U_FAILURE(status
)) {
681 status
= U_ZERO_ERROR
;
684 const char *mzkey
= ures_getKey(mz
);
685 if (uprv_strncmp(mzkey
, gMetazoneIdPrefix
, MZID_PREFIX_LEN
) == 0) {
686 const char *mzid
= mzkey
+ MZID_PREFIX_LEN
;
687 const char *territory
= uprv_strrchr(mzid
, '_');
689 int32_t territoryLen
= 0;
691 mzidLen
= territory
- mzid
;
693 territoryLen
= uprv_strlen(territory
);
695 if (mzidLen
> 0 && territoryLen
> 0) {
697 const UChar
*tzid
= ures_getStringByIndex(mz
, 0, &tzidLen
, &status
);
698 if (U_SUCCESS(status
)) {
699 // Create MetaToOlsonMappingEntry
700 MetaToOlsonMappingEntry
*entry
= (MetaToOlsonMappingEntry
*)uprv_malloc(sizeof(MetaToOlsonMappingEntry
));
702 status
= U_MEMORY_ALLOCATION_ERROR
;
706 entry
->territory
= (UChar
*)uprv_malloc((territoryLen
+ 1) * sizeof(UChar
));
707 if (entry
->territory
== NULL
) {
708 status
= U_MEMORY_ALLOCATION_ERROR
;
712 u_charsToUChars(territory
, entry
->territory
, territoryLen
+ 1);
714 // Check if mapping entries for metazone is already available
715 if (mzidLen
< ZID_KEY_MAX
) {
716 UChar mzidUChars
[ZID_KEY_MAX
];
717 u_charsToUChars(mzid
, mzidUChars
, mzidLen
);
718 mzidUChars
[mzidLen
++] = 0; // Add NUL terminator
719 UVector
*tzMappings
= (UVector
*)uhash_get(metaToOlson
, mzidUChars
);
720 if (tzMappings
== NULL
) {
721 // Create new UVector and put it into the hashtable
722 tzMappings
= new UVector(deleteMetaToOlsonMappingEntry
, NULL
, status
);
723 if (U_FAILURE(status
)) {
724 deleteMetaToOlsonMappingEntry(entry
);
727 UChar
*key
= (UChar
*)uprv_malloc(mzidLen
* sizeof(UChar
));
729 status
= U_MEMORY_ALLOCATION_ERROR
;
731 deleteMetaToOlsonMappingEntry(entry
);
734 u_strncpy(key
, mzidUChars
, mzidLen
);
735 uhash_put(metaToOlson
, key
, tzMappings
, &status
);
736 if (U_FAILURE(status
)) {
740 tzMappings
->addElement(entry
, status
);
741 if (U_FAILURE(status
)) {
745 deleteMetaToOlsonMappingEntry(entry
);
748 status
= U_ZERO_ERROR
;
756 ures_close(metazones
);
760 if (metaToOlson
!= NULL
) {
761 uhash_close(metaToOlson
);
768 * Initialize global objects
771 ZoneMeta::initializeCanonicalMap(void) {
773 UMTX_CHECK(&gZoneMetaLock
, gCanonicalMapInitialized
, initialized
);
777 // Initialize hash table
778 UHashtable
*tmpCanonicalMap
= createCanonicalMap();
780 umtx_lock(&gZoneMetaLock
);
781 if (!gCanonicalMapInitialized
) {
782 gCanonicalMap
= tmpCanonicalMap
;
783 tmpCanonicalMap
= NULL
;
784 gCanonicalMapInitialized
= TRUE
;
786 umtx_unlock(&gZoneMetaLock
);
788 // OK to call the following multiple times with the same function
789 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
790 if (tmpCanonicalMap
!= NULL
) {
791 uhash_close(tmpCanonicalMap
);
796 ZoneMeta::initializeOlsonToMeta(void) {
798 UMTX_CHECK(&gZoneMetaLock
, gOlsonToMetaInitialized
, initialized
);
802 // Initialize hash tables
803 UHashtable
*tmpOlsonToMeta
= createOlsonToMetaMap();
805 umtx_lock(&gZoneMetaLock
);
806 if (!gOlsonToMetaInitialized
) {
807 gOlsonToMeta
= tmpOlsonToMeta
;
808 tmpOlsonToMeta
= NULL
;
809 gOlsonToMetaInitialized
= TRUE
;
811 umtx_unlock(&gZoneMetaLock
);
813 // OK to call the following multiple times with the same function
814 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
815 if (tmpOlsonToMeta
!= NULL
) {
816 uhash_close(tmpOlsonToMeta
);
821 ZoneMeta::initializeMetaToOlson(void) {
823 UMTX_CHECK(&gZoneMetaLock
, gMetaToOlsonInitialized
, initialized
);
827 // Initialize hash table
828 UHashtable
*tmpMetaToOlson
= createMetaToOlsonMap();
830 umtx_lock(&gZoneMetaLock
);
831 if (!gMetaToOlsonInitialized
) {
832 gMetaToOlson
= tmpMetaToOlson
;
833 tmpMetaToOlson
= NULL
;
834 gMetaToOlsonInitialized
= TRUE
;
836 umtx_unlock(&gZoneMetaLock
);
838 // OK to call the following multiple times with the same function
839 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA
, zoneMeta_cleanup
);
840 if (tmpMetaToOlson
!= NULL
) {
841 uhash_close(tmpMetaToOlson
);
845 UnicodeString
& U_EXPORT2
846 ZoneMeta::getCanonicalSystemID(const UnicodeString
&tzid
, UnicodeString
&systemID
, UErrorCode
& status
) {
847 const CanonicalMapEntry
*entry
= getCanonicalInfo(tzid
);
849 systemID
.setTo(entry
->id
);
851 status
= U_ILLEGAL_ARGUMENT_ERROR
;
856 UnicodeString
& U_EXPORT2
857 ZoneMeta::getCanonicalCountry(const UnicodeString
&tzid
, UnicodeString
&canonicalCountry
) {
858 const CanonicalMapEntry
*entry
= getCanonicalInfo(tzid
);
859 if (entry
!= NULL
&& entry
->country
!= NULL
) {
860 canonicalCountry
.setTo(entry
->country
);
862 // Use the input tzid
863 canonicalCountry
.remove();
865 return canonicalCountry
;
868 const CanonicalMapEntry
* U_EXPORT2
869 ZoneMeta::getCanonicalInfo(const UnicodeString
&tzid
) {
870 initializeCanonicalMap();
871 CanonicalMapEntry
*entry
= NULL
;
872 if (gCanonicalMap
!= NULL
) {
873 UErrorCode status
= U_ZERO_ERROR
;
874 UChar tzidUChars
[ZID_KEY_MAX
];
875 tzid
.extract(tzidUChars
, ZID_KEY_MAX
, status
);
876 if (U_SUCCESS(status
) && status
!=U_STRING_NOT_TERMINATED_WARNING
) {
877 entry
= (CanonicalMapEntry
*)uhash_get(gCanonicalMap
, tzidUChars
);
883 UnicodeString
& U_EXPORT2
884 ZoneMeta::getSingleCountry(const UnicodeString
&tzid
, UnicodeString
&country
) {
885 UErrorCode status
= U_ZERO_ERROR
;
887 // Get canonical country for the zone
888 getCanonicalCountry(tzid
, country
);
890 if (!country
.isEmpty()) {
891 UResourceBundle
*supplementalDataBundle
= ures_openDirect(NULL
, gSupplementalData
, &status
);
892 UResourceBundle
*zoneFormatting
= ures_getByKey(supplementalDataBundle
, gZoneFormattingTag
, NULL
, &status
);
893 UResourceBundle
*multizone
= ures_getByKey(zoneFormatting
, gMultizoneTag
, NULL
, &status
);
895 if (U_SUCCESS(status
)) {
896 while (ures_hasNext(multizone
)) {
898 const UChar
* multizoneCountry
= ures_getNextString(multizone
, &len
, NULL
, &status
);
899 if (country
.compare(multizoneCountry
, len
) == 0) {
900 // Included in the multizone country list
907 ures_close(multizone
);
908 ures_close(zoneFormatting
);
909 ures_close(supplementalDataBundle
);
915 UnicodeString
& U_EXPORT2
916 ZoneMeta::getMetazoneID(const UnicodeString
&tzid
, UDate date
, UnicodeString
&result
) {
918 const UVector
*mappings
= getMetazoneMappings(tzid
);
919 if (mappings
!= NULL
) {
920 for (int32_t i
= 0; i
< mappings
->size(); i
++) {
921 OlsonToMetaMappingEntry
*mzm
= (OlsonToMetaMappingEntry
*)mappings
->elementAt(i
);
922 if (mzm
->from
<= date
&& mzm
->to
> date
) {
923 result
.setTo(mzm
->mzid
, -1);
935 const UVector
* U_EXPORT2
936 ZoneMeta::getMetazoneMappings(const UnicodeString
&tzid
) {
937 initializeOlsonToMeta();
938 const UVector
*result
= NULL
;
939 if (gOlsonToMeta
!= NULL
) {
940 UErrorCode status
= U_ZERO_ERROR
;
941 UChar tzidUChars
[ZID_KEY_MAX
];
942 tzid
.extract(tzidUChars
, ZID_KEY_MAX
, status
);
943 if (U_SUCCESS(status
) && status
!=U_STRING_NOT_TERMINATED_WARNING
) {
944 result
= (UVector
*)uhash_get(gOlsonToMeta
, tzidUChars
);
950 UnicodeString
& U_EXPORT2
951 ZoneMeta::getZoneIdByMetazone(const UnicodeString
&mzid
, const UnicodeString
®ion
, UnicodeString
&result
) {
952 initializeMetaToOlson();
954 if (gMetaToOlson
!= NULL
) {
955 UErrorCode status
= U_ZERO_ERROR
;
956 UChar mzidUChars
[ZID_KEY_MAX
];
957 mzid
.extract(mzidUChars
, ZID_KEY_MAX
, status
);
958 if (U_SUCCESS(status
) && status
!=U_STRING_NOT_TERMINATED_WARNING
) {
959 UVector
*mappings
= (UVector
*)uhash_get(gMetaToOlson
, mzidUChars
);
960 if (mappings
!= NULL
) {
961 // Find a preferred time zone for the given region.
962 for (int32_t i
= 0; i
< mappings
->size(); i
++) {
963 MetaToOlsonMappingEntry
*olsonmap
= (MetaToOlsonMappingEntry
*)mappings
->elementAt(i
);
964 if (region
.compare(olsonmap
->territory
, -1) == 0) {
965 result
.setTo(olsonmap
->id
);
968 } else if (u_strcmp(olsonmap
->territory
, gWorld
) == 0) {
969 result
.setTo(olsonmap
->id
);
985 #endif /* #if !UCONFIG_NO_FORMATTING */