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