1 /*
2 *******************************************************************************
3 * Copyright (C) 2007, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
6 */
7
8 #include "unicode/utypes.h"
9
10 #if !UCONFIG_NO_FORMATTING
11
12 #include "zonemeta.h"
13
14 #include "unicode/timezone.h"
15 #include "unicode/ustring.h"
16 #include "unicode/putil.h"
17
18 #include "umutex.h"
19 #include "uvector.h"
20 #include "cmemory.h"
21 #include "gregoimp.h"
22 #include "cstring.h"
23 #include "ucln_in.h"
24
25 static UBool gZoneMetaInitialized = FALSE;
26
27 // Metazone mapping tables
28 static UMTX gZoneMetaLock = NULL;
29 static U_NAMESPACE_QUALIFIER Hashtable *gCanonicalMap = NULL;
30 static U_NAMESPACE_QUALIFIER Hashtable *gOlsonToMeta = NULL;
31 static U_NAMESPACE_QUALIFIER Hashtable *gMetaToOlson = NULL;
32
33 U_CDECL_BEGIN
34 /**
35 * Cleanup callback func
36 */
zoneMeta_cleanup(void)37 static UBool U_CALLCONV zoneMeta_cleanup(void)
38 {
39 umtx_destroy(&gZoneMetaLock);
40
41 if (gCanonicalMap != NULL) {
42 delete gCanonicalMap;
43 gCanonicalMap = NULL;
44 }
45
46 if (gOlsonToMeta != NULL) {
47 delete gOlsonToMeta;
48 gOlsonToMeta = NULL;
49 }
50
51 if (gMetaToOlson != NULL) {
52 delete gMetaToOlson;
53 gMetaToOlson = NULL;
54 }
55
56 gZoneMetaInitialized = FALSE;
57
58 return TRUE;
59 }
60
61 /**
62 * Deleter for UVector
63 */
64 static void U_CALLCONV
deleteUVector(void * obj)65 deleteUVector(void *obj) {
66 delete (U_NAMESPACE_QUALIFIER UVector*) obj;
67 }
68
69 /**
70 * Deleter for CanonicalMapEntry
71 */
72 static void U_CALLCONV
deleteCanonicalMapEntry(void * obj)73 deleteCanonicalMapEntry(void *obj) {
74 U_NAMESPACE_QUALIFIER CanonicalMapEntry *entry = (U_NAMESPACE_QUALIFIER CanonicalMapEntry*)obj;
75 uprv_free(entry->id);
76 uprv_free(entry);
77 }
78
79 /**
80 * Deleter for OlsonToMetaMappingEntry
81 */
82 static void U_CALLCONV
deleteOlsonToMetaMappingEntry(void * obj)83 deleteOlsonToMetaMappingEntry(void *obj) {
84 U_NAMESPACE_QUALIFIER OlsonToMetaMappingEntry *entry = (U_NAMESPACE_QUALIFIER OlsonToMetaMappingEntry*)obj;
85 uprv_free(entry);
86 }
87
88 /**
89 * Deleter for MetaToOlsonMappingEntry
90 */
91 static void U_CALLCONV
deleteMetaToOlsonMappingEntry(void * obj)92 deleteMetaToOlsonMappingEntry(void *obj) {
93 U_NAMESPACE_QUALIFIER MetaToOlsonMappingEntry *entry = (U_NAMESPACE_QUALIFIER MetaToOlsonMappingEntry*)obj;
94 uprv_free(entry->territory);
95 uprv_free(entry);
96 }
97 U_CDECL_END
98
99 U_NAMESPACE_BEGIN
100
101 #define ZID_KEY_MAX 128
102 static const char gZoneStringsTag[] = "zoneStrings";
103 static const char gUseMetazoneTag[] = "um";
104
105 static const char gSupplementalData[] = "supplementalData";
106 static const char gMapTimezonesTag[] = "mapTimezones";
107 static const char gMetazonesTag[] = "metazones";
108 static const char gZoneFormattingTag[] = "zoneFormatting";
109 static const char gTerritoryTag[] = "territory";
110 static const char gAliasesTag[] = "aliases";
111 static const char gMultizoneTag[] = "multizone";
112
113 static const char gMetazoneInfo[] = "metazoneInfo";
114 static const char gMetazoneMappings[] = "metazoneMappings";
115
116 #define MZID_PREFIX_LEN 5
117 static const char gMetazoneIdPrefix[] = "meta:";
118
119 static const UChar gWorld[] = {0x30, 0x30, 0x31, 0x00}; // "001"
120
121 #define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1)
122
123 /*
124 * Convert a date string used by metazone mappings to UDate.
125 * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
126 */
parseDate(const UChar * text,UErrorCode & status)127 static UDate parseDate (const UChar *text, UErrorCode &status) {
128 if (U_FAILURE(status)) {
129 return 0;
130 }
131 int32_t len = u_strlen(text);
132 if (len != 16 && len != 10) {
133 // It must be yyyy-MM-dd HH:mm (length 16) or yyyy-MM-dd (length 10)
134 status = U_INVALID_FORMAT_ERROR;
135 return 0;
136 }
137
138 int32_t year = 0, month = 0, day = 0, hour = 0, min = 0, n;
139 int32_t idx;
140
141 // "yyyy" (0 - 3)
142 for (idx = 0; idx <= 3 && U_SUCCESS(status); idx++) {
143 n = ASCII_DIGIT(text[idx]);
144 if (n >= 0) {
145 year = 10*year + n;
146 } else {
147 status = U_INVALID_FORMAT_ERROR;
148 }
149 }
150 // "MM" (5 - 6)
151 for (idx = 5; idx <= 6 && U_SUCCESS(status); idx++) {
152 n = ASCII_DIGIT(text[idx]);
153 if (n >= 0) {
154 month = 10*month + n;
155 } else {
156 status = U_INVALID_FORMAT_ERROR;
157 }
158 }
159 // "dd" (8 - 9)
160 for (idx = 8; idx <= 9 && U_SUCCESS(status); idx++) {
161 n = ASCII_DIGIT(text[idx]);
162 if (n >= 0) {
163 day = 10*day + n;
164 } else {
165 status = U_INVALID_FORMAT_ERROR;
166 }
167 }
168 if (len == 16) {
169 // "HH" (11 - 12)
170 for (idx = 11; idx <= 12 && U_SUCCESS(status); idx++) {
171 n = ASCII_DIGIT(text[idx]);
172 if (n >= 0) {
173 hour = 10*hour + n;
174 } else {
175 status = U_INVALID_FORMAT_ERROR;
176 }
177 }
178 // "mm" (14 - 15)
179 for (idx = 14; idx <= 15 && U_SUCCESS(status); idx++) {
180 n = ASCII_DIGIT(text[idx]);
181 if (n >= 0) {
182 min = 10*min + n;
183 } else {
184 status = U_INVALID_FORMAT_ERROR;
185 }
186 }
187 }
188
189 if (U_SUCCESS(status)) {
190 UDate date = Grego::fieldsToDay(year, month - 1, day) * U_MILLIS_PER_DAY
191 + hour * U_MILLIS_PER_HOUR + min * U_MILLIS_PER_MINUTE;
192 return date;
193 }
194 return 0;
195 }
196
197 /*
198 * Initialize global objects
199 */
200 void
initialize(void)201 ZoneMeta::initialize(void) {
202 UBool initialized;
203 UMTX_CHECK(&gZoneMetaLock, gZoneMetaInitialized, initialized);
204 if (initialized) {
205 return;
206 }
207
208 // Initialize hash tables
209 Hashtable *tmpCanonicalMap = createCanonicalMap();
210 Hashtable *tmpOlsonToMeta = createOlsonToMetaMap();
211 if (tmpOlsonToMeta == NULL) {
212 // With ICU 3.8 data
213 tmpOlsonToMeta = createOlsonToMetaMapOld();
214 }
215 Hashtable *tmpMetaToOlson = createMetaToOlsonMap();
216
217 umtx_lock(&gZoneMetaLock);
218 if (gZoneMetaInitialized) {
219 // Another thread already created mappings
220 delete tmpCanonicalMap;
221 delete tmpOlsonToMeta;
222 delete tmpMetaToOlson;
223 } else {
224 gZoneMetaInitialized = TRUE;
225 gCanonicalMap = tmpCanonicalMap;
226 gOlsonToMeta = tmpOlsonToMeta;
227 gMetaToOlson = tmpMetaToOlson;
228 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
229 }
230 umtx_unlock(&gZoneMetaLock);
231 }
232
233 Hashtable*
createCanonicalMap(void)234 ZoneMeta::createCanonicalMap(void) {
235 UErrorCode status = U_ZERO_ERROR;
236
237 Hashtable *canonicalMap = NULL;
238 UResourceBundle *zoneFormatting = NULL;
239 UResourceBundle *tzitem = NULL;
240 UResourceBundle *aliases = NULL;
241
242 canonicalMap = new Hashtable(uhash_compareUnicodeString, NULL, status);
243 if (U_FAILURE(status)) {
244 return NULL;
245 }
246 canonicalMap->setValueDeleter(deleteCanonicalMapEntry);
247
248 zoneFormatting = ures_openDirect(NULL, gSupplementalData, &status);
249 zoneFormatting = ures_getByKey(zoneFormatting, gZoneFormattingTag, zoneFormatting, &status);
250 if (U_FAILURE(status)) {
251 goto error_cleanup;
252 }
253
254 while (ures_hasNext(zoneFormatting)) {
255 tzitem = ures_getNextResource(zoneFormatting, tzitem, &status);
256 if (U_FAILURE(status)) {
257 status = U_ZERO_ERROR;
258 continue;
259 }
260 if (ures_getType(tzitem) != URES_TABLE) {
261 continue;
262 }
263
264 int32_t territoryLen;
265 const UChar *territory = ures_getStringByKey(tzitem, gTerritoryTag, &territoryLen, &status);
266 if (U_FAILURE(status)) {
267 territory = NULL;
268 status = U_ZERO_ERROR;
269 }
270
271 int32_t tzidLen = 0;
272 char tzid[ZID_KEY_MAX];
273 const char *tzkey = ures_getKey(tzitem);
274 uprv_strcpy(tzid, tzkey);
275 // Replace ':' with '/'
276 char *p = tzid;
277 while (*p) {
278 if (*p == ':') {
279 *p = '/';
280 }
281 p++;
282 tzidLen++;
283 }
284
285 // Create canonical map entry
286 CanonicalMapEntry *entry = (CanonicalMapEntry*)uprv_malloc(sizeof(CanonicalMapEntry));
287 if (entry == NULL) {
288 status = U_MEMORY_ALLOCATION_ERROR;
289 goto error_cleanup;
290 }
291 entry->id = (UChar*)uprv_malloc((tzidLen + 1) * sizeof(UChar));
292 if (entry->id == NULL) {
293 status = U_MEMORY_ALLOCATION_ERROR;
294 uprv_free(entry);
295 goto error_cleanup;
296 }
297 u_charsToUChars(tzid, entry->id, tzidLen + 1);
298
299 if (territory == NULL || u_strcmp(territory, gWorld) == 0) {
300 entry->country = NULL;
301 } else {
302 entry->country = territory;
303 }
304
305 // Put this entry to the table
306 canonicalMap->put(UnicodeString(entry->id), entry, status);
307 if (U_FAILURE(status)) {
308 deleteCanonicalMapEntry(entry);
309 goto error_cleanup;
310 }
311
312 // Get aliases
313 aliases = ures_getByKey(tzitem, gAliasesTag, aliases, &status);
314 if (U_FAILURE(status)) {
315 // No aliases
316 status = U_ZERO_ERROR;
317 continue;
318 }
319
320 while (ures_hasNext(aliases)) {
321 const UChar* alias = ures_getNextString(aliases, NULL, NULL, &status);
322 if (U_FAILURE(status)) {
323 status = U_ZERO_ERROR;
324 continue;
325 }
326 // Create canonical map entry for this alias
327 entry = (CanonicalMapEntry*)uprv_malloc(sizeof(CanonicalMapEntry));
328 if (entry == NULL) {
329 status = U_MEMORY_ALLOCATION_ERROR;
330 goto error_cleanup;
331 }
332 entry->id = (UChar*)uprv_malloc((tzidLen + 1) * sizeof(UChar));
333 if (entry->id == NULL) {
334 status = U_MEMORY_ALLOCATION_ERROR;
335 uprv_free(entry);
336 goto error_cleanup;
337 }
338 u_charsToUChars(tzid, entry->id, tzidLen + 1);
339
340 if (territory == NULL || u_strcmp(territory, gWorld) == 0) {
341 entry->country = NULL;
342 } else {
343 entry->country = territory;
344 }
345 canonicalMap->put(UnicodeString(alias), entry, status);
346 if (U_FAILURE(status)) {
347 deleteCanonicalMapEntry(entry);
348 goto error_cleanup;
349 }
350 }
351 }
352
353 normal_cleanup:
354 ures_close(aliases);
355 ures_close(tzitem);
356 ures_close(zoneFormatting);
357 return canonicalMap;
358
359 error_cleanup:
360 delete canonicalMap;
361 canonicalMap = NULL;
362
363 goto normal_cleanup;
364 }
365
366 /*
367 * Creating Olson tzid to metazone mappings from resource (3.8.1 and beyond)
368 */
369 Hashtable*
createOlsonToMetaMap(void)370 ZoneMeta::createOlsonToMetaMap(void) {
371 UErrorCode status = U_ZERO_ERROR;
372
373 Hashtable *olsonToMeta = NULL;
374 UResourceBundle *metazoneMappings = NULL;
375 UResourceBundle *zoneItem = NULL;
376 UResourceBundle *mz = NULL;
377 StringEnumeration *tzids = NULL;
378
379 olsonToMeta = new Hashtable(uhash_compareUnicodeString, NULL, status);
380 if (U_FAILURE(status)) {
381 return NULL;
382 }
383 olsonToMeta->setValueDeleter(deleteUVector);
384
385 // Read metazone mappings from metazoneInfo bundle
386 metazoneMappings = ures_openDirect(NULL, gMetazoneInfo, &status);
387 metazoneMappings = ures_getByKey(metazoneMappings, gMetazoneMappings, metazoneMappings, &status);
388 if (U_FAILURE(status)) {
389 goto error_cleanup;
390 }
391
392 // Walk through all canonical tzids
393 char zidkey[ZID_KEY_MAX];
394
395 tzids = TimeZone::createEnumeration();
396 const UnicodeString *tzid;
397 while ((tzid = tzids->snext(status))) {
398 if (U_FAILURE(status)) {
399 goto error_cleanup;
400 }
401 // We may skip aliases, because the bundle
402 // contains only canonical IDs. For now, try
403 // all of them.
404 tzid->extract(0, tzid->length(), zidkey, sizeof(zidkey), US_INV);
405 zidkey[sizeof(zidkey)-1] = 0; // NULL terminate just in case.
406
407 // Replace '/' with ':'
408 UBool foundSep = FALSE;
409 char *p = zidkey;
410 while (*p) {
411 if (*p == '/') {
412 *p = ':';
413 foundSep = TRUE;
414 }
415 p++;
416 }
417 if (!foundSep) {
418 // A valid time zone key has at least one separator
419 continue;
420 }
421
422 zoneItem = ures_getByKey(metazoneMappings, zidkey, zoneItem, &status);
423 if (U_FAILURE(status)) {
424 status = U_ZERO_ERROR;
425 continue;
426 }
427
428 UVector *mzMappings = NULL;
429 while (ures_hasNext(zoneItem)) {
430 mz = ures_getNextResource(zoneItem, mz, &status);
431 const UChar *mz_name = ures_getStringByIndex(mz, 0, NULL, &status);
432 const UChar *mz_from = ures_getStringByIndex(mz, 1, NULL, &status);
433 const UChar *mz_to = ures_getStringByIndex(mz, 2, NULL, &status);
434
435 if(U_FAILURE(status)){
436 status = U_ZERO_ERROR;
437 continue;
438 }
439 // We do not want to use SimpleDateformat to parse boundary dates,
440 // because this code could be triggered by the initialization code
441 // used by SimpleDateFormat.
442 UDate from = parseDate(mz_from, status);
443 UDate to = parseDate(mz_to, status);
444 if (U_FAILURE(status)) {
445 status = U_ZERO_ERROR;
446 continue;
447 }
448
449 OlsonToMetaMappingEntry *entry = (OlsonToMetaMappingEntry*)uprv_malloc(sizeof(OlsonToMetaMappingEntry));
450 if (entry == NULL) {
451 status = U_MEMORY_ALLOCATION_ERROR;
452 break;
453 }
454 entry->mzid = mz_name;
455 entry->from = from;
456 entry->to = to;
457
458 if (mzMappings == NULL) {
459 mzMappings = new UVector(deleteOlsonToMetaMappingEntry, NULL, status);
460 if (U_FAILURE(status)) {
461 delete mzMappings;
462 deleteOlsonToMetaMappingEntry(entry);
463 uprv_free(entry);
464 break;
465 }
466 }
467
468 mzMappings->addElement(entry, status);
469 if (U_FAILURE(status)) {
470 break;
471 }
472 }
473
474 if (U_FAILURE(status)) {
475 if (mzMappings != NULL) {
476 delete mzMappings;
477 }
478 goto error_cleanup;
479 }
480 if (mzMappings != NULL) {
481 olsonToMeta->put(*tzid, mzMappings, status);
482 if (U_FAILURE(status)) {
483 delete mzMappings;
484 goto error_cleanup;
485 }
486 }
487 }
488
489 normal_cleanup:
490 if (tzids != NULL) {
491 delete tzids;
492 }
493 ures_close(zoneItem);
494 ures_close(mz);
495 ures_close(metazoneMappings);
496 return olsonToMeta;
497
498 error_cleanup:
499 if (olsonToMeta != NULL) {
500 delete olsonToMeta;
501 olsonToMeta = NULL;
502 }
503 goto normal_cleanup;
504 }
505
506 /*
507 * Creating Olson tzid to metazone mappings from ICU resource (3.8)
508 */
509 Hashtable*
createOlsonToMetaMapOld(void)510 ZoneMeta::createOlsonToMetaMapOld(void) {
511 UErrorCode status = U_ZERO_ERROR;
512
513 Hashtable *olsonToMeta = NULL;
514 UResourceBundle *zoneStringsArray = NULL;
515 UResourceBundle *mz = NULL;
516 UResourceBundle *zoneItem = NULL;
517 UResourceBundle *useMZ = NULL;
518 StringEnumeration *tzids = NULL;
519
520 olsonToMeta = new Hashtable(uhash_compareUnicodeString, NULL, status);
521 if (U_FAILURE(status)) {
522 return NULL;
523 }
524 olsonToMeta->setValueDeleter(deleteUVector);
525
526 // Read metazone mappings from root bundle
527 zoneStringsArray = ures_openDirect(NULL, "", &status);
528 zoneStringsArray = ures_getByKey(zoneStringsArray, gZoneStringsTag, zoneStringsArray, &status);
529 if (U_FAILURE(status)) {
530 goto error_cleanup;
531 }
532
533 // Walk through all canonical tzids
534 char zidkey[ZID_KEY_MAX];
535
536 tzids = TimeZone::createEnumeration();
537 const UnicodeString *tzid;
538 while ((tzid = tzids->snext(status))) {
539 if (U_FAILURE(status)) {
540 goto error_cleanup;
541 }
542 // We may skip aliases, because the bundle
543 // contains only canonical IDs. For now, try
544 // all of them.
545 tzid->extract(0, tzid->length(), zidkey, sizeof(zidkey), US_INV);
546 zidkey[sizeof(zidkey)-1] = 0; // NULL terminate just in case.
547
548 // Replace '/' with ':'
549 UBool foundSep = FALSE;
550 char *p = zidkey;
551 while (*p) {
552 if (*p == '/') {
553 *p = ':';
554 foundSep = TRUE;
555 }
556 p++;
557 }
558 if (!foundSep) {
559 // A valid time zone key has at least one separator
560 continue;
561 }
562
563 zoneItem = ures_getByKey(zoneStringsArray, zidkey, zoneItem, &status);
564 useMZ = ures_getByKey(zoneItem, gUseMetazoneTag, useMZ, &status);
565 if (U_FAILURE(status)) {
566 status = U_ZERO_ERROR;
567 continue;
568 }
569
570 UVector *mzMappings = NULL;
571 while (ures_hasNext(useMZ)) {
572 mz = ures_getNextResource(useMZ, 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);
576
577 if(U_FAILURE(status)){
578 status = U_ZERO_ERROR;
579 continue;
580 }
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;
588 continue;
589 }
590
591 OlsonToMetaMappingEntry *entry = (OlsonToMetaMappingEntry*)uprv_malloc(sizeof(OlsonToMetaMappingEntry));
592 if (entry == NULL) {
593 status = U_MEMORY_ALLOCATION_ERROR;
594 break;
595 }
596 entry->mzid = mz_name;
597 entry->from = from;
598 entry->to = to;
599
600 if (mzMappings == NULL) {
601 mzMappings = new UVector(deleteOlsonToMetaMappingEntry, NULL, status);
602 if (U_FAILURE(status)) {
603 delete mzMappings;
604 deleteOlsonToMetaMappingEntry(entry);
605 uprv_free(entry);
606 break;
607 }
608 }
609
610 mzMappings->addElement(entry, status);
611 if (U_FAILURE(status)) {
612 break;
613 }
614 }
615
616 if (U_FAILURE(status)) {
617 if (mzMappings != NULL) {
618 delete mzMappings;
619 }
620 goto error_cleanup;
621 }
622 if (mzMappings != NULL) {
623 olsonToMeta->put(*tzid, mzMappings, status);
624 if (U_FAILURE(status)) {
625 delete mzMappings;
626 goto error_cleanup;
627 }
628 }
629 }
630
631 normal_cleanup:
632 if (tzids != NULL) {
633 delete tzids;
634 }
635 ures_close(zoneItem);
636 ures_close(useMZ);
637 ures_close(mz);
638 ures_close(zoneStringsArray);
639 return olsonToMeta;
640
641 error_cleanup:
642 if (olsonToMeta != NULL) {
643 delete olsonToMeta;
644 }
645 goto normal_cleanup;
646 }
647
648 Hashtable*
createMetaToOlsonMap(void)649 ZoneMeta::createMetaToOlsonMap(void) {
650 UErrorCode status = U_ZERO_ERROR;
651
652 Hashtable *metaToOlson = NULL;
653 UResourceBundle *metazones = NULL;
654 UResourceBundle *mz = NULL;
655
656 metaToOlson = new Hashtable(uhash_compareUnicodeString, NULL, status);
657 if (U_FAILURE(status)) {
658 return NULL;
659 }
660 metaToOlson->setValueDeleter(deleteUVector);
661
662 metazones = ures_openDirect(NULL, gSupplementalData, &status);
663 metazones = ures_getByKey(metazones, gMapTimezonesTag, metazones, &status);
664 metazones = ures_getByKey(metazones, gMetazonesTag, metazones, &status);
665 if (U_FAILURE(status)) {
666 goto error_cleanup;
667 }
668
669 while (ures_hasNext(metazones)) {
670 mz = ures_getNextResource(metazones, mz, &status);
671 if (U_FAILURE(status)) {
672 status = U_ZERO_ERROR;
673 continue;
674 }
675 const char *mzkey = ures_getKey(mz);
676 if (uprv_strncmp(mzkey, gMetazoneIdPrefix, MZID_PREFIX_LEN) == 0) {
677 const char *mzid = mzkey + MZID_PREFIX_LEN;
678 const char *territory = uprv_strrchr(mzid, '_');
679 int32_t mzidLen = 0;
680 int32_t territoryLen = 0;
681 if (territory) {
682 mzidLen = territory - mzid;
683 territory++;
684 territoryLen = uprv_strlen(territory);
685 }
686 if (mzidLen > 0 && territoryLen > 0) {
687 int32_t tzidLen;
688 const UChar *tzid = ures_getStringByIndex(mz, 0, &tzidLen, &status);
689 if (U_SUCCESS(status)) {
690 // Create MetaToOlsonMappingEntry
691 MetaToOlsonMappingEntry *entry = (MetaToOlsonMappingEntry*)uprv_malloc(sizeof(MetaToOlsonMappingEntry));
692 if (entry == NULL) {
693 status = U_MEMORY_ALLOCATION_ERROR;
694 goto error_cleanup;
695 }
696 entry->id = tzid;
697 entry->territory = (UChar*)uprv_malloc((territoryLen + 1) * sizeof(UChar));
698 if (entry->territory == NULL) {
699 status = U_MEMORY_ALLOCATION_ERROR;
700 uprv_free(entry);
701 goto error_cleanup;
702 }
703 u_charsToUChars(territory, entry->territory, territoryLen + 1);
704
705 // Check if mapping entries for metazone is already available
706 UnicodeString mzidStr(mzid, mzidLen, US_INV);
707 UVector *tzMappings = (UVector*)metaToOlson->get(mzidStr);
708 if (tzMappings == NULL) {
709 // Create new UVector and put it into the hashtable
710 tzMappings = new UVector(deleteMetaToOlsonMappingEntry, NULL, status);
711 metaToOlson->put(mzidStr, tzMappings, status);
712 if (U_FAILURE(status)) {
713 if (tzMappings != NULL) {
714 delete tzMappings;
715 }
716 deleteMetaToOlsonMappingEntry(entry);
717 goto error_cleanup;
718 }
719 }
720 tzMappings->addElement(entry, status);
721 if (U_FAILURE(status)) {
722 goto error_cleanup;
723 }
724 } else {
725 status = U_ZERO_ERROR;
726 }
727 }
728 }
729 }
730
731 normal_cleanup:
732 ures_close(mz);
733 ures_close(metazones);
734 return metaToOlson;
735
736 error_cleanup:
737 if (metaToOlson != NULL) {
738 delete metaToOlson;
739 }
740 goto normal_cleanup;
741 }
742
743 UnicodeString&
getCanonicalID(const UnicodeString & tzid,UnicodeString & canonicalID)744 ZoneMeta::getCanonicalID(const UnicodeString &tzid, UnicodeString &canonicalID) {
745 const CanonicalMapEntry *entry = getCanonicalInfo(tzid);
746 if (entry != NULL) {
747 canonicalID.setTo(entry->id);
748 } else {
749 // Use the input tzid
750 canonicalID.setTo(tzid);
751 }
752 return canonicalID;
753 }
754
755 UnicodeString&
getCanonicalCountry(const UnicodeString & tzid,UnicodeString & canonicalCountry)756 ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &canonicalCountry) {
757 const CanonicalMapEntry *entry = getCanonicalInfo(tzid);
758 if (entry != NULL && entry->country != NULL) {
759 canonicalCountry.setTo(entry->country);
760 } else {
761 // Use the input tzid
762 canonicalCountry.remove();
763 }
764 return canonicalCountry;
765 }
766
767 const CanonicalMapEntry*
getCanonicalInfo(const UnicodeString & tzid)768 ZoneMeta::getCanonicalInfo(const UnicodeString &tzid) {
769 initialize();
770 CanonicalMapEntry *entry = NULL;
771 UnicodeString canonicalOlsonId;
772 TimeZone::getOlsonCanonicalID(tzid, canonicalOlsonId);
773 if (!canonicalOlsonId.isEmpty()) {
774 if (gCanonicalMap != NULL) {
775 entry = (CanonicalMapEntry*)gCanonicalMap->get(tzid);
776 }
777 }
778 return entry;
779 }
780
781 UnicodeString&
getSingleCountry(const UnicodeString & tzid,UnicodeString & country)782 ZoneMeta::getSingleCountry(const UnicodeString &tzid, UnicodeString &country) {
783 UErrorCode status = U_ZERO_ERROR;
784
785 // Get canonical country for the zone
786 getCanonicalCountry(tzid, country);
787
788 if (!country.isEmpty()) {
789 UResourceBundle *supplementalDataBundle = ures_openDirect(NULL, gSupplementalData, &status);
790 UResourceBundle *zoneFormatting = ures_getByKey(supplementalDataBundle, gZoneFormattingTag, NULL, &status);
791 UResourceBundle *multizone = ures_getByKey(zoneFormatting, gMultizoneTag, NULL, &status);
792
793 if (U_SUCCESS(status)) {
794 while (ures_hasNext(multizone)) {
795 int32_t len;
796 const UChar* multizoneCountry = ures_getNextString(multizone, &len, NULL, &status);
797 if (country.compare(multizoneCountry, len) == 0) {
798 // Included in the multizone country list
799 country.remove();
800 break;
801 }
802 }
803 }
804
805 ures_close(multizone);
806 ures_close(zoneFormatting);
807 ures_close(supplementalDataBundle);
808 }
809
810 return country;
811 }
812
813 UnicodeString&
getMetazoneID(const UnicodeString & tzid,UDate date,UnicodeString & result)814 ZoneMeta::getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &result) {
815 UBool isSet = FALSE;
816 const UVector *mappings = getMetazoneMappings(tzid);
817 if (mappings != NULL) {
818 for (int32_t i = 0; i < mappings->size(); i++) {
819 OlsonToMetaMappingEntry *mzm = (OlsonToMetaMappingEntry*)mappings->elementAt(i);
820 if (mzm->from <= date && mzm->to > date) {
821 result.setTo(mzm->mzid, -1);
822 isSet = TRUE;
823 break;
824 }
825 }
826 }
827 if (!isSet) {
828 result.remove();
829 }
830 return result;
831 }
832
833 const UVector*
getMetazoneMappings(const UnicodeString & tzid)834 ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) {
835 initialize();
836 const UVector *result = NULL;
837 if (gOlsonToMeta != NULL) {
838 result = (UVector*)gOlsonToMeta->get(tzid);
839 }
840 return result;
841 }
842
843 UnicodeString&
getZoneIdByMetazone(const UnicodeString & mzid,const UnicodeString & region,UnicodeString & result)844 ZoneMeta::getZoneIdByMetazone(const UnicodeString &mzid, const UnicodeString ®ion, UnicodeString &result) {
845 initialize();
846 UBool isSet = FALSE;
847 if (gMetaToOlson != NULL) {
848 UVector *mappings = (UVector*)gMetaToOlson->get(mzid);
849 if (mappings != NULL) {
850 // Find a preferred time zone for the given region.
851 for (int32_t i = 0; i < mappings->size(); i++) {
852 MetaToOlsonMappingEntry *olsonmap = (MetaToOlsonMappingEntry*)mappings->elementAt(i);
853 if (region.compare(olsonmap->territory, -1) == 0) {
854 result.setTo(olsonmap->id);
855 isSet = TRUE;
856 break;
857 } else if (u_strcmp(olsonmap->territory, gWorld) == 0) {
858 result.setTo(olsonmap->id);
859 isSet = TRUE;
860 }
861 }
862 }
863 }
864 if (!isSet) {
865 result.remove();
866 }
867 return result;
868 }
869
870
871 U_NAMESPACE_END
872
873 #endif /* #if !UCONFIG_NO_FORMATTING */
874