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