• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &region, 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