• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 *******************************************************************************
3 * Copyright (C) 2007-2014, 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 #include "unicode/simpletz.h"
18 
19 #include "umutex.h"
20 #include "uvector.h"
21 #include "cmemory.h"
22 #include "gregoimp.h"
23 #include "cstring.h"
24 #include "ucln_in.h"
25 #include "uassert.h"
26 #include "uresimp.h"
27 #include "uhash.h"
28 #include "olsontz.h"
29 
30 static UMutex gZoneMetaLock = U_MUTEX_INITIALIZER;
31 
32 // CLDR Canonical ID mapping table
33 static UHashtable *gCanonicalIDCache = NULL;
34 static icu::UInitOnce gCanonicalIDCacheInitOnce = U_INITONCE_INITIALIZER;
35 
36 // Metazone mapping table
37 static UHashtable *gOlsonToMeta = NULL;
38 static icu::UInitOnce gOlsonToMetaInitOnce = U_INITONCE_INITIALIZER;
39 
40 // Available metazone IDs vector and table
41 static icu::UVector *gMetaZoneIDs = NULL;
42 static UHashtable *gMetaZoneIDTable = NULL;
43 static icu::UInitOnce gMetaZoneIDsInitOnce = U_INITONCE_INITIALIZER;
44 
45 // Country info vectors
46 static icu::UVector *gSingleZoneCountries = NULL;
47 static icu::UVector *gMultiZonesCountries = NULL;
48 static icu::UInitOnce gCountryInfoVectorsInitOnce = U_INITONCE_INITIALIZER;
49 
50 U_CDECL_BEGIN
51 
52 /**
53  * Cleanup callback func
54  */
zoneMeta_cleanup(void)55 static UBool U_CALLCONV zoneMeta_cleanup(void)
56 {
57     if (gCanonicalIDCache != NULL) {
58         uhash_close(gCanonicalIDCache);
59         gCanonicalIDCache = NULL;
60     }
61     gCanonicalIDCacheInitOnce.reset();
62 
63     if (gOlsonToMeta != NULL) {
64         uhash_close(gOlsonToMeta);
65         gOlsonToMeta = NULL;
66     }
67     gOlsonToMetaInitOnce.reset();
68 
69     if (gMetaZoneIDTable != NULL) {
70         uhash_close(gMetaZoneIDTable);
71         gMetaZoneIDTable = NULL;
72     }
73     // delete after closing gMetaZoneIDTable, because it holds
74     // value objects held by the hashtable
75     delete gMetaZoneIDs;
76     gMetaZoneIDs = NULL;
77     gMetaZoneIDsInitOnce.reset();
78 
79     delete gSingleZoneCountries;
80     gSingleZoneCountries = NULL;
81     delete gMultiZonesCountries;
82     gMultiZonesCountries = NULL;
83     gCountryInfoVectorsInitOnce.reset();
84 
85     return TRUE;
86 }
87 
88 /**
89  * Deleter for UChar* string
90  */
91 static void U_CALLCONV
deleteUCharString(void * obj)92 deleteUCharString(void *obj) {
93     UChar *entry = (UChar*)obj;
94     uprv_free(entry);
95 }
96 
97 /**
98  * Deleter for UVector
99  */
100 static void U_CALLCONV
deleteUVector(void * obj)101 deleteUVector(void *obj) {
102    delete (icu::UVector*) obj;
103 }
104 
105 /**
106  * Deleter for OlsonToMetaMappingEntry
107  */
108 static void U_CALLCONV
deleteOlsonToMetaMappingEntry(void * obj)109 deleteOlsonToMetaMappingEntry(void *obj) {
110     icu::OlsonToMetaMappingEntry *entry = (icu::OlsonToMetaMappingEntry*)obj;
111     uprv_free(entry);
112 }
113 
114 U_CDECL_END
115 
116 U_NAMESPACE_BEGIN
117 
118 #define ZID_KEY_MAX 128
119 
120 static const char gMetaZones[]          = "metaZones";
121 static const char gMetazoneInfo[]       = "metazoneInfo";
122 static const char gMapTimezonesTag[]    = "mapTimezones";
123 
124 static const char gKeyTypeData[]        = "keyTypeData";
125 static const char gTypeAliasTag[]       = "typeAlias";
126 static const char gTypeMapTag[]         = "typeMap";
127 static const char gTimezoneTag[]        = "timezone";
128 
129 static const char gPrimaryZonesTag[]    = "primaryZones";
130 
131 static const char gWorldTag[]           = "001";
132 
133 static const UChar gWorld[] = {0x30, 0x30, 0x31, 0x00}; // "001"
134 
135 static const UChar gDefaultFrom[] = {0x31, 0x39, 0x37, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31,
136                                      0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x00}; // "1970-01-01 00:00"
137 static const UChar gDefaultTo[]   = {0x39, 0x39, 0x39, 0x39, 0x2D, 0x31, 0x32, 0x2D, 0x33, 0x31,
138                                      0x20, 0x32, 0x33, 0x3A, 0x35, 0x39, 0x00}; // "9999-12-31 23:59"
139 
140 static const UChar gCustomTzPrefix[]    = {0x47, 0x4D, 0x54, 0};    // "GMT"
141 
142 #define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1)
143 
144 /*
145  * Convert a date string used by metazone mappings to UDate.
146  * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
147  */
148 static UDate
parseDate(const UChar * text,UErrorCode & status)149 parseDate (const UChar *text, UErrorCode &status) {
150     if (U_FAILURE(status)) {
151         return 0;
152     }
153     int32_t len = u_strlen(text);
154     if (len != 16 && len != 10) {
155         // It must be yyyy-MM-dd HH:mm (length 16) or yyyy-MM-dd (length 10)
156         status = U_INVALID_FORMAT_ERROR;
157         return 0;
158     }
159 
160     int32_t year = 0, month = 0, day = 0, hour = 0, min = 0, n;
161     int32_t idx;
162 
163     // "yyyy" (0 - 3)
164     for (idx = 0; idx <= 3 && U_SUCCESS(status); idx++) {
165         n = ASCII_DIGIT((int32_t)text[idx]);
166         if (n >= 0) {
167             year = 10*year + n;
168         } else {
169             status = U_INVALID_FORMAT_ERROR;
170         }
171     }
172     // "MM" (5 - 6)
173     for (idx = 5; idx <= 6 && U_SUCCESS(status); idx++) {
174         n = ASCII_DIGIT((int32_t)text[idx]);
175         if (n >= 0) {
176             month = 10*month + n;
177         } else {
178             status = U_INVALID_FORMAT_ERROR;
179         }
180     }
181     // "dd" (8 - 9)
182     for (idx = 8; idx <= 9 && U_SUCCESS(status); idx++) {
183         n = ASCII_DIGIT((int32_t)text[idx]);
184         if (n >= 0) {
185             day = 10*day + n;
186         } else {
187             status = U_INVALID_FORMAT_ERROR;
188         }
189     }
190     if (len == 16) {
191         // "HH" (11 - 12)
192         for (idx = 11; idx <= 12 && U_SUCCESS(status); idx++) {
193             n = ASCII_DIGIT((int32_t)text[idx]);
194             if (n >= 0) {
195                 hour = 10*hour + n;
196             } else {
197                 status = U_INVALID_FORMAT_ERROR;
198             }
199         }
200         // "mm" (14 - 15)
201         for (idx = 14; idx <= 15 && U_SUCCESS(status); idx++) {
202             n = ASCII_DIGIT((int32_t)text[idx]);
203             if (n >= 0) {
204                 min = 10*min + n;
205             } else {
206                 status = U_INVALID_FORMAT_ERROR;
207             }
208         }
209     }
210 
211     if (U_SUCCESS(status)) {
212         UDate date = Grego::fieldsToDay(year, month - 1, day) * U_MILLIS_PER_DAY
213             + hour * U_MILLIS_PER_HOUR + min * U_MILLIS_PER_MINUTE;
214         return date;
215     }
216     return 0;
217 }
218 
initCanonicalIDCache(UErrorCode & status)219 static void U_CALLCONV initCanonicalIDCache(UErrorCode &status) {
220     gCanonicalIDCache = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
221     if (gCanonicalIDCache == NULL) {
222         status = U_MEMORY_ALLOCATION_ERROR;
223     }
224     if (U_FAILURE(status)) {
225         gCanonicalIDCache = NULL;
226     }
227     // No key/value deleters - keys/values are from a resource bundle
228     ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
229 }
230 
231 
232 const UChar* U_EXPORT2
getCanonicalCLDRID(const UnicodeString & tzid,UErrorCode & status)233 ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UErrorCode& status) {
234     if (U_FAILURE(status)) {
235         return NULL;
236     }
237 
238     if (tzid.isBogus() || tzid.length() > ZID_KEY_MAX) {
239         status = U_ILLEGAL_ARGUMENT_ERROR;
240         return NULL;
241     }
242 
243     // Checking the cached results
244     umtx_initOnce(gCanonicalIDCacheInitOnce, &initCanonicalIDCache, status);
245     if (U_FAILURE(status)) {
246         return NULL;
247     }
248 
249     const UChar *canonicalID = NULL;
250 
251     UErrorCode tmpStatus = U_ZERO_ERROR;
252     UChar utzid[ZID_KEY_MAX + 1];
253     tzid.extract(utzid, ZID_KEY_MAX + 1, tmpStatus);
254     U_ASSERT(tmpStatus == U_ZERO_ERROR);    // we checked the length of tzid already
255 
256     // Check if it was already cached
257     umtx_lock(&gZoneMetaLock);
258     {
259         canonicalID = (const UChar *)uhash_get(gCanonicalIDCache, utzid);
260     }
261     umtx_unlock(&gZoneMetaLock);
262 
263     if (canonicalID != NULL) {
264         return canonicalID;
265     }
266 
267     // If not, resolve CLDR canonical ID with resource data
268     UBool isInputCanonical = FALSE;
269     char id[ZID_KEY_MAX + 1];
270     tzid.extract(0, 0x7fffffff, id, UPRV_LENGTHOF(id), US_INV);
271 
272     // replace '/' with ':'
273     char *p = id;
274     while (*p++) {
275         if (*p == '/') {
276             *p = ':';
277         }
278     }
279 
280     UResourceBundle *top = ures_openDirect(NULL, gKeyTypeData, &tmpStatus);
281     UResourceBundle *rb = ures_getByKey(top, gTypeMapTag, NULL, &tmpStatus);
282     ures_getByKey(rb, gTimezoneTag, rb, &tmpStatus);
283     ures_getByKey(rb, id, rb, &tmpStatus);
284     if (U_SUCCESS(tmpStatus)) {
285         // type entry (canonical) found
286         // the input is the canonical ID. resolve to const UChar*
287         canonicalID = TimeZone::findID(tzid);
288         isInputCanonical = TRUE;
289     }
290 
291     if (canonicalID == NULL) {
292         // If a map element not found, then look for an alias
293         tmpStatus = U_ZERO_ERROR;
294         ures_getByKey(top, gTypeAliasTag, rb, &tmpStatus);
295         ures_getByKey(rb, gTimezoneTag, rb, &tmpStatus);
296         const UChar *canonical = ures_getStringByKey(rb,id,NULL,&tmpStatus);
297         if (U_SUCCESS(tmpStatus)) {
298             // canonical map found
299             canonicalID = canonical;
300         }
301 
302         if (canonicalID == NULL) {
303             // Dereference the input ID using the tz data
304             const UChar *derefer = TimeZone::dereferOlsonLink(tzid);
305             if (derefer == NULL) {
306                 status = U_ILLEGAL_ARGUMENT_ERROR;
307             } else {
308                 int32_t len = u_strlen(derefer);
309                 u_UCharsToChars(derefer,id,len);
310                 id[len] = (char) 0; // Make sure it is null terminated.
311 
312                 // replace '/' with ':'
313                 char *p = id;
314                 while (*p++) {
315                     if (*p == '/') {
316                         *p = ':';
317                     }
318                 }
319 
320                 // If a dereference turned something up then look for an alias.
321                 // rb still points to the alias table, so we don't have to go looking
322                 // for it.
323                 tmpStatus = U_ZERO_ERROR;
324                 canonical = ures_getStringByKey(rb,id,NULL,&tmpStatus);
325                 if (U_SUCCESS(tmpStatus)) {
326                     // canonical map for the dereferenced ID found
327                     canonicalID = canonical;
328                 } else {
329                     canonicalID = derefer;
330                     isInputCanonical = TRUE;
331                 }
332             }
333         }
334     }
335     ures_close(rb);
336     ures_close(top);
337 
338     if (U_SUCCESS(status)) {
339         U_ASSERT(canonicalID != NULL);  // canocanilD must be non-NULL here
340 
341         // Put the resolved canonical ID to the cache
342         umtx_lock(&gZoneMetaLock);
343         {
344             const UChar* idInCache = (const UChar *)uhash_get(gCanonicalIDCache, utzid);
345             if (idInCache == NULL) {
346                 const UChar* key = ZoneMeta::findTimeZoneID(tzid);
347                 U_ASSERT(key != NULL);
348                 if (key != NULL) {
349                     idInCache = (const UChar *)uhash_put(gCanonicalIDCache, (void *)key, (void *)canonicalID, &status);
350                     U_ASSERT(idInCache == NULL);
351                 }
352             }
353             if (U_SUCCESS(status) && isInputCanonical) {
354                 // Also put canonical ID itself into the cache if not exist
355                 const UChar *canonicalInCache = (const UChar*)uhash_get(gCanonicalIDCache, canonicalID);
356                 if (canonicalInCache == NULL) {
357                     canonicalInCache = (const UChar *)uhash_put(gCanonicalIDCache, (void *)canonicalID, (void *)canonicalID, &status);
358                     U_ASSERT(canonicalInCache == NULL);
359                 }
360             }
361         }
362         umtx_unlock(&gZoneMetaLock);
363     }
364 
365     return canonicalID;
366 }
367 
368 UnicodeString& U_EXPORT2
getCanonicalCLDRID(const UnicodeString & tzid,UnicodeString & systemID,UErrorCode & status)369 ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UnicodeString &systemID, UErrorCode& status) {
370     const UChar *canonicalID = getCanonicalCLDRID(tzid, status);
371     if (U_FAILURE(status) || canonicalID == NULL) {
372         systemID.setToBogus();
373         return systemID;
374     }
375     systemID.setTo(TRUE, canonicalID, -1);
376     return systemID;
377 }
378 
379 const UChar* U_EXPORT2
getCanonicalCLDRID(const TimeZone & tz)380 ZoneMeta::getCanonicalCLDRID(const TimeZone& tz) {
381     if (dynamic_cast<const OlsonTimeZone *>(&tz) != NULL) {
382         // short cut for OlsonTimeZone
383         const OlsonTimeZone *otz = (const OlsonTimeZone*)&tz;
384         return otz->getCanonicalID();
385     }
386     UErrorCode status = U_ZERO_ERROR;
387     UnicodeString tzID;
388     return getCanonicalCLDRID(tz.getID(tzID), status);
389 }
390 
countryInfoVectorsInit(UErrorCode & status)391 static void U_CALLCONV countryInfoVectorsInit(UErrorCode &status) {
392     // Create empty vectors
393     // No deleters for these UVectors, it's a reference to a resource bundle string.
394     gSingleZoneCountries = new UVector(NULL, uhash_compareUChars, status);
395     if (gSingleZoneCountries == NULL) {
396         status = U_MEMORY_ALLOCATION_ERROR;
397     }
398     gMultiZonesCountries = new UVector(NULL, uhash_compareUChars, status);
399     if (gMultiZonesCountries == NULL) {
400         status = U_MEMORY_ALLOCATION_ERROR;
401     }
402 
403     if (U_FAILURE(status)) {
404         delete gSingleZoneCountries;
405         delete gMultiZonesCountries;
406         gSingleZoneCountries = NULL;
407         gMultiZonesCountries  = NULL;
408     }
409     ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
410 }
411 
412 
413 UnicodeString& U_EXPORT2
getCanonicalCountry(const UnicodeString & tzid,UnicodeString & country,UBool * isPrimary)414 ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &country, UBool *isPrimary /* = NULL */) {
415     if (isPrimary != NULL) {
416         *isPrimary = FALSE;
417     }
418 
419     const UChar *region = TimeZone::getRegion(tzid);
420     if (region != NULL && u_strcmp(gWorld, region) != 0) {
421         country.setTo(region, -1);
422     } else {
423         country.setToBogus();
424         return country;
425     }
426 
427     if (isPrimary != NULL) {
428         char regionBuf[] = {0, 0, 0};
429 
430         // Checking the cached results
431         UErrorCode status = U_ZERO_ERROR;
432         umtx_initOnce(gCountryInfoVectorsInitOnce, &countryInfoVectorsInit, status);
433         if (U_FAILURE(status)) {
434             return country;
435         }
436 
437         // Check if it was already cached
438         UBool cached = FALSE;
439         UBool singleZone = FALSE;
440         umtx_lock(&gZoneMetaLock);
441         {
442             singleZone = cached = gSingleZoneCountries->contains((void*)region);
443             if (!cached) {
444                 cached = gMultiZonesCountries->contains((void*)region);
445             }
446         }
447         umtx_unlock(&gZoneMetaLock);
448 
449         if (!cached) {
450             // We need to go through all zones associated with the region.
451             // This is relatively heavy operation.
452 
453             U_ASSERT(u_strlen(region) == 2);
454 
455             u_UCharsToChars(region, regionBuf, 2);
456 
457             StringEnumeration *ids = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, regionBuf, NULL, status);
458             int32_t idsLen = ids->count(status);
459             if (U_SUCCESS(status) && idsLen == 1) {
460                 // only the single zone is available for the region
461                 singleZone = TRUE;
462             }
463             delete ids;
464 
465             // Cache the result
466             umtx_lock(&gZoneMetaLock);
467             {
468                 UErrorCode ec = U_ZERO_ERROR;
469                 if (singleZone) {
470                     if (!gSingleZoneCountries->contains((void*)region)) {
471                         gSingleZoneCountries->addElement((void*)region, ec);
472                     }
473                 } else {
474                     if (!gMultiZonesCountries->contains((void*)region)) {
475                         gMultiZonesCountries->addElement((void*)region, ec);
476                     }
477                 }
478             }
479             umtx_unlock(&gZoneMetaLock);
480         }
481 
482         if (singleZone) {
483             *isPrimary = TRUE;
484         } else {
485             // Note: We may cache the primary zone map in future.
486 
487             // Even a country has multiple zones, one of them might be
488             // dominant and treated as a primary zone
489             int32_t idLen = 0;
490             if (regionBuf[0] == 0) {
491                 u_UCharsToChars(region, regionBuf, 2);
492             }
493 
494             UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
495             ures_getByKey(rb, gPrimaryZonesTag, rb, &status);
496             const UChar *primaryZone = ures_getStringByKey(rb, regionBuf, &idLen, &status);
497             if (U_SUCCESS(status)) {
498                 if (tzid.compare(primaryZone, idLen) == 0) {
499                     *isPrimary = TRUE;
500                 } else {
501                     // The given ID might not be a canonical ID
502                     UnicodeString canonicalID;
503                     TimeZone::getCanonicalID(tzid, canonicalID, status);
504                     if (U_SUCCESS(status) && canonicalID.compare(primaryZone, idLen) == 0) {
505                         *isPrimary = TRUE;
506                     }
507                 }
508             }
509             ures_close(rb);
510         }
511     }
512 
513     return country;
514 }
515 
516 UnicodeString& U_EXPORT2
getMetazoneID(const UnicodeString & tzid,UDate date,UnicodeString & result)517 ZoneMeta::getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &result) {
518     UBool isSet = FALSE;
519     const UVector *mappings = getMetazoneMappings(tzid);
520     if (mappings != NULL) {
521         for (int32_t i = 0; i < mappings->size(); i++) {
522             OlsonToMetaMappingEntry *mzm = (OlsonToMetaMappingEntry*)mappings->elementAt(i);
523             if (mzm->from <= date && mzm->to > date) {
524                 result.setTo(mzm->mzid, -1);
525                 isSet = TRUE;
526                 break;
527             }
528         }
529     }
530     if (!isSet) {
531         result.setToBogus();
532     }
533     return result;
534 }
535 
olsonToMetaInit(UErrorCode & status)536 static void U_CALLCONV olsonToMetaInit(UErrorCode &status) {
537     U_ASSERT(gOlsonToMeta == NULL);
538     ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
539     gOlsonToMeta = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
540     if (U_FAILURE(status)) {
541         gOlsonToMeta = NULL;
542     } else {
543         uhash_setKeyDeleter(gOlsonToMeta, deleteUCharString);
544         uhash_setValueDeleter(gOlsonToMeta, deleteUVector);
545     }
546 }
547 
548 
549 const UVector* U_EXPORT2
getMetazoneMappings(const UnicodeString & tzid)550 ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) {
551     UErrorCode status = U_ZERO_ERROR;
552     UChar tzidUChars[ZID_KEY_MAX + 1];
553     tzid.extract(tzidUChars, ZID_KEY_MAX + 1, status);
554     if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) {
555         return NULL;
556     }
557 
558     umtx_initOnce(gOlsonToMetaInitOnce, &olsonToMetaInit, status);
559     if (U_FAILURE(status)) {
560         return NULL;
561     }
562 
563     // get the mapping from cache
564     const UVector *result = NULL;
565 
566     umtx_lock(&gZoneMetaLock);
567     {
568         result = (UVector*) uhash_get(gOlsonToMeta, tzidUChars);
569     }
570     umtx_unlock(&gZoneMetaLock);
571 
572     if (result != NULL) {
573         return result;
574     }
575 
576     // miss the cache - create new one
577     UVector *tmpResult = createMetazoneMappings(tzid);
578     if (tmpResult == NULL) {
579         // not available
580         return NULL;
581     }
582 
583     // put the new one into the cache
584     umtx_lock(&gZoneMetaLock);
585     {
586         // make sure it's already created
587         result = (UVector*) uhash_get(gOlsonToMeta, tzidUChars);
588         if (result == NULL) {
589             // add the one just created
590             int32_t tzidLen = tzid.length() + 1;
591             UChar *key = (UChar*)uprv_malloc(tzidLen * sizeof(UChar));
592             if (key == NULL) {
593                 // memory allocation error..  just return NULL
594                 result = NULL;
595                 delete tmpResult;
596             } else {
597                 tzid.extract(key, tzidLen, status);
598                 uhash_put(gOlsonToMeta, key, tmpResult, &status);
599                 if (U_FAILURE(status)) {
600                     // delete the mapping
601                     result = NULL;
602                     delete tmpResult;
603                 } else {
604                     result = tmpResult;
605                 }
606             }
607         } else {
608             // another thread already put the one
609             delete tmpResult;
610         }
611     }
612     umtx_unlock(&gZoneMetaLock);
613 
614     return result;
615 }
616 
617 UVector*
createMetazoneMappings(const UnicodeString & tzid)618 ZoneMeta::createMetazoneMappings(const UnicodeString &tzid) {
619     UVector *mzMappings = NULL;
620     UErrorCode status = U_ZERO_ERROR;
621 
622     UnicodeString canonicalID;
623     UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
624     ures_getByKey(rb, gMetazoneInfo, rb, &status);
625     getCanonicalCLDRID(tzid, canonicalID, status);
626 
627     if (U_SUCCESS(status)) {
628         char tzKey[ZID_KEY_MAX + 1];
629         int32_t tzKeyLen = canonicalID.extract(0, canonicalID.length(), tzKey, sizeof(tzKey), US_INV);
630         tzKey[tzKeyLen] = 0;
631 
632         // tzid keys are using ':' as separators
633         char *p = tzKey;
634         while (*p) {
635             if (*p == '/') {
636                 *p = ':';
637             }
638             p++;
639         }
640 
641         ures_getByKey(rb, tzKey, rb, &status);
642 
643         if (U_SUCCESS(status)) {
644             UResourceBundle *mz = NULL;
645             while (ures_hasNext(rb)) {
646                 mz = ures_getNextResource(rb, mz, &status);
647 
648                 const UChar *mz_name = ures_getStringByIndex(mz, 0, NULL, &status);
649                 const UChar *mz_from = gDefaultFrom;
650                 const UChar *mz_to = gDefaultTo;
651 
652                 if (ures_getSize(mz) == 3) {
653                     mz_from = ures_getStringByIndex(mz, 1, NULL, &status);
654                     mz_to   = ures_getStringByIndex(mz, 2, NULL, &status);
655                 }
656 
657                 if(U_FAILURE(status)){
658                     status = U_ZERO_ERROR;
659                     continue;
660                 }
661                 // We do not want to use SimpleDateformat to parse boundary dates,
662                 // because this code could be triggered by the initialization code
663                 // used by SimpleDateFormat.
664                 UDate from = parseDate(mz_from, status);
665                 UDate to = parseDate(mz_to, status);
666                 if (U_FAILURE(status)) {
667                     status = U_ZERO_ERROR;
668                     continue;
669                 }
670 
671                 OlsonToMetaMappingEntry *entry = (OlsonToMetaMappingEntry*)uprv_malloc(sizeof(OlsonToMetaMappingEntry));
672                 if (entry == NULL) {
673                     status = U_MEMORY_ALLOCATION_ERROR;
674                     break;
675                 }
676                 entry->mzid = mz_name;
677                 entry->from = from;
678                 entry->to = to;
679 
680                 if (mzMappings == NULL) {
681                     mzMappings = new UVector(deleteOlsonToMetaMappingEntry, NULL, status);
682                     if (U_FAILURE(status)) {
683                         delete mzMappings;
684                         deleteOlsonToMetaMappingEntry(entry);
685                         uprv_free(entry);
686                         break;
687                     }
688                 }
689 
690                 mzMappings->addElement(entry, status);
691                 if (U_FAILURE(status)) {
692                     break;
693                 }
694             }
695             ures_close(mz);
696             if (U_FAILURE(status)) {
697                 if (mzMappings != NULL) {
698                     delete mzMappings;
699                     mzMappings = NULL;
700                 }
701             }
702         }
703     }
704     ures_close(rb);
705     return mzMappings;
706 }
707 
708 UnicodeString& U_EXPORT2
getZoneIdByMetazone(const UnicodeString & mzid,const UnicodeString & region,UnicodeString & result)709 ZoneMeta::getZoneIdByMetazone(const UnicodeString &mzid, const UnicodeString &region, UnicodeString &result) {
710     UErrorCode status = U_ZERO_ERROR;
711     const UChar *tzid = NULL;
712     int32_t tzidLen = 0;
713     char keyBuf[ZID_KEY_MAX + 1];
714     int32_t keyLen = 0;
715 
716     if (mzid.isBogus() || mzid.length() > ZID_KEY_MAX) {
717         result.setToBogus();
718         return result;
719     }
720 
721     keyLen = mzid.extract(0, mzid.length(), keyBuf, ZID_KEY_MAX + 1, US_INV);
722     keyBuf[keyLen] = 0;
723 
724     UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
725     ures_getByKey(rb, gMapTimezonesTag, rb, &status);
726     ures_getByKey(rb, keyBuf, rb, &status);
727 
728     if (U_SUCCESS(status)) {
729         // check region mapping
730         if (region.length() == 2 || region.length() == 3) {
731             keyLen = region.extract(0, region.length(), keyBuf, ZID_KEY_MAX + 1, US_INV);
732             keyBuf[keyLen] = 0;
733             tzid = ures_getStringByKey(rb, keyBuf, &tzidLen, &status);
734             if (status == U_MISSING_RESOURCE_ERROR) {
735                 status = U_ZERO_ERROR;
736             }
737         }
738         if (U_SUCCESS(status) && tzid == NULL) {
739             // try "001"
740             tzid = ures_getStringByKey(rb, gWorldTag, &tzidLen, &status);
741         }
742     }
743     ures_close(rb);
744 
745     if (tzid == NULL) {
746         result.setToBogus();
747     } else {
748         result.setTo(tzid, tzidLen);
749     }
750 
751     return result;
752 }
753 
initAvailableMetaZoneIDs()754 static void U_CALLCONV initAvailableMetaZoneIDs () {
755     U_ASSERT(gMetaZoneIDs == NULL);
756     U_ASSERT(gMetaZoneIDTable == NULL);
757     ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
758 
759     UErrorCode status = U_ZERO_ERROR;
760     gMetaZoneIDTable = uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, NULL, &status);
761     if (U_FAILURE(status) || gMetaZoneIDTable == NULL) {
762         gMetaZoneIDTable = NULL;
763         return;
764     }
765     uhash_setKeyDeleter(gMetaZoneIDTable, uprv_deleteUObject);
766     // No valueDeleter, because the vector maintain the value objects
767     gMetaZoneIDs = new UVector(NULL, uhash_compareUChars, status);
768     if (U_FAILURE(status) || gMetaZoneIDs == NULL) {
769         gMetaZoneIDs = NULL;
770         uhash_close(gMetaZoneIDTable);
771         gMetaZoneIDTable = NULL;
772         return;
773     }
774     gMetaZoneIDs->setDeleter(uprv_free);
775 
776     UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
777     UResourceBundle *bundle = ures_getByKey(rb, gMapTimezonesTag, NULL, &status);
778     UResourceBundle res;
779     ures_initStackObject(&res);
780     while (U_SUCCESS(status) && ures_hasNext(bundle)) {
781         ures_getNextResource(bundle, &res, &status);
782         if (U_FAILURE(status)) {
783             break;
784         }
785         const char *mzID = ures_getKey(&res);
786         int32_t len = uprv_strlen(mzID);
787         UChar *uMzID = (UChar*)uprv_malloc(sizeof(UChar) * (len + 1));
788         if (uMzID == NULL) {
789             status = U_MEMORY_ALLOCATION_ERROR;
790             break;
791         }
792         u_charsToUChars(mzID, uMzID, len);
793         uMzID[len] = 0;
794         UnicodeString *usMzID = new UnicodeString(uMzID);
795         if (uhash_get(gMetaZoneIDTable, usMzID) == NULL) {
796             gMetaZoneIDs->addElement((void *)uMzID, status);
797             uhash_put(gMetaZoneIDTable, (void *)usMzID, (void *)uMzID, &status);
798         } else {
799             uprv_free(uMzID);
800             delete usMzID;
801         }
802     }
803     ures_close(&res);
804     ures_close(bundle);
805     ures_close(rb);
806 
807     if (U_FAILURE(status)) {
808         uhash_close(gMetaZoneIDTable);
809         delete gMetaZoneIDs;
810         gMetaZoneIDTable = NULL;
811         gMetaZoneIDs = NULL;
812     }
813 }
814 
815 const UVector*
getAvailableMetazoneIDs()816 ZoneMeta::getAvailableMetazoneIDs() {
817     umtx_initOnce(gMetaZoneIDsInitOnce, &initAvailableMetaZoneIDs);
818     return gMetaZoneIDs;
819 }
820 
821 const UChar*
findMetaZoneID(const UnicodeString & mzid)822 ZoneMeta::findMetaZoneID(const UnicodeString& mzid) {
823     umtx_initOnce(gMetaZoneIDsInitOnce, &initAvailableMetaZoneIDs);
824     if (gMetaZoneIDTable == NULL) {
825         return NULL;
826     }
827     return (const UChar*)uhash_get(gMetaZoneIDTable, &mzid);
828 }
829 
830 const UChar*
findTimeZoneID(const UnicodeString & tzid)831 ZoneMeta::findTimeZoneID(const UnicodeString& tzid) {
832     return TimeZone::findID(tzid);
833 }
834 
835 
836 TimeZone*
createCustomTimeZone(int32_t offset)837 ZoneMeta::createCustomTimeZone(int32_t offset) {
838     UBool negative = FALSE;
839     int32_t tmp = offset;
840     if (offset < 0) {
841         negative = TRUE;
842         tmp = -offset;
843     }
844     int32_t hour, min, sec;
845 
846     tmp /= 1000;
847     sec = tmp % 60;
848     tmp /= 60;
849     min = tmp % 60;
850     hour = tmp / 60;
851 
852     UnicodeString zid;
853     formatCustomID(hour, min, sec, negative, zid);
854     return new SimpleTimeZone(offset, zid);
855 }
856 
857 UnicodeString&
formatCustomID(uint8_t hour,uint8_t min,uint8_t sec,UBool negative,UnicodeString & id)858 ZoneMeta::formatCustomID(uint8_t hour, uint8_t min, uint8_t sec, UBool negative, UnicodeString& id) {
859     // Create normalized time zone ID - GMT[+|-]HH:mm[:ss]
860     id.setTo(gCustomTzPrefix, -1);
861     if (hour != 0 || min != 0) {
862         if (negative) {
863           id.append((UChar)0x2D);    // '-'
864         } else {
865           id.append((UChar)0x2B);    // '+'
866         }
867         // Always use US-ASCII digits
868         id.append((UChar)(0x30 + (hour%100)/10));
869         id.append((UChar)(0x30 + (hour%10)));
870         id.append((UChar)0x3A);    // ':'
871         id.append((UChar)(0x30 + (min%100)/10));
872         id.append((UChar)(0x30 + (min%10)));
873         if (sec != 0) {
874           id.append((UChar)0x3A);    // ':'
875           id.append((UChar)(0x30 + (sec%100)/10));
876           id.append((UChar)(0x30 + (sec%10)));
877         }
878     }
879     return id;
880 }
881 
882 const UChar*
getShortID(const TimeZone & tz)883 ZoneMeta::getShortID(const TimeZone& tz) {
884     const UChar* canonicalID = NULL;
885     if (dynamic_cast<const OlsonTimeZone *>(&tz) != NULL) {
886         // short cut for OlsonTimeZone
887         const OlsonTimeZone *otz = (const OlsonTimeZone*)&tz;
888         canonicalID = otz->getCanonicalID();
889     }
890     if (canonicalID == NULL) {
891         return NULL;
892     }
893     return getShortIDFromCanonical(canonicalID);
894 }
895 
896 const UChar*
getShortID(const UnicodeString & id)897 ZoneMeta::getShortID(const UnicodeString& id) {
898     UErrorCode status = U_ZERO_ERROR;
899     const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(id, status);
900     if (U_FAILURE(status) || canonicalID == NULL) {
901         return NULL;
902     }
903     return ZoneMeta::getShortIDFromCanonical(canonicalID);
904 }
905 
906 const UChar*
getShortIDFromCanonical(const UChar * canonicalID)907 ZoneMeta::getShortIDFromCanonical(const UChar* canonicalID) {
908     const UChar* shortID = NULL;
909     int32_t len = u_strlen(canonicalID);
910     char tzidKey[ZID_KEY_MAX + 1];
911 
912     u_UCharsToChars(canonicalID, tzidKey, len);
913     tzidKey[len] = (char) 0; // Make sure it is null terminated.
914 
915     // replace '/' with ':'
916     char *p = tzidKey;
917     while (*p++) {
918         if (*p == '/') {
919             *p = ':';
920         }
921     }
922 
923     UErrorCode status = U_ZERO_ERROR;
924     UResourceBundle *rb = ures_openDirect(NULL, gKeyTypeData, &status);
925     ures_getByKey(rb, gTypeMapTag, rb, &status);
926     ures_getByKey(rb, gTimezoneTag, rb, &status);
927     shortID = ures_getStringByKey(rb, tzidKey, NULL, &status);
928     ures_close(rb);
929 
930     return shortID;
931 }
932 
933 U_NAMESPACE_END
934 
935 #endif /* #if !UCONFIG_NO_FORMATTING */
936