zonemeta.cpp 30 KB

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