• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 *******************************************************************************
3 * Copyright (C) 2007-2011, 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 #include "uassert.h"
25 #include "uresimp.h"
26 #include "uhash.h"
27 #include "olsontz.h"
28 
29 static UMTX gZoneMetaLock = NULL;
30 
31 // CLDR Canonical ID mapping table
32 static UHashtable *gCanonicalIDCache = NULL;
33 static UBool gCanonicalIDCacheInitialized = FALSE;
34 
35 // Metazone mapping table
36 static UHashtable *gOlsonToMeta = NULL;
37 static UBool gOlsonToMetaInitialized = FALSE;
38 
39 // Available metazone IDs vector and table
40 static U_NAMESPACE_QUALIFIER UVector *gMetaZoneIDs = NULL;
41 static UHashtable *gMetaZoneIDTable = NULL;
42 static UBool gMetaZoneIDsInitialized = FALSE;
43 
44 // Country info vectors
45 static U_NAMESPACE_QUALIFIER UVector *gSingleZoneCountries = NULL;
46 static U_NAMESPACE_QUALIFIER UVector *gMultiZonesCountries = NULL;
47 static UBool gCountryInfoVectorsInitialized = FALSE;
48 
49 U_CDECL_BEGIN
50 
51 /**
52  * Cleanup callback func
53  */
zoneMeta_cleanup(void)54 static UBool U_CALLCONV zoneMeta_cleanup(void)
55 {
56     umtx_destroy(&gZoneMetaLock);
57 
58     if (gCanonicalIDCache != NULL) {
59         uhash_close(gCanonicalIDCache);
60         gCanonicalIDCache = NULL;
61     }
62     gCanonicalIDCacheInitialized = FALSE;
63 
64     if (gOlsonToMeta != NULL) {
65         uhash_close(gOlsonToMeta);
66         gOlsonToMeta = NULL;
67     }
68     gOlsonToMetaInitialized = FALSE;
69 
70     if (gMetaZoneIDTable != NULL) {
71         uhash_close(gMetaZoneIDTable);
72     }
73     // delete after closing gMetaZoneIDTable, because it holds
74     // value objects held by the hashtable
75     delete gMetaZoneIDs;
76     gMetaZoneIDsInitialized = FALSE;
77 
78     delete gSingleZoneCountries;
79     delete gMultiZonesCountries;
80     gCountryInfoVectorsInitialized = FALSE;
81 
82     return TRUE;
83 }
84 
85 /**
86  * Deleter for UChar* string
87  */
88 static void U_CALLCONV
deleteUCharString(void * obj)89 deleteUCharString(void *obj) {
90     UChar *entry = (UChar*)obj;
91     uprv_free(entry);
92 }
93 
94 /**
95  * Deleter for UVector
96  */
97 static void U_CALLCONV
deleteUVector(void * obj)98 deleteUVector(void *obj) {
99    delete (U_NAMESPACE_QUALIFIER UVector*) obj;
100 }
101 
102 /**
103  * Deleter for OlsonToMetaMappingEntry
104  */
105 static void U_CALLCONV
deleteOlsonToMetaMappingEntry(void * obj)106 deleteOlsonToMetaMappingEntry(void *obj) {
107     U_NAMESPACE_QUALIFIER OlsonToMetaMappingEntry *entry = (U_NAMESPACE_QUALIFIER OlsonToMetaMappingEntry*)obj;
108     uprv_free(entry);
109 }
110 
111 U_CDECL_END
112 
113 U_NAMESPACE_BEGIN
114 
115 #define ZID_KEY_MAX 128
116 
117 static const char gMetaZones[]          = "metaZones";
118 static const char gMetazoneInfo[]       = "metazoneInfo";
119 static const char gMapTimezonesTag[]    = "mapTimezones";
120 
121 static const char gTimeZoneTypes[]      = "timezoneTypes";
122 static const char gTypeAliasTag[]       = "typeAlias";
123 static const char gTypeMapTag[]         = "typeMap";
124 static const char gTimezoneTag[]        = "timezone";
125 
126 static const char gWorldTag[]           = "001";
127 
128 static const UChar gWorld[] = {0x30, 0x30, 0x31, 0x00}; // "001"
129 
130 static const UChar gDefaultFrom[] = {0x31, 0x39, 0x37, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31,
131                                      0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x00}; // "1970-01-01 00:00"
132 static const UChar gDefaultTo[]   = {0x39, 0x39, 0x39, 0x39, 0x2D, 0x31, 0x32, 0x2D, 0x33, 0x31,
133                                      0x20, 0x32, 0x33, 0x3A, 0x35, 0x39, 0x00}; // "9999-12-31 23:59"
134 
135 #define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1)
136 
137 /*
138  * Convert a date string used by metazone mappings to UDate.
139  * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
140  */
141 static UDate
parseDate(const UChar * text,UErrorCode & status)142 parseDate (const UChar *text, UErrorCode &status) {
143     if (U_FAILURE(status)) {
144         return 0;
145     }
146     int32_t len = u_strlen(text);
147     if (len != 16 && len != 10) {
148         // It must be yyyy-MM-dd HH:mm (length 16) or yyyy-MM-dd (length 10)
149         status = U_INVALID_FORMAT_ERROR;
150         return 0;
151     }
152 
153     int32_t year = 0, month = 0, day = 0, hour = 0, min = 0, n;
154     int32_t idx;
155 
156     // "yyyy" (0 - 3)
157     for (idx = 0; idx <= 3 && U_SUCCESS(status); idx++) {
158         n = ASCII_DIGIT((int32_t)text[idx]);
159         if (n >= 0) {
160             year = 10*year + n;
161         } else {
162             status = U_INVALID_FORMAT_ERROR;
163         }
164     }
165     // "MM" (5 - 6)
166     for (idx = 5; idx <= 6 && U_SUCCESS(status); idx++) {
167         n = ASCII_DIGIT((int32_t)text[idx]);
168         if (n >= 0) {
169             month = 10*month + n;
170         } else {
171             status = U_INVALID_FORMAT_ERROR;
172         }
173     }
174     // "dd" (8 - 9)
175     for (idx = 8; idx <= 9 && U_SUCCESS(status); idx++) {
176         n = ASCII_DIGIT((int32_t)text[idx]);
177         if (n >= 0) {
178             day = 10*day + n;
179         } else {
180             status = U_INVALID_FORMAT_ERROR;
181         }
182     }
183     if (len == 16) {
184         // "HH" (11 - 12)
185         for (idx = 11; idx <= 12 && U_SUCCESS(status); idx++) {
186             n = ASCII_DIGIT((int32_t)text[idx]);
187             if (n >= 0) {
188                 hour = 10*hour + n;
189             } else {
190                 status = U_INVALID_FORMAT_ERROR;
191             }
192         }
193         // "mm" (14 - 15)
194         for (idx = 14; idx <= 15 && U_SUCCESS(status); idx++) {
195             n = ASCII_DIGIT((int32_t)text[idx]);
196             if (n >= 0) {
197                 min = 10*min + n;
198             } else {
199                 status = U_INVALID_FORMAT_ERROR;
200             }
201         }
202     }
203 
204     if (U_SUCCESS(status)) {
205         UDate date = Grego::fieldsToDay(year, month - 1, day) * U_MILLIS_PER_DAY
206             + hour * U_MILLIS_PER_HOUR + min * U_MILLIS_PER_MINUTE;
207         return date;
208     }
209     return 0;
210 }
211 
212 const UChar* U_EXPORT2
getCanonicalCLDRID(const UnicodeString & tzid,UErrorCode & status)213 ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UErrorCode& status) {
214     if (U_FAILURE(status)) {
215         return NULL;
216     }
217 
218     int32_t len = tzid.length();
219     if (len > ZID_KEY_MAX) {
220         status = U_ILLEGAL_ARGUMENT_ERROR;
221         return NULL;
222     }
223 
224     // Checking the cached results
225     UBool initialized;
226     UMTX_CHECK(&gZoneMetaLock, gCanonicalIDCacheInitialized, initialized);
227     if (!initialized) {
228         // Create empty hashtable
229         umtx_lock(&gZoneMetaLock);
230         {
231             if (!gCanonicalIDCacheInitialized) {
232                 gCanonicalIDCache = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
233                 if (gCanonicalIDCache == NULL) {
234                     status = U_MEMORY_ALLOCATION_ERROR;
235                 }
236                 if (U_FAILURE(status)) {
237                     gCanonicalIDCache = NULL;
238                     return NULL;
239                 }
240                 // No key/value deleters - keys/values are from a resource bundle
241                 gCanonicalIDCacheInitialized = TRUE;
242                 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
243             }
244         }
245         umtx_unlock(&gZoneMetaLock);
246     }
247 
248     const UChar *canonicalID = NULL;
249 
250     UErrorCode tmpStatus = U_ZERO_ERROR;
251     UChar utzid[ZID_KEY_MAX + 1];
252     tzid.extract(utzid, ZID_KEY_MAX + 1, tmpStatus);
253     U_ASSERT(tmpStatus == U_ZERO_ERROR);    // we checked the length of tzid already
254 
255     // Check if it was already cached
256     umtx_lock(&gZoneMetaLock);
257     {
258         canonicalID = (const UChar *)uhash_get(gCanonicalIDCache, utzid);
259     }
260     umtx_unlock(&gZoneMetaLock);
261 
262     if (canonicalID != NULL) {
263         return canonicalID;
264     }
265 
266     // If not, resolve CLDR canonical ID with resource data
267     UBool isInputCanonical = FALSE;
268     char id[ZID_KEY_MAX + 1];
269     const UChar* idChars = tzid.getBuffer();
270 
271     u_UCharsToChars(idChars,id,len);
272     id[len] = (char) 0; // Make sure it is null terminated.
273 
274     // replace '/' with ':'
275     char *p = id;
276     while (*p++) {
277         if (*p == '/') {
278             *p = ':';
279         }
280     }
281 
282     UResourceBundle *top = ures_openDirect(NULL, gTimeZoneTypes, &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;
291     }
292 
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;
302         }
303 
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;
309             } else {
310                 len = u_strlen(derefer);
311                 u_UCharsToChars(derefer,id,len);
312                 id[len] = (char) 0; // Make sure it is null terminated.
313 
314                 // replace '/' with ':'
315                 char *p = id;
316                 while (*p++) {
317                     if (*p == '/') {
318                         *p = ':';
319                     }
320                 }
321 
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
324                 // for it.
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;
330                 } else {
331                     canonicalID = derefer;
332                     isInputCanonical = TRUE;
333                 }
334             }
335         }
336     }
337     ures_close(rb);
338     ures_close(top);
339 
340     if (U_SUCCESS(status)) {
341         U_ASSERT(canonicalID != NULL);  // canocanilD must be non-NULL here
342 
343         // Put the resolved canonical ID to the cache
344         umtx_lock(&gZoneMetaLock);
345         {
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);
350                 if (key != NULL) {
351                     idInCache = (const UChar *)uhash_put(gCanonicalIDCache, (void *)key, (void *)canonicalID, &status);
352                     U_ASSERT(idInCache == NULL);
353                 }
354             }
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);
361                 }
362             }
363         }
364         umtx_unlock(&gZoneMetaLock);
365     }
366 
367     return canonicalID;
368 }
369 
370 UnicodeString& U_EXPORT2
getCanonicalCLDRID(const UnicodeString & tzid,UnicodeString & systemID,UErrorCode & status)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();
375         return systemID;
376     }
377     systemID.setTo(TRUE, canonicalID, -1);
378     return systemID;
379 }
380 
381 const UChar* U_EXPORT2
getCanonicalCLDRID(const TimeZone & tz)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();
387     }
388     UErrorCode status = U_ZERO_ERROR;
389     UnicodeString tzID;
390     return getCanonicalCLDRID(tz.getID(tzID), status);
391 }
392 
393 
394 
395 UnicodeString& U_EXPORT2
getCanonicalCountry(const UnicodeString & tzid,UnicodeString & canonicalCountry)396 ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &canonicalCountry) {
397     const UChar *region = TimeZone::getRegion(tzid);
398     if (region != NULL && u_strcmp(gWorld, region) != 0) {
399         canonicalCountry.setTo(region, -1);
400     } else {
401         canonicalCountry.setToBogus();
402     }
403     return canonicalCountry;
404 }
405 
406 UnicodeString& U_EXPORT2
getSingleCountry(const UnicodeString & tzid,UnicodeString & country)407 ZoneMeta::getSingleCountry(const UnicodeString &tzid, UnicodeString &country) {
408     // Get canonical country for the zone
409     const UChar *region = TimeZone::getRegion(tzid);
410     if (region == NULL || u_strcmp(gWorld, region) == 0) {
411         // special case - unknown or "001"
412         country.setToBogus();
413         return country;
414     }
415 
416     // Checking the cached results
417     UErrorCode status = U_ZERO_ERROR;
418     UBool initialized;
419     UMTX_CHECK(&gZoneMetaLock, gCountryInfoVectorsInitialized, initialized);
420     if (!initialized) {
421         // Create empty vectors
422         umtx_lock(&gZoneMetaLock);
423         {
424             if (!gCountryInfoVectorsInitialized) {
425                 // No deleters for these UVectors, it's a reference to a resource bundle string.
426                 gSingleZoneCountries = new UVector(NULL, uhash_compareUChars, status);
427                 if (gSingleZoneCountries == NULL) {
428                     status = U_MEMORY_ALLOCATION_ERROR;
429                 }
430                 gMultiZonesCountries = new UVector(NULL, uhash_compareUChars, status);
431                 if (gMultiZonesCountries == NULL) {
432                     status = U_MEMORY_ALLOCATION_ERROR;
433                 }
434 
435                 if (U_SUCCESS(status)) {
436                     gCountryInfoVectorsInitialized = TRUE;
437                 } else {
438                     delete gSingleZoneCountries;
439                     delete gMultiZonesCountries;
440                 }
441             }
442         }
443         umtx_unlock(&gZoneMetaLock);
444 
445         if (U_FAILURE(status)) {
446             country.setToBogus();
447             return country;
448         }
449     }
450 
451     // Check if it was already cached
452     UBool cached = FALSE;
453     UBool multiZones = FALSE;
454     umtx_lock(&gZoneMetaLock);
455     {
456         multiZones = cached = gMultiZonesCountries->contains((void*)region);
457         if (!multiZones) {
458             cached = gSingleZoneCountries->contains((void*)region);
459         }
460     }
461     umtx_unlock(&gZoneMetaLock);
462 
463     if (!cached) {
464         // We need to go through all zones associated with the region.
465         // This is relatively heavy operation.
466 
467         U_ASSERT(u_strlen(region) == 2);
468 
469         char buf[] = {0, 0, 0};
470         u_UCharsToChars(region, buf, 2);
471 
472         StringEnumeration *ids = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, buf, NULL, status);
473         int32_t idsLen = ids->count(status);
474         if (U_SUCCESS(status) && idsLen > 1) {
475             // multiple canonical zones are available for the region
476             multiZones = TRUE;
477         }
478         if (U_FAILURE(status)) {
479             // no single country by default for any error cases
480             multiZones = TRUE;
481         }
482         delete ids;
483 
484         // Cache the result
485         umtx_lock(&gZoneMetaLock);
486         {
487             UErrorCode ec = U_ZERO_ERROR;
488             if (multiZones) {
489                 if (!gMultiZonesCountries->contains((void*)region)) {
490                     gMultiZonesCountries->addElement((void*)region, ec);
491                 }
492             } else {
493                 if (!gSingleZoneCountries->contains((void*)region)) {
494                     gSingleZoneCountries->addElement((void*)region, ec);
495                 }
496             }
497         }
498         umtx_unlock(&gZoneMetaLock);
499     }
500 
501     if (multiZones) {
502         country.setToBogus();
503     } else {
504         country.setTo(region, -1);
505     }
506     return country;
507 }
508 
509 UnicodeString& U_EXPORT2
getMetazoneID(const UnicodeString & tzid,UDate date,UnicodeString & result)510 ZoneMeta::getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &result) {
511     UBool isSet = FALSE;
512     const UVector *mappings = getMetazoneMappings(tzid);
513     if (mappings != NULL) {
514         for (int32_t i = 0; i < mappings->size(); i++) {
515             OlsonToMetaMappingEntry *mzm = (OlsonToMetaMappingEntry*)mappings->elementAt(i);
516             if (mzm->from <= date && mzm->to > date) {
517                 result.setTo(mzm->mzid, -1);
518                 isSet = TRUE;
519                 break;
520             }
521         }
522     }
523     if (!isSet) {
524         result.setToBogus();
525     }
526     return result;
527 }
528 
529 const UVector* U_EXPORT2
getMetazoneMappings(const UnicodeString & tzid)530 ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) {
531     UErrorCode status = U_ZERO_ERROR;
532     UChar tzidUChars[ZID_KEY_MAX + 1];
533     tzid.extract(tzidUChars, ZID_KEY_MAX + 1, status);
534     if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) {
535         return NULL;
536     }
537 
538     UBool initialized;
539     UMTX_CHECK(&gZoneMetaLock, gOlsonToMetaInitialized, initialized);
540     if (!initialized) {
541         UHashtable *tmpOlsonToMeta = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
542         if (U_FAILURE(status)) {
543             return NULL;
544         }
545         uhash_setKeyDeleter(tmpOlsonToMeta, deleteUCharString);
546         uhash_setValueDeleter(tmpOlsonToMeta, deleteUVector);
547 
548         umtx_lock(&gZoneMetaLock);
549         {
550             if (!gOlsonToMetaInitialized) {
551                 gOlsonToMeta = tmpOlsonToMeta;
552                 tmpOlsonToMeta = NULL;
553                 gOlsonToMetaInitialized = TRUE;
554             }
555         }
556         umtx_unlock(&gZoneMetaLock);
557 
558         // OK to call the following multiple times with the same function
559         ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
560         if (tmpOlsonToMeta != NULL) {
561             uhash_close(tmpOlsonToMeta);
562         }
563     }
564 
565     // get the mapping from cache
566     const UVector *result = NULL;
567 
568     umtx_lock(&gZoneMetaLock);
569     {
570         result = (UVector*) uhash_get(gOlsonToMeta, tzidUChars);
571     }
572     umtx_unlock(&gZoneMetaLock);
573 
574     if (result != NULL) {
575         return result;
576     }
577 
578     // miss the cache - create new one
579     UVector *tmpResult = createMetazoneMappings(tzid);
580     if (tmpResult == NULL) {
581         // not available
582         return NULL;
583     }
584 
585     // put the new one into the cache
586     umtx_lock(&gZoneMetaLock);
587     {
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));
594             if (key == NULL) {
595                 // memory allocation error..  just return NULL
596                 result = NULL;
597                 delete tmpResult;
598             } else {
599                 tzid.extract(key, tzidLen, status);
600                 uhash_put(gOlsonToMeta, key, tmpResult, &status);
601                 if (U_FAILURE(status)) {
602                     // delete the mapping
603                     result = NULL;
604                     delete tmpResult;
605                 } else {
606                     result = tmpResult;
607                 }
608             }
609         } else {
610             // another thread already put the one
611             delete tmpResult;
612         }
613     }
614     umtx_unlock(&gZoneMetaLock);
615 
616     return result;
617 }
618 
619 UVector*
createMetazoneMappings(const UnicodeString & tzid)620 ZoneMeta::createMetazoneMappings(const UnicodeString &tzid) {
621     UVector *mzMappings = NULL;
622     UErrorCode status = U_ZERO_ERROR;
623 
624     UnicodeString canonicalID;
625     UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
626     ures_getByKey(rb, gMetazoneInfo, rb, &status);
627     getCanonicalCLDRID(tzid, canonicalID, status);
628 
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);
632         tzKey[tzKeyLen] = 0;
633 
634         // tzid keys are using ':' as separators
635         char *p = tzKey;
636         while (*p) {
637             if (*p == '/') {
638                 *p = ':';
639             }
640             p++;
641         }
642 
643         ures_getByKey(rb, tzKey, rb, &status);
644 
645         if (U_SUCCESS(status)) {
646             UResourceBundle *mz = NULL;
647             while (ures_hasNext(rb)) {
648                 mz = ures_getNextResource(rb, mz, &status);
649 
650                 const UChar *mz_name = ures_getStringByIndex(mz, 0, NULL, &status);
651                 const UChar *mz_from = gDefaultFrom;
652                 const UChar *mz_to = gDefaultTo;
653 
654                 if (ures_getSize(mz) == 3) {
655                     mz_from = ures_getStringByIndex(mz, 1, NULL, &status);
656                     mz_to   = ures_getStringByIndex(mz, 2, NULL, &status);
657                 }
658 
659                 if(U_FAILURE(status)){
660                     status = U_ZERO_ERROR;
661                     continue;
662                 }
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;
670                     continue;
671                 }
672 
673                 OlsonToMetaMappingEntry *entry = (OlsonToMetaMappingEntry*)uprv_malloc(sizeof(OlsonToMetaMappingEntry));
674                 if (entry == NULL) {
675                     status = U_MEMORY_ALLOCATION_ERROR;
676                     break;
677                 }
678                 entry->mzid = mz_name;
679                 entry->from = from;
680                 entry->to = to;
681 
682                 if (mzMappings == NULL) {
683                     mzMappings = new UVector(deleteOlsonToMetaMappingEntry, NULL, status);
684                     if (U_FAILURE(status)) {
685                         delete mzMappings;
686                         deleteOlsonToMetaMappingEntry(entry);
687                         uprv_free(entry);
688                         break;
689                     }
690                 }
691 
692                 mzMappings->addElement(entry, status);
693                 if (U_FAILURE(status)) {
694                     break;
695                 }
696             }
697             ures_close(mz);
698             if (U_FAILURE(status)) {
699                 if (mzMappings != NULL) {
700                     delete mzMappings;
701                     mzMappings = NULL;
702                 }
703             }
704         }
705     }
706     ures_close(rb);
707     return mzMappings;
708 }
709 
710 UnicodeString& U_EXPORT2
getZoneIdByMetazone(const UnicodeString & mzid,const UnicodeString & region,UnicodeString & result)711 ZoneMeta::getZoneIdByMetazone(const UnicodeString &mzid, const UnicodeString &region, UnicodeString &result) {
712     UErrorCode status = U_ZERO_ERROR;
713     const UChar *tzid = NULL;
714     int32_t tzidLen = 0;
715     char keyBuf[ZID_KEY_MAX + 1];
716     int32_t keyLen = 0;
717 
718     if (mzid.length() > ZID_KEY_MAX) {
719         result.setToBogus();
720         return result;
721     }
722 
723     keyLen = mzid.extract(0, mzid.length(), keyBuf, ZID_KEY_MAX + 1, US_INV);
724     keyBuf[keyLen] = 0;
725 
726     UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
727     ures_getByKey(rb, gMapTimezonesTag, rb, &status);
728     ures_getByKey(rb, keyBuf, rb, &status);
729 
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);
734             keyBuf[keyLen] = 0;
735             tzid = ures_getStringByKey(rb, keyBuf, &tzidLen, &status);
736             if (status == U_MISSING_RESOURCE_ERROR) {
737                 status = U_ZERO_ERROR;
738             }
739         }
740         if (U_SUCCESS(status) && tzid == NULL) {
741             // try "001"
742             tzid = ures_getStringByKey(rb, gWorldTag, &tzidLen, &status);
743         }
744     }
745     ures_close(rb);
746 
747     if (tzid == NULL) {
748         result.setToBogus();
749     } else {
750         result.setTo(tzid, tzidLen);
751     }
752 
753     return result;
754 }
755 
756 void
initAvailableMetaZoneIDs()757 ZoneMeta::initAvailableMetaZoneIDs () {
758     UBool initialized;
759     UMTX_CHECK(&gZoneMetaLock, gMetaZoneIDsInitialized, initialized);
760     if (!initialized) {
761         umtx_lock(&gZoneMetaLock);
762         {
763             if (!gMetaZoneIDsInitialized) {
764                 UErrorCode status = U_ZERO_ERROR;
765                 UHashtable *metaZoneIDTable = uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, NULL, &status);
766                 uhash_setKeyDeleter(metaZoneIDTable, uhash_deleteUnicodeString);
767                 // No valueDeleter, because the vector maintain the value objects
768                 UVector *metaZoneIDs = NULL;
769                 if (U_SUCCESS(status)) {
770                     metaZoneIDs = new UVector(NULL, uhash_compareUChars, status);
771                     if (metaZoneIDs == NULL) {
772                         status = U_MEMORY_ALLOCATION_ERROR;
773                     }
774                 } else {
775                     uhash_close(metaZoneIDTable);
776                 }
777                 if (U_SUCCESS(status)) {
778                     metaZoneIDs->setDeleter(uhash_freeBlock);
779 
780                     UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
781                     UResourceBundle *bundle = ures_getByKey(rb, gMapTimezonesTag, NULL, &status);
782                     UResourceBundle res;
783                     ures_initStackObject(&res);
784                     while (U_SUCCESS(status) && ures_hasNext(bundle)) {
785                         ures_getNextResource(bundle, &res, &status);
786                         if (U_FAILURE(status)) {
787                             break;
788                         }
789                         const char *mzID = ures_getKey(&res);
790                         int32_t len = uprv_strlen(mzID);
791                         UChar *uMzID = (UChar*)uprv_malloc(sizeof(UChar) * (len + 1));
792                         if (uMzID == NULL) {
793                             status = U_MEMORY_ALLOCATION_ERROR;
794                             break;
795                         }
796                         u_charsToUChars(mzID, uMzID, len);
797                         uMzID[len] = 0;
798                         UnicodeString *usMzID = new UnicodeString(uMzID);
799                         if (uhash_get(metaZoneIDTable, usMzID) == NULL) {
800                             metaZoneIDs->addElement((void *)uMzID, status);
801                             uhash_put(metaZoneIDTable, (void *)usMzID, (void *)uMzID, &status);
802                         } else {
803                             uprv_free(uMzID);
804                             delete usMzID;
805                         }
806                     }
807                     if (U_SUCCESS(status)) {
808                         gMetaZoneIDs = metaZoneIDs;
809                         gMetaZoneIDTable = metaZoneIDTable;
810                         gMetaZoneIDsInitialized = TRUE;
811                     } else {
812                         uhash_close(metaZoneIDTable);
813                         delete metaZoneIDs;
814                     }
815                     ures_close(&res);
816                     ures_close(bundle);
817                     ures_close(rb);
818                 }
819             }
820         }
821         umtx_unlock(&gZoneMetaLock);
822     }
823 }
824 
825 const UVector*
getAvailableMetazoneIDs()826 ZoneMeta::getAvailableMetazoneIDs() {
827     initAvailableMetaZoneIDs();
828     return gMetaZoneIDs;
829 }
830 
831 const UChar*
findMetaZoneID(const UnicodeString & mzid)832 ZoneMeta::findMetaZoneID(const UnicodeString& mzid) {
833     initAvailableMetaZoneIDs();
834     return (const UChar*)uhash_get(gMetaZoneIDTable, &mzid);
835 }
836 
837 const UChar*
findTimeZoneID(const UnicodeString & tzid)838 ZoneMeta::findTimeZoneID(const UnicodeString& tzid) {
839     return TimeZone::findID(tzid);
840 }
841 
842 U_NAMESPACE_END
843 
844 #endif /* #if !UCONFIG_NO_FORMATTING */
845