1 /* 2 ******************************************************************************* 3 * Copyright (C) 2007-2009, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 */ 7 8 #include "unicode/utypes.h" 9 10 #if !UCONFIG_NO_FORMATTING 11 12 #include "zonemeta.h" 13 14 #include "unicode/timezone.h" 15 #include "unicode/ustring.h" 16 #include "unicode/putil.h" 17 18 #include "umutex.h" 19 #include "uvector.h" 20 #include "cmemory.h" 21 #include "gregoimp.h" 22 #include "cstring.h" 23 #include "ucln_in.h" 24 25 // Metazone mapping tables 26 static UMTX gZoneMetaLock = NULL; 27 static UHashtable *gCanonicalMap = NULL; 28 static UHashtable *gOlsonToMeta = NULL; 29 static UHashtable *gMetaToOlson = NULL; 30 static UBool gCanonicalMapInitialized = FALSE; 31 static UBool gOlsonToMetaInitialized = FALSE; 32 static UBool gMetaToOlsonInitialized = FALSE; 33 static UChar **gUStringTable = NULL; 34 static int32_t gUStringCount = 0; 35 static int32_t gUStringAlloc = 0; 36 37 // Currently (ICU 4.1.3+), gUStringTable only contains strings allocated in the section of 38 // createCanonicalMap that iterates over the enumerator created with TimeZone::createEnumeration. 39 // And currently, that allocates a total of 22 strings. So USTRING_ALLOC_START is defined to 40 // be adequate for that set, and USTRING_ALLOC_INCR is a reasonable expansion increment. In 41 // future versions of ICU, these numbers may need adjusting to avoid excessive reallocs, or to 42 // avoid allocating unused memory (but in any case the effects are small). 43 #define USTRING_ALLOC_START 24 44 #define USTRING_ALLOC_INCR 12 45 46 U_CDECL_BEGIN 47 48 // We have switched CanonicalMap to use const UChar* strings for the key and for the id field of 49 // CanonicalMapEntry; that is because for the most part these now point into UChar strings in the 50 // shared data file, in order to reduce process-specific dynamically-allocated memory. Consequently, 51 // there is no longer a deleter for the key field, and the deleter for CanonicalMapEntry 52 // no longer frees the id field. However, for the few strings that are obtained from the 53 // TimeZone::createEnumeration() enumerator or from TimeZone::dereferOlsonLink instead of the 54 // data file, we do need to allocate copies. In order to ensure that these strings are freed by 55 // zoneMeta_cleanup(), we need to create a little memory manager for them; this is in the form of 56 // a table that tracks the strings allocated for this purpose. The following three functions 57 // (along with the gUStringXxxxx statics) are used to allocate and free such strings. 58 59 // The following allocs space for a UChar* string of the specified length, puts a pointer to the string 60 // in gUStringTable, and returns either a pointer to the allocated string space, or NULL for failure. 61 static UChar * allocUStringInTable(int32_t uStringLen) { 62 UChar * uStringSpace = NULL; 63 // initialize the table if necessary 64 umtx_lock(&gZoneMetaLock); 65 if (gUStringTable == NULL) { 66 gUStringTable = (UChar**)uprv_malloc(USTRING_ALLOC_START*sizeof(UChar*)); 67 if (gUStringTable != NULL) { 68 gUStringAlloc = USTRING_ALLOC_START; 69 } 70 } 71 if (gUStringTable != NULL) { 72 // expand the table if necessary 73 if (gUStringCount == gUStringAlloc) { 74 UChar ** newTable = (UChar**)uprv_realloc(gUStringTable, (gUStringAlloc+USTRING_ALLOC_INCR)*sizeof(UChar*)); 75 if (newTable != NULL) { 76 gUStringTable = newTable; 77 gUStringAlloc += USTRING_ALLOC_INCR; 78 } 79 } 80 // add the string if possible 81 if (gUStringCount < gUStringAlloc) { 82 uStringSpace = (UChar*)uprv_malloc(uStringLen*sizeof(UChar)); 83 if (uStringSpace != NULL) { 84 gUStringTable[gUStringCount++] = uStringSpace; 85 } 86 } 87 } 88 umtx_unlock(&gZoneMetaLock); 89 return uStringSpace; 90 } 91 92 static void removeLastUStringFromTable(void) { 93 umtx_lock(&gZoneMetaLock); 94 if (gUStringCount > 0) { 95 free(gUStringTable[--gUStringCount]); 96 } 97 umtx_unlock(&gZoneMetaLock); 98 } 99 100 static void freeUStringTable(void) { 101 int32_t uStringCount = gUStringCount; 102 gUStringCount = 0; 103 gUStringAlloc = 0; 104 if (gUStringTable != NULL) { 105 while (uStringCount > 0) { 106 free(gUStringTable[--uStringCount]); 107 } 108 free(gUStringTable); 109 gUStringTable = NULL; 110 } 111 } 112 113 /** 114 * Cleanup callback func 115 */ 116 static UBool U_CALLCONV zoneMeta_cleanup(void) 117 { 118 umtx_destroy(&gZoneMetaLock); 119 120 if (gCanonicalMap != NULL) { 121 uhash_close(gCanonicalMap); 122 gCanonicalMap = NULL; 123 } 124 gCanonicalMapInitialized = FALSE; 125 126 if (gOlsonToMeta != NULL) { 127 uhash_close(gOlsonToMeta); 128 gOlsonToMeta = NULL; 129 } 130 gOlsonToMetaInitialized = FALSE; 131 132 if (gMetaToOlson != NULL) { 133 uhash_close(gMetaToOlson); 134 gMetaToOlson = NULL; 135 } 136 gMetaToOlsonInitialized = FALSE; 137 138 freeUStringTable(); 139 140 return TRUE; 141 } 142 143 /** 144 * Deleter for UChar* string 145 */ 146 static void U_CALLCONV 147 deleteUCharString(void *obj) { 148 UChar *entry = (UChar*)obj; 149 uprv_free(entry); 150 } 151 152 /** 153 * Deleter for UVector 154 */ 155 static void U_CALLCONV 156 deleteUVector(void *obj) { 157 delete (U_NAMESPACE_QUALIFIER UVector*) obj; 158 } 159 160 /** 161 * Deleter for CanonicalMapEntry 162 */ 163 static void U_CALLCONV 164 deleteCanonicalMapEntry(void *obj) { 165 U_NAMESPACE_QUALIFIER CanonicalMapEntry *entry = (U_NAMESPACE_QUALIFIER CanonicalMapEntry*)obj; 166 uprv_free(entry); 167 } 168 169 /** 170 * Deleter for OlsonToMetaMappingEntry 171 */ 172 static void U_CALLCONV 173 deleteOlsonToMetaMappingEntry(void *obj) { 174 U_NAMESPACE_QUALIFIER OlsonToMetaMappingEntry *entry = (U_NAMESPACE_QUALIFIER OlsonToMetaMappingEntry*)obj; 175 uprv_free(entry); 176 } 177 178 /** 179 * Deleter for MetaToOlsonMappingEntry 180 */ 181 static void U_CALLCONV 182 deleteMetaToOlsonMappingEntry(void *obj) { 183 U_NAMESPACE_QUALIFIER MetaToOlsonMappingEntry *entry = (U_NAMESPACE_QUALIFIER MetaToOlsonMappingEntry*)obj; 184 uprv_free(entry->territory); 185 uprv_free(entry); 186 } 187 U_CDECL_END 188 189 U_NAMESPACE_BEGIN 190 191 #define ZID_KEY_MAX 128 192 static const char gZoneStringsTag[] = "zoneStrings"; 193 static const char gUseMetazoneTag[] = "um"; 194 195 static const char gSupplementalData[] = "supplementalData"; 196 static const char gMapTimezonesTag[] = "mapTimezones"; 197 static const char gMetazonesTag[] = "metazones"; 198 static const char gZoneFormattingTag[] = "zoneFormatting"; 199 static const char gCanonicalTag[] = "canonical"; 200 static const char gTerritoryTag[] = "territory"; 201 static const char gAliasesTag[] = "aliases"; 202 static const char gMultizoneTag[] = "multizone"; 203 204 static const char gMetazoneInfo[] = "metazoneInfo"; 205 static const char gMetazoneMappings[] = "metazoneMappings"; 206 207 #define MZID_PREFIX_LEN 5 208 static const char gMetazoneIdPrefix[] = "meta:"; 209 210 static const UChar gWorld[] = {0x30, 0x30, 0x31, 0x00}; // "001" 211 212 #define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1) 213 214 /* 215 * Convert a date string used by metazone mappings to UDate. 216 * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm". 217 */ 218 static UDate 219 parseDate (const UChar *text, UErrorCode &status) { 220 if (U_FAILURE(status)) { 221 return 0; 222 } 223 int32_t len = u_strlen(text); 224 if (len != 16 && len != 10) { 225 // It must be yyyy-MM-dd HH:mm (length 16) or yyyy-MM-dd (length 10) 226 status = U_INVALID_FORMAT_ERROR; 227 return 0; 228 } 229 230 int32_t year = 0, month = 0, day = 0, hour = 0, min = 0, n; 231 int32_t idx; 232 233 // "yyyy" (0 - 3) 234 for (idx = 0; idx <= 3 && U_SUCCESS(status); idx++) { 235 n = ASCII_DIGIT((int32_t)text[idx]); 236 if (n >= 0) { 237 year = 10*year + n; 238 } else { 239 status = U_INVALID_FORMAT_ERROR; 240 } 241 } 242 // "MM" (5 - 6) 243 for (idx = 5; idx <= 6 && U_SUCCESS(status); idx++) { 244 n = ASCII_DIGIT((int32_t)text[idx]); 245 if (n >= 0) { 246 month = 10*month + n; 247 } else { 248 status = U_INVALID_FORMAT_ERROR; 249 } 250 } 251 // "dd" (8 - 9) 252 for (idx = 8; idx <= 9 && U_SUCCESS(status); idx++) { 253 n = ASCII_DIGIT((int32_t)text[idx]); 254 if (n >= 0) { 255 day = 10*day + n; 256 } else { 257 status = U_INVALID_FORMAT_ERROR; 258 } 259 } 260 if (len == 16) { 261 // "HH" (11 - 12) 262 for (idx = 11; idx <= 12 && U_SUCCESS(status); idx++) { 263 n = ASCII_DIGIT((int32_t)text[idx]); 264 if (n >= 0) { 265 hour = 10*hour + n; 266 } else { 267 status = U_INVALID_FORMAT_ERROR; 268 } 269 } 270 // "mm" (14 - 15) 271 for (idx = 14; idx <= 15 && U_SUCCESS(status); idx++) { 272 n = ASCII_DIGIT((int32_t)text[idx]); 273 if (n >= 0) { 274 min = 10*min + n; 275 } else { 276 status = U_INVALID_FORMAT_ERROR; 277 } 278 } 279 } 280 281 if (U_SUCCESS(status)) { 282 UDate date = Grego::fieldsToDay(year, month - 1, day) * U_MILLIS_PER_DAY 283 + hour * U_MILLIS_PER_HOUR + min * U_MILLIS_PER_MINUTE; 284 return date; 285 } 286 return 0; 287 } 288 289 UHashtable* 290 ZoneMeta::createCanonicalMap(void) { 291 UErrorCode status = U_ZERO_ERROR; 292 293 UHashtable *canonicalMap = NULL; 294 UResourceBundle *zoneFormatting = NULL; 295 UResourceBundle *tzitem = NULL; 296 UResourceBundle *aliases = NULL; 297 298 StringEnumeration* tzenum = NULL; 299 int32_t numZones; 300 301 canonicalMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); 302 if (U_FAILURE(status)) { 303 return NULL; 304 } 305 // no key deleter 306 uhash_setValueDeleter(canonicalMap, deleteCanonicalMapEntry); 307 308 zoneFormatting = ures_openDirect(NULL, gSupplementalData, &status); 309 zoneFormatting = ures_getByKey(zoneFormatting, gZoneFormattingTag, zoneFormatting, &status); 310 if (U_FAILURE(status)) { 311 goto error_cleanup; 312 } 313 314 while (ures_hasNext(zoneFormatting)) { 315 tzitem = ures_getNextResource(zoneFormatting, tzitem, &status); 316 if (U_FAILURE(status)) { 317 status = U_ZERO_ERROR; 318 continue; 319 } 320 if (ures_getType(tzitem) != URES_TABLE) { 321 continue; 322 } 323 324 int32_t canonicalLen; 325 const UChar *canonical = ures_getStringByKey(tzitem, gCanonicalTag, &canonicalLen, &status); 326 if (U_FAILURE(status)) { 327 status = U_ZERO_ERROR; 328 continue; 329 } 330 331 int32_t territoryLen; 332 const UChar *territory = ures_getStringByKey(tzitem, gTerritoryTag, &territoryLen, &status); 333 if (U_FAILURE(status)) { 334 territory = NULL; 335 status = U_ZERO_ERROR; 336 } 337 338 // Create canonical map entry 339 CanonicalMapEntry *entry = (CanonicalMapEntry*)uprv_malloc(sizeof(CanonicalMapEntry)); 340 if (entry == NULL) { 341 status = U_MEMORY_ALLOCATION_ERROR; 342 goto error_cleanup; 343 } 344 entry->id = canonical; 345 if (territory == NULL || u_strcmp(territory, gWorld) == 0) { 346 entry->country = NULL; 347 } else { 348 entry->country = territory; 349 } 350 351 // Put this entry in the hashtable. Since this hashtable has no key deleter, 352 // key is treated as const, but must be passed as non-const. 353 uhash_put(canonicalMap, (UChar*)canonical, entry, &status); 354 if (U_FAILURE(status)) { 355 goto error_cleanup; 356 } 357 358 // Get aliases 359 aliases = ures_getByKey(tzitem, gAliasesTag, aliases, &status); 360 if (U_FAILURE(status)) { 361 // No aliases 362 status = U_ZERO_ERROR; 363 continue; 364 } 365 366 while (ures_hasNext(aliases)) { 367 const UChar* alias = ures_getNextString(aliases, NULL, NULL, &status); 368 if (U_FAILURE(status)) { 369 status = U_ZERO_ERROR; 370 continue; 371 } 372 // Create canonical map entry for this alias 373 entry = (CanonicalMapEntry*)uprv_malloc(sizeof(CanonicalMapEntry)); 374 if (entry == NULL) { 375 status = U_MEMORY_ALLOCATION_ERROR; 376 goto error_cleanup; 377 } 378 entry->id = canonical; 379 if (territory == NULL || u_strcmp(territory, gWorld) == 0) { 380 entry->country = NULL; 381 } else { 382 entry->country = territory; 383 } 384 385 // Put this entry in the hashtable. Since this hashtable has no key deleter, 386 // key is treated as const, but must be passed as non-const. 387 uhash_put(canonicalMap, (UChar*)alias, entry, &status); 388 if (U_FAILURE(status)) { 389 goto error_cleanup; 390 } 391 } 392 } 393 394 // Some available Olson zones are not included in CLDR data (such as Asia/Riyadh87). 395 // Also, when we update Olson tzdata, new zones may be added. 396 // This code scans all available zones in zoneinfo.res, and if any of them are 397 // missing, add them to the map. 398 tzenum = TimeZone::createEnumeration(); 399 numZones = tzenum->count(status); 400 if (U_SUCCESS(status)) { 401 int32_t i; 402 for (i = 0; i < numZones; i++) { 403 const UnicodeString *zone = tzenum->snext(status); 404 if (U_FAILURE(status)) { 405 // We should not get here. 406 status = U_ZERO_ERROR; 407 continue; 408 } 409 UChar zoneUChars[ZID_KEY_MAX]; 410 int32_t zoneUCharsLen = zone->extract(zoneUChars, ZID_KEY_MAX, status) + 1; // Add one for NUL termination 411 if (U_FAILURE(status) || status==U_STRING_NOT_TERMINATED_WARNING) { 412 status = U_ZERO_ERROR; 413 continue; // zone id is too long to extract 414 } 415 CanonicalMapEntry *entry = (CanonicalMapEntry*)uhash_get(canonicalMap, zoneUChars); 416 if (entry) { 417 // Already included in CLDR data 418 continue; 419 } 420 // Not in CLDR data, but it could be new one whose alias is available 421 // in CLDR. 422 int32_t nTzdataEquivalent = TimeZone::countEquivalentIDs(*zone); 423 int32_t j; 424 for (j = 0; j < nTzdataEquivalent; j++) { 425 UnicodeString alias = TimeZone::getEquivalentID(*zone, j); 426 if (alias == *zone) { 427 continue; 428 } 429 UChar aliasUChars[ZID_KEY_MAX]; 430 alias.extract(aliasUChars, ZID_KEY_MAX, status); 431 if (U_FAILURE(status) || status==U_STRING_NOT_TERMINATED_WARNING) { 432 status = U_ZERO_ERROR; 433 continue; // zone id is too long to extract 434 } 435 entry = (CanonicalMapEntry*)uhash_get(canonicalMap, aliasUChars); 436 if (entry != NULL) { 437 break; 438 } 439 } 440 // Create a new map entry 441 CanonicalMapEntry* newEntry = (CanonicalMapEntry*)uprv_malloc(sizeof(CanonicalMapEntry)); 442 int32_t idLen; 443 if (newEntry == NULL) { 444 status = U_MEMORY_ALLOCATION_ERROR; 445 goto error_cleanup; 446 } 447 if (entry == NULL) { 448 // Set dereferenced zone ID as the canonical ID 449 UnicodeString derefZone; 450 TimeZone::dereferOlsonLink(*zone, derefZone); 451 if (derefZone.length() == 0) { 452 // It should never happen.. but just in case 453 derefZone = *zone; 454 } 455 idLen = derefZone.length() + 1; 456 newEntry->id = allocUStringInTable(idLen); 457 if (newEntry->id == NULL) { 458 status = U_MEMORY_ALLOCATION_ERROR; 459 uprv_free(newEntry); 460 goto error_cleanup; 461 } 462 // Copy NULL terminated string 463 derefZone.extract((UChar*)(newEntry->id), idLen, status); 464 if (U_FAILURE(status)) { 465 removeLastUStringFromTable(); 466 uprv_free(newEntry); 467 goto error_cleanup; 468 } 469 // No territory information available 470 newEntry->country = NULL; 471 } else { 472 // Duplicate the entry 473 newEntry->id = entry->id; 474 newEntry->country = entry->country; 475 } 476 477 // Put this entry in the hashtable 478 UChar *key = allocUStringInTable(zoneUCharsLen); 479 if (key == NULL) { 480 status = U_MEMORY_ALLOCATION_ERROR; 481 deleteCanonicalMapEntry(newEntry); 482 goto error_cleanup; 483 } 484 u_strncpy(key, zoneUChars, zoneUCharsLen); 485 uhash_put(canonicalMap, key, newEntry, &status); 486 if (U_FAILURE(status)) { 487 goto error_cleanup; 488 } 489 } 490 } 491 492 normal_cleanup: 493 ures_close(aliases); 494 ures_close(tzitem); 495 ures_close(zoneFormatting); 496 delete tzenum; 497 return canonicalMap; 498 499 error_cleanup: 500 if (canonicalMap != NULL) { 501 uhash_close(canonicalMap); 502 canonicalMap = NULL; 503 } 504 goto normal_cleanup; 505 } 506 507 /* 508 * Creating Olson tzid to metazone mappings from resource (3.8.1 and beyond) 509 */ 510 UHashtable* 511 ZoneMeta::createOlsonToMetaMap(void) { 512 UErrorCode status = U_ZERO_ERROR; 513 514 UHashtable *olsonToMeta = NULL; 515 UResourceBundle *metazoneMappings = NULL; 516 UResourceBundle *zoneItem = NULL; 517 UResourceBundle *mz = NULL; 518 StringEnumeration *tzids = NULL; 519 520 olsonToMeta = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); 521 if (U_FAILURE(status)) { 522 return NULL; 523 } 524 uhash_setKeyDeleter(olsonToMeta, deleteUCharString); 525 uhash_setValueDeleter(olsonToMeta, deleteUVector); 526 527 // Read metazone mappings from metazoneInfo bundle 528 metazoneMappings = ures_openDirect(NULL, gMetazoneInfo, &status); 529 metazoneMappings = ures_getByKey(metazoneMappings, gMetazoneMappings, metazoneMappings, &status); 530 if (U_FAILURE(status)) { 531 goto error_cleanup; 532 } 533 534 // Walk through all canonical tzids 535 char zidkey[ZID_KEY_MAX]; 536 537 tzids = TimeZone::createEnumeration(); 538 const UnicodeString *tzid; 539 while ((tzid = tzids->snext(status))) { 540 if (U_FAILURE(status)) { 541 goto error_cleanup; 542 } 543 // We may skip aliases, because the bundle 544 // contains only canonical IDs. For now, try 545 // all of them. 546 tzid->extract(0, tzid->length(), zidkey, sizeof(zidkey), US_INV); 547 zidkey[sizeof(zidkey)-1] = 0; // NULL terminate just in case. 548 549 // Replace '/' with ':' 550 UBool foundSep = FALSE; 551 char *p = zidkey; 552 while (*p) { 553 if (*p == '/') { 554 *p = ':'; 555 foundSep = TRUE; 556 } 557 p++; 558 } 559 if (!foundSep) { 560 // A valid time zone key has at least one separator 561 continue; 562 } 563 564 zoneItem = ures_getByKey(metazoneMappings, zidkey, zoneItem, &status); 565 if (U_FAILURE(status)) { 566 status = U_ZERO_ERROR; 567 continue; 568 } 569 570 UVector *mzMappings = NULL; 571 while (ures_hasNext(zoneItem)) { 572 mz = ures_getNextResource(zoneItem, mz, &status); 573 const UChar *mz_name = ures_getStringByIndex(mz, 0, NULL, &status); 574 const UChar *mz_from = ures_getStringByIndex(mz, 1, NULL, &status); 575 const UChar *mz_to = ures_getStringByIndex(mz, 2, NULL, &status); 576 577 if(U_FAILURE(status)){ 578 status = U_ZERO_ERROR; 579 continue; 580 } 581 // We do not want to use SimpleDateformat to parse boundary dates, 582 // because this code could be triggered by the initialization code 583 // used by SimpleDateFormat. 584 UDate from = parseDate(mz_from, status); 585 UDate to = parseDate(mz_to, status); 586 if (U_FAILURE(status)) { 587 status = U_ZERO_ERROR; 588 continue; 589 } 590 591 OlsonToMetaMappingEntry *entry = (OlsonToMetaMappingEntry*)uprv_malloc(sizeof(OlsonToMetaMappingEntry)); 592 if (entry == NULL) { 593 status = U_MEMORY_ALLOCATION_ERROR; 594 break; 595 } 596 entry->mzid = mz_name; 597 entry->from = from; 598 entry->to = to; 599 600 if (mzMappings == NULL) { 601 mzMappings = new UVector(deleteOlsonToMetaMappingEntry, NULL, status); 602 if (U_FAILURE(status)) { 603 delete mzMappings; 604 deleteOlsonToMetaMappingEntry(entry); 605 uprv_free(entry); 606 break; 607 } 608 } 609 610 mzMappings->addElement(entry, status); 611 if (U_FAILURE(status)) { 612 break; 613 } 614 } 615 616 if (U_FAILURE(status)) { 617 if (mzMappings != NULL) { 618 delete mzMappings; 619 } 620 goto error_cleanup; 621 } 622 if (mzMappings != NULL) { 623 // Add to hashtable 624 int32_t tzidLen = tzid->length() + 1; // Add one for NUL terminator 625 UChar *key = (UChar*)uprv_malloc(tzidLen * sizeof(UChar)); 626 if (key == NULL) { 627 status = U_MEMORY_ALLOCATION_ERROR; 628 delete mzMappings; 629 goto error_cleanup; 630 } 631 tzid->extract(key, tzidLen, status); 632 uhash_put(olsonToMeta, key, mzMappings, &status); 633 if (U_FAILURE(status)) { 634 goto error_cleanup; 635 } 636 } 637 } 638 639 normal_cleanup: 640 if (tzids != NULL) { 641 delete tzids; 642 } 643 ures_close(zoneItem); 644 ures_close(mz); 645 ures_close(metazoneMappings); 646 return olsonToMeta; 647 648 error_cleanup: 649 if (olsonToMeta != NULL) { 650 uhash_close(olsonToMeta); 651 olsonToMeta = NULL; 652 } 653 goto normal_cleanup; 654 } 655 656 UHashtable* 657 ZoneMeta::createMetaToOlsonMap(void) { 658 UErrorCode status = U_ZERO_ERROR; 659 660 UHashtable *metaToOlson = NULL; 661 UResourceBundle *metazones = NULL; 662 UResourceBundle *mz = NULL; 663 664 metaToOlson = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); 665 if (U_FAILURE(status)) { 666 return NULL; 667 } 668 uhash_setKeyDeleter(metaToOlson, deleteUCharString); 669 uhash_setValueDeleter(metaToOlson, deleteUVector); 670 671 metazones = ures_openDirect(NULL, gSupplementalData, &status); 672 metazones = ures_getByKey(metazones, gMapTimezonesTag, metazones, &status); 673 metazones = ures_getByKey(metazones, gMetazonesTag, metazones, &status); 674 if (U_FAILURE(status)) { 675 goto error_cleanup; 676 } 677 678 while (ures_hasNext(metazones)) { 679 mz = ures_getNextResource(metazones, mz, &status); 680 if (U_FAILURE(status)) { 681 status = U_ZERO_ERROR; 682 continue; 683 } 684 const char *mzkey = ures_getKey(mz); 685 if (uprv_strncmp(mzkey, gMetazoneIdPrefix, MZID_PREFIX_LEN) == 0) { 686 const char *mzid = mzkey + MZID_PREFIX_LEN; 687 const char *territory = uprv_strrchr(mzid, '_'); 688 int32_t mzidLen = 0; 689 int32_t territoryLen = 0; 690 if (territory) { 691 mzidLen = territory - mzid; 692 territory++; 693 territoryLen = uprv_strlen(territory); 694 } 695 if (mzidLen > 0 && territoryLen > 0) { 696 int32_t tzidLen; 697 const UChar *tzid = ures_getStringByIndex(mz, 0, &tzidLen, &status); 698 if (U_SUCCESS(status)) { 699 // Create MetaToOlsonMappingEntry 700 MetaToOlsonMappingEntry *entry = (MetaToOlsonMappingEntry*)uprv_malloc(sizeof(MetaToOlsonMappingEntry)); 701 if (entry == NULL) { 702 status = U_MEMORY_ALLOCATION_ERROR; 703 goto error_cleanup; 704 } 705 entry->id = tzid; 706 entry->territory = (UChar*)uprv_malloc((territoryLen + 1) * sizeof(UChar)); 707 if (entry->territory == NULL) { 708 status = U_MEMORY_ALLOCATION_ERROR; 709 uprv_free(entry); 710 goto error_cleanup; 711 } 712 u_charsToUChars(territory, entry->territory, territoryLen + 1); 713 714 // Check if mapping entries for metazone is already available 715 if (mzidLen < ZID_KEY_MAX) { 716 UChar mzidUChars[ZID_KEY_MAX]; 717 u_charsToUChars(mzid, mzidUChars, mzidLen); 718 mzidUChars[mzidLen++] = 0; // Add NUL terminator 719 UVector *tzMappings = (UVector*)uhash_get(metaToOlson, mzidUChars); 720 if (tzMappings == NULL) { 721 // Create new UVector and put it into the hashtable 722 tzMappings = new UVector(deleteMetaToOlsonMappingEntry, NULL, status); 723 if (U_FAILURE(status)) { 724 deleteMetaToOlsonMappingEntry(entry); 725 goto error_cleanup; 726 } 727 UChar *key = (UChar*)uprv_malloc(mzidLen * sizeof(UChar)); 728 if (key == NULL) { 729 status = U_MEMORY_ALLOCATION_ERROR; 730 delete tzMappings; 731 deleteMetaToOlsonMappingEntry(entry); 732 goto error_cleanup; 733 } 734 u_strncpy(key, mzidUChars, mzidLen); 735 uhash_put(metaToOlson, key, tzMappings, &status); 736 if (U_FAILURE(status)) { 737 goto error_cleanup; 738 } 739 } 740 tzMappings->addElement(entry, status); 741 if (U_FAILURE(status)) { 742 goto error_cleanup; 743 } 744 } else { 745 deleteMetaToOlsonMappingEntry(entry); 746 } 747 } else { 748 status = U_ZERO_ERROR; 749 } 750 } 751 } 752 } 753 754 normal_cleanup: 755 ures_close(mz); 756 ures_close(metazones); 757 return metaToOlson; 758 759 error_cleanup: 760 if (metaToOlson != NULL) { 761 uhash_close(metaToOlson); 762 metaToOlson = NULL; 763 } 764 goto normal_cleanup; 765 } 766 767 /* 768 * Initialize global objects 769 */ 770 void 771 ZoneMeta::initializeCanonicalMap(void) { 772 UBool initialized; 773 UMTX_CHECK(&gZoneMetaLock, gCanonicalMapInitialized, initialized); 774 if (initialized) { 775 return; 776 } 777 // Initialize hash table 778 UHashtable *tmpCanonicalMap = createCanonicalMap(); 779 780 umtx_lock(&gZoneMetaLock); 781 if (!gCanonicalMapInitialized) { 782 gCanonicalMap = tmpCanonicalMap; 783 tmpCanonicalMap = NULL; 784 gCanonicalMapInitialized = TRUE; 785 } 786 umtx_unlock(&gZoneMetaLock); 787 788 // OK to call the following multiple times with the same function 789 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup); 790 if (tmpCanonicalMap != NULL) { 791 uhash_close(tmpCanonicalMap); 792 } 793 } 794 795 void 796 ZoneMeta::initializeOlsonToMeta(void) { 797 UBool initialized; 798 UMTX_CHECK(&gZoneMetaLock, gOlsonToMetaInitialized, initialized); 799 if (initialized) { 800 return; 801 } 802 // Initialize hash tables 803 UHashtable *tmpOlsonToMeta = createOlsonToMetaMap(); 804 805 umtx_lock(&gZoneMetaLock); 806 if (!gOlsonToMetaInitialized) { 807 gOlsonToMeta = tmpOlsonToMeta; 808 tmpOlsonToMeta = NULL; 809 gOlsonToMetaInitialized = TRUE; 810 } 811 umtx_unlock(&gZoneMetaLock); 812 813 // OK to call the following multiple times with the same function 814 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup); 815 if (tmpOlsonToMeta != NULL) { 816 uhash_close(tmpOlsonToMeta); 817 } 818 } 819 820 void 821 ZoneMeta::initializeMetaToOlson(void) { 822 UBool initialized; 823 UMTX_CHECK(&gZoneMetaLock, gMetaToOlsonInitialized, initialized); 824 if (initialized) { 825 return; 826 } 827 // Initialize hash table 828 UHashtable *tmpMetaToOlson = createMetaToOlsonMap(); 829 830 umtx_lock(&gZoneMetaLock); 831 if (!gMetaToOlsonInitialized) { 832 gMetaToOlson = tmpMetaToOlson; 833 tmpMetaToOlson = NULL; 834 gMetaToOlsonInitialized = TRUE; 835 } 836 umtx_unlock(&gZoneMetaLock); 837 838 // OK to call the following multiple times with the same function 839 ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup); 840 if (tmpMetaToOlson != NULL) { 841 uhash_close(tmpMetaToOlson); 842 } 843 } 844 845 UnicodeString& U_EXPORT2 846 ZoneMeta::getCanonicalSystemID(const UnicodeString &tzid, UnicodeString &systemID, UErrorCode& status) { 847 const CanonicalMapEntry *entry = getCanonicalInfo(tzid); 848 if (entry != NULL) { 849 systemID.setTo(entry->id); 850 } else { 851 status = U_ILLEGAL_ARGUMENT_ERROR; 852 } 853 return systemID; 854 } 855 856 UnicodeString& U_EXPORT2 857 ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &canonicalCountry) { 858 const CanonicalMapEntry *entry = getCanonicalInfo(tzid); 859 if (entry != NULL && entry->country != NULL) { 860 canonicalCountry.setTo(entry->country); 861 } else { 862 // Use the input tzid 863 canonicalCountry.remove(); 864 } 865 return canonicalCountry; 866 } 867 868 const CanonicalMapEntry* U_EXPORT2 869 ZoneMeta::getCanonicalInfo(const UnicodeString &tzid) { 870 initializeCanonicalMap(); 871 CanonicalMapEntry *entry = NULL; 872 if (gCanonicalMap != NULL) { 873 UErrorCode status = U_ZERO_ERROR; 874 UChar tzidUChars[ZID_KEY_MAX]; 875 tzid.extract(tzidUChars, ZID_KEY_MAX, status); 876 if (U_SUCCESS(status) && status!=U_STRING_NOT_TERMINATED_WARNING) { 877 entry = (CanonicalMapEntry*)uhash_get(gCanonicalMap, tzidUChars); 878 } 879 } 880 return entry; 881 } 882 883 UnicodeString& U_EXPORT2 884 ZoneMeta::getSingleCountry(const UnicodeString &tzid, UnicodeString &country) { 885 UErrorCode status = U_ZERO_ERROR; 886 887 // Get canonical country for the zone 888 getCanonicalCountry(tzid, country); 889 890 if (!country.isEmpty()) { 891 UResourceBundle *supplementalDataBundle = ures_openDirect(NULL, gSupplementalData, &status); 892 UResourceBundle *zoneFormatting = ures_getByKey(supplementalDataBundle, gZoneFormattingTag, NULL, &status); 893 UResourceBundle *multizone = ures_getByKey(zoneFormatting, gMultizoneTag, NULL, &status); 894 895 if (U_SUCCESS(status)) { 896 while (ures_hasNext(multizone)) { 897 int32_t len; 898 const UChar* multizoneCountry = ures_getNextString(multizone, &len, NULL, &status); 899 if (country.compare(multizoneCountry, len) == 0) { 900 // Included in the multizone country list 901 country.remove(); 902 break; 903 } 904 } 905 } 906 907 ures_close(multizone); 908 ures_close(zoneFormatting); 909 ures_close(supplementalDataBundle); 910 } 911 912 return country; 913 } 914 915 UnicodeString& U_EXPORT2 916 ZoneMeta::getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &result) { 917 UBool isSet = FALSE; 918 const UVector *mappings = getMetazoneMappings(tzid); 919 if (mappings != NULL) { 920 for (int32_t i = 0; i < mappings->size(); i++) { 921 OlsonToMetaMappingEntry *mzm = (OlsonToMetaMappingEntry*)mappings->elementAt(i); 922 if (mzm->from <= date && mzm->to > date) { 923 result.setTo(mzm->mzid, -1); 924 isSet = TRUE; 925 break; 926 } 927 } 928 } 929 if (!isSet) { 930 result.remove(); 931 } 932 return result; 933 } 934 935 const UVector* U_EXPORT2 936 ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) { 937 initializeOlsonToMeta(); 938 const UVector *result = NULL; 939 if (gOlsonToMeta != NULL) { 940 UErrorCode status = U_ZERO_ERROR; 941 UChar tzidUChars[ZID_KEY_MAX]; 942 tzid.extract(tzidUChars, ZID_KEY_MAX, status); 943 if (U_SUCCESS(status) && status!=U_STRING_NOT_TERMINATED_WARNING) { 944 result = (UVector*)uhash_get(gOlsonToMeta, tzidUChars); 945 } 946 } 947 return result; 948 } 949 950 UnicodeString& U_EXPORT2 951 ZoneMeta::getZoneIdByMetazone(const UnicodeString &mzid, const UnicodeString ®ion, UnicodeString &result) { 952 initializeMetaToOlson(); 953 UBool isSet = FALSE; 954 if (gMetaToOlson != NULL) { 955 UErrorCode status = U_ZERO_ERROR; 956 UChar mzidUChars[ZID_KEY_MAX]; 957 mzid.extract(mzidUChars, ZID_KEY_MAX, status); 958 if (U_SUCCESS(status) && status!=U_STRING_NOT_TERMINATED_WARNING) { 959 UVector *mappings = (UVector*)uhash_get(gMetaToOlson, mzidUChars); 960 if (mappings != NULL) { 961 // Find a preferred time zone for the given region. 962 for (int32_t i = 0; i < mappings->size(); i++) { 963 MetaToOlsonMappingEntry *olsonmap = (MetaToOlsonMappingEntry*)mappings->elementAt(i); 964 if (region.compare(olsonmap->territory, -1) == 0) { 965 result.setTo(olsonmap->id); 966 isSet = TRUE; 967 break; 968 } else if (u_strcmp(olsonmap->territory, gWorld) == 0) { 969 result.setTo(olsonmap->id); 970 isSet = TRUE; 971 } 972 } 973 } 974 } 975 } 976 if (!isSet) { 977 result.remove(); 978 } 979 return result; 980 } 981 982 983 U_NAMESPACE_END 984 985 #endif /* #if !UCONFIG_NO_FORMATTING */ 986