• 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 #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
31 
32 static UMutex gZoneMetaLock = U_MUTEX_INITIALIZER;
33 
34 // CLDR Canonical ID mapping table
35 static UHashtable *gCanonicalIDCache = NULL;
36 static icu::UInitOnce gCanonicalIDCacheInitOnce = U_INITONCE_INITIALIZER;
37 
38 // Metazone mapping table
39 static UHashtable *gOlsonToMeta = NULL;
40 static icu::UInitOnce gOlsonToMetaInitOnce = U_INITONCE_INITIALIZER;
41 
42 // Available metazone IDs vector and table
43 static icu::UVector *gMetaZoneIDs = NULL;
44 static UHashtable *gMetaZoneIDTable = NULL;
45 static icu::UInitOnce gMetaZoneIDsInitOnce = U_INITONCE_INITIALIZER;
46 
47 // Country info vectors
48 static icu::UVector *gSingleZoneCountries = NULL;
49 static icu::UVector *gMultiZonesCountries = NULL;
50 static icu::UInitOnce gCountryInfoVectorsInitOnce = U_INITONCE_INITIALIZER;
51 
52 U_CDECL_BEGIN
53 
54 /**
55  * Cleanup callback func
56  */
zoneMeta_cleanup(void)57 static UBool U_CALLCONV zoneMeta_cleanup(void)
58 {
59     if (gCanonicalIDCache != NULL) {
60         uhash_close(gCanonicalIDCache);
61         gCanonicalIDCache = NULL;
62     }
63     gCanonicalIDCacheInitOnce.reset();
64 
65     if (gOlsonToMeta != NULL) {
66         uhash_close(gOlsonToMeta);
67         gOlsonToMeta = NULL;
68     }
69     gOlsonToMetaInitOnce.reset();
70 
71     if (gMetaZoneIDTable != NULL) {
72         uhash_close(gMetaZoneIDTable);
73         gMetaZoneIDTable = NULL;
74     }
75     // delete after closing gMetaZoneIDTable, because it holds
76     // value objects held by the hashtable
77     delete gMetaZoneIDs;
78     gMetaZoneIDs = NULL;
79     gMetaZoneIDsInitOnce.reset();
80 
81     delete gSingleZoneCountries;
82     gSingleZoneCountries = NULL;
83     delete gMultiZonesCountries;
84     gMultiZonesCountries = NULL;
85     gCountryInfoVectorsInitOnce.reset();
86 
87     return TRUE;
88 }
89 
90 /**
91  * Deleter for UChar* string
92  */
93 static void U_CALLCONV
deleteUCharString(void * obj)94 deleteUCharString(void *obj) {
95     UChar *entry = (UChar*)obj;
96     uprv_free(entry);
97 }
98 
99 /**
100  * Deleter for UVector
101  */
102 static void U_CALLCONV
deleteUVector(void * obj)103 deleteUVector(void *obj) {
104    delete (icu::UVector*) obj;
105 }
106 
107 /**
108  * Deleter for OlsonToMetaMappingEntry
109  */
110 static void U_CALLCONV
deleteOlsonToMetaMappingEntry(void * obj)111 deleteOlsonToMetaMappingEntry(void *obj) {
112     icu::OlsonToMetaMappingEntry *entry = (icu::OlsonToMetaMappingEntry*)obj;
113     uprv_free(entry);
114 }
115 
116 U_CDECL_END
117 
118 U_NAMESPACE_BEGIN
119 
120 #define ZID_KEY_MAX 128
121 
122 static const char gMetaZones[]          = "metaZones";
123 static const char gMetazoneInfo[]       = "metazoneInfo";
124 static const char gMapTimezonesTag[]    = "mapTimezones";
125 
126 static const char gKeyTypeData[]        = "keyTypeData";
127 static const char gTypeAliasTag[]       = "typeAlias";
128 static const char gTypeMapTag[]         = "typeMap";
129 static const char gTimezoneTag[]        = "timezone";
130 
131 static const char gPrimaryZonesTag[]    = "primaryZones";
132 
133 static const char gWorldTag[]           = "001";
134 
135 static const UChar gWorld[] = {0x30, 0x30, 0x31, 0x00}; // "001"
136 
137 static const UChar gDefaultFrom[] = {0x31, 0x39, 0x37, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31,
138                                      0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x00}; // "1970-01-01 00:00"
139 static const UChar gDefaultTo[]   = {0x39, 0x39, 0x39, 0x39, 0x2D, 0x31, 0x32, 0x2D, 0x33, 0x31,
140                                      0x20, 0x32, 0x33, 0x3A, 0x35, 0x39, 0x00}; // "9999-12-31 23:59"
141 
142 static const UChar gCustomTzPrefix[]    = {0x47, 0x4D, 0x54, 0};    // "GMT"
143 
144 #define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1)
145 
146 /*
147  * Convert a date string used by metazone mappings to UDate.
148  * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
149  */
150 static UDate
parseDate(const UChar * text,UErrorCode & status)151 parseDate (const UChar *text, UErrorCode &status) {
152     if (U_FAILURE(status)) {
153         return 0;
154     }
155     int32_t len = u_strlen(text);
156     if (len != 16 && len != 10) {
157         // It must be yyyy-MM-dd HH:mm (length 16) or yyyy-MM-dd (length 10)
158         status = U_INVALID_FORMAT_ERROR;
159         return 0;
160     }
161 
162     int32_t year = 0, month = 0, day = 0, hour = 0, min = 0, n;
163     int32_t idx;
164 
165     // "yyyy" (0 - 3)
166     for (idx = 0; idx <= 3 && U_SUCCESS(status); idx++) {
167         n = ASCII_DIGIT((int32_t)text[idx]);
168         if (n >= 0) {
169             year = 10*year + n;
170         } else {
171             status = U_INVALID_FORMAT_ERROR;
172         }
173     }
174     // "MM" (5 - 6)
175     for (idx = 5; idx <= 6 && U_SUCCESS(status); idx++) {
176         n = ASCII_DIGIT((int32_t)text[idx]);
177         if (n >= 0) {
178             month = 10*month + n;
179         } else {
180             status = U_INVALID_FORMAT_ERROR;
181         }
182     }
183     // "dd" (8 - 9)
184     for (idx = 8; idx <= 9 && U_SUCCESS(status); idx++) {
185         n = ASCII_DIGIT((int32_t)text[idx]);
186         if (n >= 0) {
187             day = 10*day + n;
188         } else {
189             status = U_INVALID_FORMAT_ERROR;
190         }
191     }
192     if (len == 16) {
193         // "HH" (11 - 12)
194         for (idx = 11; idx <= 12 && U_SUCCESS(status); idx++) {
195             n = ASCII_DIGIT((int32_t)text[idx]);
196             if (n >= 0) {
197                 hour = 10*hour + n;
198             } else {
199                 status = U_INVALID_FORMAT_ERROR;
200             }
201         }
202         // "mm" (14 - 15)
203         for (idx = 14; idx <= 15 && U_SUCCESS(status); idx++) {
204             n = ASCII_DIGIT((int32_t)text[idx]);
205             if (n >= 0) {
206                 min = 10*min + n;
207             } else {
208                 status = U_INVALID_FORMAT_ERROR;
209             }
210         }
211     }
212 
213     if (U_SUCCESS(status)) {
214         UDate date = Grego::fieldsToDay(year, month - 1, day) * U_MILLIS_PER_DAY
215             + hour * U_MILLIS_PER_HOUR + min * U_MILLIS_PER_MINUTE;
216         return date;
217     }
218     return 0;
219 }
220 
initCanonicalIDCache(UErrorCode & status)221 static void U_CALLCONV initCanonicalIDCache(UErrorCode &status) {
222     gCanonicalIDCache = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
223     if (gCanonicalIDCache == NULL) {
224         status = U_MEMORY_ALLOCATION_ERROR;
225     }
226     if (U_FAILURE(status)) {
227         gCanonicalIDCache = NULL;
228     }
229     // No key/value deleters - keys/values are from a resource bundle
230     ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
231 }
232 
233 
234 const UChar* U_EXPORT2
getCanonicalCLDRID(const UnicodeString & tzid,UErrorCode & status)235 ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UErrorCode& status) {
236     if (U_FAILURE(status)) {
237         return NULL;
238     }
239 
240     if (tzid.isBogus() || tzid.length() > ZID_KEY_MAX) {
241         status = U_ILLEGAL_ARGUMENT_ERROR;
242         return NULL;
243     }
244 
245     // Checking the cached results
246     umtx_initOnce(gCanonicalIDCacheInitOnce, &initCanonicalIDCache, status);
247     if (U_FAILURE(status)) {
248         return NULL;
249     }
250 
251     const UChar *canonicalID = NULL;
252 
253     UErrorCode tmpStatus = U_ZERO_ERROR;
254     UChar utzid[ZID_KEY_MAX + 1];
255     tzid.extract(utzid, ZID_KEY_MAX + 1, tmpStatus);
256     U_ASSERT(tmpStatus == U_ZERO_ERROR);    // we checked the length of tzid already
257 
258     // Check if it was already cached
259     umtx_lock(&gZoneMetaLock);
260     {
261         canonicalID = (const UChar *)uhash_get(gCanonicalIDCache, utzid);
262     }
263     umtx_unlock(&gZoneMetaLock);
264 
265     if (canonicalID != NULL) {
266         return canonicalID;
267     }
268 
269     // If not, resolve CLDR canonical ID with resource data
270     UBool isInputCanonical = FALSE;
271     char id[ZID_KEY_MAX + 1];
272     tzid.extract(0, 0x7fffffff, id, LENGTHOF(id), US_INV);
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, gKeyTypeData, &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                 int32_t 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 
countryInfoVectorsInit(UErrorCode & status)393 static void U_CALLCONV countryInfoVectorsInit(UErrorCode &status) {
394     // Create empty vectors
395     // No deleters for these UVectors, it's a reference to a resource bundle string.
396     gSingleZoneCountries = new UVector(NULL, uhash_compareUChars, status);
397     if (gSingleZoneCountries == NULL) {
398         status = U_MEMORY_ALLOCATION_ERROR;
399     }
400     gMultiZonesCountries = new UVector(NULL, uhash_compareUChars, status);
401     if (gMultiZonesCountries == NULL) {
402         status = U_MEMORY_ALLOCATION_ERROR;
403     }
404 
405     if (U_FAILURE(status)) {
406         delete gSingleZoneCountries;
407         delete gMultiZonesCountries;
408         gSingleZoneCountries = NULL;
409         gMultiZonesCountries  = NULL;
410     }
411     ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
412 }
413 
414 
415 UnicodeString& U_EXPORT2
getCanonicalCountry(const UnicodeString & tzid,UnicodeString & country,UBool * isPrimary)416 ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &country, UBool *isPrimary /* = NULL */) {
417     if (isPrimary != NULL) {
418         *isPrimary = FALSE;
419     }
420 
421     const UChar *region = TimeZone::getRegion(tzid);
422     if (region != NULL && u_strcmp(gWorld, region) != 0) {
423         country.setTo(region, -1);
424     } else {
425         country.setToBogus();
426         return country;
427     }
428 
429     if (isPrimary != NULL) {
430         char regionBuf[] = {0, 0, 0};
431 
432         // Checking the cached results
433         UErrorCode status = U_ZERO_ERROR;
434         umtx_initOnce(gCountryInfoVectorsInitOnce, &countryInfoVectorsInit, status);
435         if (U_FAILURE(status)) {
436             return country;
437         }
438 
439         // Check if it was already cached
440         UBool cached = FALSE;
441         UBool singleZone = FALSE;
442         umtx_lock(&gZoneMetaLock);
443         {
444             singleZone = cached = gSingleZoneCountries->contains((void*)region);
445             if (!cached) {
446                 cached = gMultiZonesCountries->contains((void*)region);
447             }
448         }
449         umtx_unlock(&gZoneMetaLock);
450 
451         if (!cached) {
452             // We need to go through all zones associated with the region.
453             // This is relatively heavy operation.
454 
455             U_ASSERT(u_strlen(region) == 2);
456 
457             u_UCharsToChars(region, regionBuf, 2);
458 
459             StringEnumeration *ids = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, regionBuf, NULL, status);
460             int32_t idsLen = ids->count(status);
461             if (U_SUCCESS(status) && idsLen == 1) {
462                 // only the single zone is available for the region
463                 singleZone = TRUE;
464             }
465             delete ids;
466 
467             // Cache the result
468             umtx_lock(&gZoneMetaLock);
469             {
470                 UErrorCode ec = U_ZERO_ERROR;
471                 if (singleZone) {
472                     if (!gSingleZoneCountries->contains((void*)region)) {
473                         gSingleZoneCountries->addElement((void*)region, ec);
474                     }
475                 } else {
476                     if (!gMultiZonesCountries->contains((void*)region)) {
477                         gMultiZonesCountries->addElement((void*)region, ec);
478                     }
479                 }
480             }
481             umtx_unlock(&gZoneMetaLock);
482         }
483 
484         if (singleZone) {
485             *isPrimary = TRUE;
486         } else {
487             // Note: We may cache the primary zone map in future.
488 
489             // Even a country has multiple zones, one of them might be
490             // dominant and treated as a primary zone
491             int32_t idLen = 0;
492             if (regionBuf[0] == 0) {
493                 u_UCharsToChars(region, regionBuf, 2);
494             }
495 
496             UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
497             ures_getByKey(rb, gPrimaryZonesTag, rb, &status);
498             const UChar *primaryZone = ures_getStringByKey(rb, regionBuf, &idLen, &status);
499             if (U_SUCCESS(status)) {
500                 if (tzid.compare(primaryZone, idLen) == 0) {
501                     *isPrimary = TRUE;
502                 } else {
503                     // The given ID might not be a canonical ID
504                     UnicodeString canonicalID;
505                     TimeZone::getCanonicalID(tzid, canonicalID, status);
506                     if (U_SUCCESS(status) && canonicalID.compare(primaryZone, idLen) == 0) {
507                         *isPrimary = TRUE;
508                     }
509                 }
510             }
511             ures_close(rb);
512         }
513     }
514 
515     return country;
516 }
517 
518 UnicodeString& U_EXPORT2
getMetazoneID(const UnicodeString & tzid,UDate date,UnicodeString & result)519 ZoneMeta::getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &result) {
520     UBool isSet = FALSE;
521     const UVector *mappings = getMetazoneMappings(tzid);
522     if (mappings != NULL) {
523         for (int32_t i = 0; i < mappings->size(); i++) {
524             OlsonToMetaMappingEntry *mzm = (OlsonToMetaMappingEntry*)mappings->elementAt(i);
525             if (mzm->from <= date && mzm->to > date) {
526                 result.setTo(mzm->mzid, -1);
527                 isSet = TRUE;
528                 break;
529             }
530         }
531     }
532     if (!isSet) {
533         result.setToBogus();
534     }
535     return result;
536 }
537 
olsonToMetaInit(UErrorCode & status)538 static void U_CALLCONV olsonToMetaInit(UErrorCode &status) {
539     U_ASSERT(gOlsonToMeta == NULL);
540     ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
541     gOlsonToMeta = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
542     if (U_FAILURE(status)) {
543         gOlsonToMeta = NULL;
544     } else {
545         uhash_setKeyDeleter(gOlsonToMeta, deleteUCharString);
546         uhash_setValueDeleter(gOlsonToMeta, deleteUVector);
547     }
548 }
549 
550 
551 const UVector* U_EXPORT2
getMetazoneMappings(const UnicodeString & tzid)552 ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) {
553     UErrorCode status = U_ZERO_ERROR;
554     UChar tzidUChars[ZID_KEY_MAX + 1];
555     tzid.extract(tzidUChars, ZID_KEY_MAX + 1, status);
556     if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) {
557         return NULL;
558     }
559 
560     umtx_initOnce(gOlsonToMetaInitOnce, &olsonToMetaInit, status);
561     if (U_FAILURE(status)) {
562         return NULL;
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.isBogus() || 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 
initAvailableMetaZoneIDs()756 static void U_CALLCONV initAvailableMetaZoneIDs () {
757     U_ASSERT(gMetaZoneIDs == NULL);
758     U_ASSERT(gMetaZoneIDTable == NULL);
759     ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
760 
761     UErrorCode status = U_ZERO_ERROR;
762     gMetaZoneIDTable = uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, NULL, &status);
763     if (U_FAILURE(status) || gMetaZoneIDTable == NULL) {
764         gMetaZoneIDTable = NULL;
765         return;
766     }
767     uhash_setKeyDeleter(gMetaZoneIDTable, uprv_deleteUObject);
768     // No valueDeleter, because the vector maintain the value objects
769     gMetaZoneIDs = new UVector(NULL, uhash_compareUChars, status);
770     if (U_FAILURE(status) || gMetaZoneIDs == NULL) {
771         gMetaZoneIDs = NULL;
772         uhash_close(gMetaZoneIDTable);
773         gMetaZoneIDTable = NULL;
774         return;
775     }
776     gMetaZoneIDs->setDeleter(uprv_free);
777 
778     UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
779     UResourceBundle *bundle = ures_getByKey(rb, gMapTimezonesTag, NULL, &status);
780     UResourceBundle res;
781     ures_initStackObject(&res);
782     while (U_SUCCESS(status) && ures_hasNext(bundle)) {
783         ures_getNextResource(bundle, &res, &status);
784         if (U_FAILURE(status)) {
785             break;
786         }
787         const char *mzID = ures_getKey(&res);
788         int32_t len = uprv_strlen(mzID);
789         UChar *uMzID = (UChar*)uprv_malloc(sizeof(UChar) * (len + 1));
790         if (uMzID == NULL) {
791             status = U_MEMORY_ALLOCATION_ERROR;
792             break;
793         }
794         u_charsToUChars(mzID, uMzID, len);
795         uMzID[len] = 0;
796         UnicodeString *usMzID = new UnicodeString(uMzID);
797         if (uhash_get(gMetaZoneIDTable, usMzID) == NULL) {
798             gMetaZoneIDs->addElement((void *)uMzID, status);
799             uhash_put(gMetaZoneIDTable, (void *)usMzID, (void *)uMzID, &status);
800         } else {
801             uprv_free(uMzID);
802             delete usMzID;
803         }
804     }
805     ures_close(&res);
806     ures_close(bundle);
807     ures_close(rb);
808 
809     if (U_FAILURE(status)) {
810         uhash_close(gMetaZoneIDTable);
811         delete gMetaZoneIDs;
812         gMetaZoneIDTable = NULL;
813         gMetaZoneIDs = NULL;
814     }
815 }
816 
817 const UVector*
getAvailableMetazoneIDs()818 ZoneMeta::getAvailableMetazoneIDs() {
819     umtx_initOnce(gMetaZoneIDsInitOnce, &initAvailableMetaZoneIDs);
820     return gMetaZoneIDs;
821 }
822 
823 const UChar*
findMetaZoneID(const UnicodeString & mzid)824 ZoneMeta::findMetaZoneID(const UnicodeString& mzid) {
825     umtx_initOnce(gMetaZoneIDsInitOnce, &initAvailableMetaZoneIDs);
826     if (gMetaZoneIDTable == NULL) {
827         return NULL;
828     }
829     return (const UChar*)uhash_get(gMetaZoneIDTable, &mzid);
830 }
831 
832 const UChar*
findTimeZoneID(const UnicodeString & tzid)833 ZoneMeta::findTimeZoneID(const UnicodeString& tzid) {
834     return TimeZone::findID(tzid);
835 }
836 
837 
838 TimeZone*
createCustomTimeZone(int32_t offset)839 ZoneMeta::createCustomTimeZone(int32_t offset) {
840     UBool negative = FALSE;
841     int32_t tmp = offset;
842     if (offset < 0) {
843         negative = TRUE;
844         tmp = -offset;
845     }
846     int32_t hour, min, sec;
847 
848     tmp /= 1000;
849     sec = tmp % 60;
850     tmp /= 60;
851     min = tmp % 60;
852     hour = tmp / 60;
853 
854     UnicodeString zid;
855     formatCustomID(hour, min, sec, negative, zid);
856     return new SimpleTimeZone(offset, zid);
857 }
858 
859 UnicodeString&
formatCustomID(uint8_t hour,uint8_t min,uint8_t sec,UBool negative,UnicodeString & id)860 ZoneMeta::formatCustomID(uint8_t hour, uint8_t min, uint8_t sec, UBool negative, UnicodeString& id) {
861     // Create normalized time zone ID - GMT[+|-]HH:mm[:ss]
862     id.setTo(gCustomTzPrefix, -1);
863     if (hour != 0 || min != 0) {
864         if (negative) {
865           id.append((UChar)0x2D);    // '-'
866         } else {
867           id.append((UChar)0x2B);    // '+'
868         }
869         // Always use US-ASCII digits
870         id.append((UChar)(0x30 + (hour%100)/10));
871         id.append((UChar)(0x30 + (hour%10)));
872         id.append((UChar)0x3A);    // ':'
873         id.append((UChar)(0x30 + (min%100)/10));
874         id.append((UChar)(0x30 + (min%10)));
875         if (sec != 0) {
876           id.append((UChar)0x3A);    // ':'
877           id.append((UChar)(0x30 + (sec%100)/10));
878           id.append((UChar)(0x30 + (sec%10)));
879         }
880     }
881     return id;
882 }
883 
884 const UChar*
getShortID(const TimeZone & tz)885 ZoneMeta::getShortID(const TimeZone& tz) {
886     const UChar* canonicalID = NULL;
887     if (dynamic_cast<const OlsonTimeZone *>(&tz) != NULL) {
888         // short cut for OlsonTimeZone
889         const OlsonTimeZone *otz = (const OlsonTimeZone*)&tz;
890         canonicalID = otz->getCanonicalID();
891     }
892     if (canonicalID == NULL) {
893         return NULL;
894     }
895     return getShortIDFromCanonical(canonicalID);
896 }
897 
898 const UChar*
getShortID(const UnicodeString & id)899 ZoneMeta::getShortID(const UnicodeString& id) {
900     UErrorCode status = U_ZERO_ERROR;
901     const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(id, status);
902     if (U_FAILURE(status) || canonicalID == NULL) {
903         return NULL;
904     }
905     return ZoneMeta::getShortIDFromCanonical(canonicalID);
906 }
907 
908 const UChar*
getShortIDFromCanonical(const UChar * canonicalID)909 ZoneMeta::getShortIDFromCanonical(const UChar* canonicalID) {
910     const UChar* shortID = NULL;
911     int32_t len = u_strlen(canonicalID);
912     char tzidKey[ZID_KEY_MAX + 1];
913 
914     u_UCharsToChars(canonicalID, tzidKey, len);
915     tzidKey[len] = (char) 0; // Make sure it is null terminated.
916 
917     // replace '/' with ':'
918     char *p = tzidKey;
919     while (*p++) {
920         if (*p == '/') {
921             *p = ':';
922         }
923     }
924 
925     UErrorCode status = U_ZERO_ERROR;
926     UResourceBundle *rb = ures_openDirect(NULL, gKeyTypeData, &status);
927     ures_getByKey(rb, gTypeMapTag, rb, &status);
928     ures_getByKey(rb, gTimezoneTag, rb, &status);
929     shortID = ures_getStringByKey(rb, tzidKey, NULL, &status);
930     ures_close(rb);
931 
932     return shortID;
933 }
934 
935 U_NAMESPACE_END
936 
937 #endif /* #if !UCONFIG_NO_FORMATTING */
938