tzgnames.cpp 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327
  1. // © 2016 and later: Unicode, Inc. and others.
  2. // License & terms of use: http://www.unicode.org/copyright.html
  3. /*
  4. *******************************************************************************
  5. * Copyright (C) 2011-2016, International Business Machines Corporation and
  6. * others. All Rights Reserved.
  7. *******************************************************************************
  8. */
  9. #include "unicode/utypes.h"
  10. #if !UCONFIG_NO_FORMATTING
  11. #include "tzgnames.h"
  12. #include "unicode/basictz.h"
  13. #include "unicode/locdspnm.h"
  14. #include "unicode/rbtz.h"
  15. #include "unicode/simpleformatter.h"
  16. #include "unicode/simpletz.h"
  17. #include "unicode/strenum.h"
  18. #include "unicode/vtzone.h"
  19. #include "bytesinkutil.h"
  20. #include "charstr.h"
  21. #include "cmemory.h"
  22. #include "cstring.h"
  23. #include "mutex.h"
  24. #include "uhash.h"
  25. #include "uassert.h"
  26. #include "umutex.h"
  27. #include "ulocimp.h"
  28. #include "uresimp.h"
  29. #include "ureslocs.h"
  30. #include "zonemeta.h"
  31. #include "tznames_impl.h"
  32. #include "olsontz.h"
  33. #include "ucln_in.h"
  34. U_NAMESPACE_BEGIN
  35. #define ZID_KEY_MAX 128
  36. static const char gZoneStrings[] = "zoneStrings";
  37. static const char gRegionFormatTag[] = "regionFormat";
  38. static const char gFallbackFormatTag[] = "fallbackFormat";
  39. static const char16_t gEmpty[] = {0x00};
  40. static const char16_t gDefRegionPattern[] = {0x7B, 0x30, 0x7D, 0x00}; // "{0}"
  41. static const char16_t gDefFallbackPattern[] = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})"
  42. static const double kDstCheckRange = (double)184*U_MILLIS_PER_DAY;
  43. U_CDECL_BEGIN
  44. typedef struct PartialLocationKey {
  45. const char16_t* tzID;
  46. const char16_t* mzID;
  47. UBool isLong;
  48. } PartialLocationKey;
  49. /**
  50. * Hash function for partial location name hash key
  51. */
  52. static int32_t U_CALLCONV
  53. hashPartialLocationKey(const UHashTok key) {
  54. // <tzID>&<mzID>#[L|S]
  55. PartialLocationKey *p = (PartialLocationKey *)key.pointer;
  56. UnicodeString str(p->tzID);
  57. str.append((char16_t)0x26)
  58. .append(p->mzID, -1)
  59. .append((char16_t)0x23)
  60. .append((char16_t)(p->isLong ? 0x4C : 0x53));
  61. return str.hashCode();
  62. }
  63. /**
  64. * Comparer for partial location name hash key
  65. */
  66. static UBool U_CALLCONV
  67. comparePartialLocationKey(const UHashTok key1, const UHashTok key2) {
  68. PartialLocationKey *p1 = (PartialLocationKey *)key1.pointer;
  69. PartialLocationKey *p2 = (PartialLocationKey *)key2.pointer;
  70. if (p1 == p2) {
  71. return true;
  72. }
  73. if (p1 == nullptr || p2 == nullptr) {
  74. return false;
  75. }
  76. // We just check identity of tzID/mzID
  77. return (p1->tzID == p2->tzID && p1->mzID == p2->mzID && p1->isLong == p2->isLong);
  78. }
  79. /**
  80. * Deleter for GNameInfo
  81. */
  82. static void U_CALLCONV
  83. deleteGNameInfo(void *obj) {
  84. uprv_free(obj);
  85. }
  86. /**
  87. * GNameInfo stores zone name information in the local trie
  88. */
  89. typedef struct GNameInfo {
  90. UTimeZoneGenericNameType type;
  91. const char16_t* tzID;
  92. } ZNameInfo;
  93. /**
  94. * GMatchInfo stores zone name match information used by find method
  95. */
  96. typedef struct GMatchInfo {
  97. const GNameInfo* gnameInfo;
  98. int32_t matchLength;
  99. UTimeZoneFormatTimeType timeType;
  100. } ZMatchInfo;
  101. U_CDECL_END
  102. // ---------------------------------------------------
  103. // The class stores time zone generic name match information
  104. // ---------------------------------------------------
  105. class TimeZoneGenericNameMatchInfo : public UMemory {
  106. public:
  107. TimeZoneGenericNameMatchInfo(UVector* matches);
  108. ~TimeZoneGenericNameMatchInfo();
  109. int32_t size() const;
  110. UTimeZoneGenericNameType getGenericNameType(int32_t index) const;
  111. int32_t getMatchLength(int32_t index) const;
  112. UnicodeString& getTimeZoneID(int32_t index, UnicodeString& tzID) const;
  113. private:
  114. UVector* fMatches; // vector of MatchEntry
  115. };
  116. TimeZoneGenericNameMatchInfo::TimeZoneGenericNameMatchInfo(UVector* matches)
  117. : fMatches(matches) {
  118. }
  119. TimeZoneGenericNameMatchInfo::~TimeZoneGenericNameMatchInfo() {
  120. if (fMatches != nullptr) {
  121. delete fMatches;
  122. }
  123. }
  124. int32_t
  125. TimeZoneGenericNameMatchInfo::size() const {
  126. if (fMatches == nullptr) {
  127. return 0;
  128. }
  129. return fMatches->size();
  130. }
  131. UTimeZoneGenericNameType
  132. TimeZoneGenericNameMatchInfo::getGenericNameType(int32_t index) const {
  133. GMatchInfo *minfo = (GMatchInfo *)fMatches->elementAt(index);
  134. if (minfo != nullptr) {
  135. return static_cast<UTimeZoneGenericNameType>(minfo->gnameInfo->type);
  136. }
  137. return UTZGNM_UNKNOWN;
  138. }
  139. int32_t
  140. TimeZoneGenericNameMatchInfo::getMatchLength(int32_t index) const {
  141. ZMatchInfo *minfo = (ZMatchInfo *)fMatches->elementAt(index);
  142. if (minfo != nullptr) {
  143. return minfo->matchLength;
  144. }
  145. return -1;
  146. }
  147. UnicodeString&
  148. TimeZoneGenericNameMatchInfo::getTimeZoneID(int32_t index, UnicodeString& tzID) const {
  149. GMatchInfo *minfo = (GMatchInfo *)fMatches->elementAt(index);
  150. if (minfo != nullptr && minfo->gnameInfo->tzID != nullptr) {
  151. tzID.setTo(true, minfo->gnameInfo->tzID, -1);
  152. } else {
  153. tzID.setToBogus();
  154. }
  155. return tzID;
  156. }
  157. // ---------------------------------------------------
  158. // GNameSearchHandler
  159. // ---------------------------------------------------
  160. class GNameSearchHandler : public TextTrieMapSearchResultHandler {
  161. public:
  162. GNameSearchHandler(uint32_t types);
  163. virtual ~GNameSearchHandler();
  164. UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) override;
  165. UVector* getMatches(int32_t& maxMatchLen);
  166. private:
  167. uint32_t fTypes;
  168. UVector* fResults;
  169. int32_t fMaxMatchLen;
  170. };
  171. GNameSearchHandler::GNameSearchHandler(uint32_t types)
  172. : fTypes(types), fResults(nullptr), fMaxMatchLen(0) {
  173. }
  174. GNameSearchHandler::~GNameSearchHandler() {
  175. if (fResults != nullptr) {
  176. delete fResults;
  177. }
  178. }
  179. UBool
  180. GNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
  181. if (U_FAILURE(status)) {
  182. return false;
  183. }
  184. if (node->hasValues()) {
  185. int32_t valuesCount = node->countValues();
  186. for (int32_t i = 0; i < valuesCount; i++) {
  187. GNameInfo *nameinfo = (ZNameInfo *)node->getValue(i);
  188. if (nameinfo == nullptr) {
  189. break;
  190. }
  191. if ((nameinfo->type & fTypes) != 0) {
  192. // matches a requested type
  193. if (fResults == nullptr) {
  194. LocalPointer<UVector> lpResults(new UVector(uprv_free, nullptr, status), status);
  195. if (U_FAILURE(status)) {
  196. return false;
  197. }
  198. fResults = lpResults.orphan();
  199. }
  200. GMatchInfo *gmatch = (GMatchInfo *)uprv_malloc(sizeof(GMatchInfo));
  201. if (gmatch == nullptr) {
  202. status = U_MEMORY_ALLOCATION_ERROR;
  203. return false;
  204. }
  205. // add the match to the vector
  206. gmatch->gnameInfo = nameinfo;
  207. gmatch->matchLength = matchLength;
  208. gmatch->timeType = UTZFMT_TIME_TYPE_UNKNOWN;
  209. fResults->adoptElement(gmatch, status);
  210. if (U_FAILURE(status)) {
  211. return false;
  212. }
  213. if (matchLength > fMaxMatchLen) {
  214. fMaxMatchLen = matchLength;
  215. }
  216. }
  217. }
  218. }
  219. return true;
  220. }
  221. UVector*
  222. GNameSearchHandler::getMatches(int32_t& maxMatchLen) {
  223. // give the ownership to the caller
  224. UVector *results = fResults;
  225. maxMatchLen = fMaxMatchLen;
  226. // reset
  227. fResults = nullptr;
  228. fMaxMatchLen = 0;
  229. return results;
  230. }
  231. static UMutex gLock;
  232. class TZGNCore : public UMemory {
  233. public:
  234. TZGNCore(const Locale& locale, UErrorCode& status);
  235. virtual ~TZGNCore();
  236. UnicodeString& getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,
  237. UDate date, UnicodeString& name) const;
  238. UnicodeString& getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const;
  239. int32_t findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
  240. UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const;
  241. private:
  242. Locale fLocale;
  243. const TimeZoneNames* fTimeZoneNames;
  244. UHashtable* fLocationNamesMap;
  245. UHashtable* fPartialLocationNamesMap;
  246. SimpleFormatter fRegionFormat;
  247. SimpleFormatter fFallbackFormat;
  248. LocaleDisplayNames* fLocaleDisplayNames;
  249. ZNStringPool fStringPool;
  250. TextTrieMap fGNamesTrie;
  251. UBool fGNamesTrieFullyLoaded;
  252. char fTargetRegion[ULOC_COUNTRY_CAPACITY];
  253. void initialize(const Locale& locale, UErrorCode& status);
  254. void cleanup();
  255. void loadStrings(const UnicodeString& tzCanonicalID);
  256. const char16_t* getGenericLocationName(const UnicodeString& tzCanonicalID);
  257. UnicodeString& formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type,
  258. UDate date, UnicodeString& name) const;
  259. UnicodeString& getPartialLocationName(const UnicodeString& tzCanonicalID,
  260. const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName,
  261. UnicodeString& name) const;
  262. const char16_t* getPartialLocationName(const UnicodeString& tzCanonicalID,
  263. const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName);
  264. TimeZoneGenericNameMatchInfo* findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
  265. TimeZoneNames::MatchInfoCollection* findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
  266. };
  267. // ---------------------------------------------------
  268. // TZGNCore - core implementation of TimeZoneGenericNames
  269. //
  270. // TimeZoneGenericNames is parallel to TimeZoneNames,
  271. // but handles run-time generated time zone names.
  272. // This is the main part of this module.
  273. // ---------------------------------------------------
  274. TZGNCore::TZGNCore(const Locale& locale, UErrorCode& status)
  275. : fLocale(locale),
  276. fTimeZoneNames(nullptr),
  277. fLocationNamesMap(nullptr),
  278. fPartialLocationNamesMap(nullptr),
  279. fLocaleDisplayNames(nullptr),
  280. fStringPool(status),
  281. fGNamesTrie(true, deleteGNameInfo),
  282. fGNamesTrieFullyLoaded(false) {
  283. initialize(locale, status);
  284. }
  285. TZGNCore::~TZGNCore() {
  286. cleanup();
  287. }
  288. void
  289. TZGNCore::initialize(const Locale& locale, UErrorCode& status) {
  290. if (U_FAILURE(status)) {
  291. return;
  292. }
  293. // TimeZoneNames
  294. fTimeZoneNames = TimeZoneNames::createInstance(locale, status);
  295. if (U_FAILURE(status)) {
  296. return;
  297. }
  298. // Initialize format patterns
  299. UnicodeString rpat(true, gDefRegionPattern, -1);
  300. UnicodeString fpat(true, gDefFallbackPattern, -1);
  301. UErrorCode tmpsts = U_ZERO_ERROR; // OK with fallback warning..
  302. UResourceBundle *zoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts);
  303. zoneStrings = ures_getByKeyWithFallback(zoneStrings, gZoneStrings, zoneStrings, &tmpsts);
  304. if (U_SUCCESS(tmpsts)) {
  305. const char16_t *regionPattern = ures_getStringByKeyWithFallback(zoneStrings, gRegionFormatTag, nullptr, &tmpsts);
  306. if (U_SUCCESS(tmpsts) && u_strlen(regionPattern) > 0) {
  307. rpat.setTo(regionPattern, -1);
  308. }
  309. tmpsts = U_ZERO_ERROR;
  310. const char16_t *fallbackPattern = ures_getStringByKeyWithFallback(zoneStrings, gFallbackFormatTag, nullptr, &tmpsts);
  311. if (U_SUCCESS(tmpsts) && u_strlen(fallbackPattern) > 0) {
  312. fpat.setTo(fallbackPattern, -1);
  313. }
  314. }
  315. ures_close(zoneStrings);
  316. fRegionFormat.applyPatternMinMaxArguments(rpat, 1, 1, status);
  317. fFallbackFormat.applyPatternMinMaxArguments(fpat, 2, 2, status);
  318. if (U_FAILURE(status)) {
  319. cleanup();
  320. return;
  321. }
  322. // locale display names
  323. fLocaleDisplayNames = LocaleDisplayNames::createInstance(locale);
  324. // hash table for names - no key/value deleters
  325. fLocationNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, nullptr, &status);
  326. if (U_FAILURE(status)) {
  327. cleanup();
  328. return;
  329. }
  330. fPartialLocationNamesMap = uhash_open(hashPartialLocationKey, comparePartialLocationKey, nullptr, &status);
  331. if (U_FAILURE(status)) {
  332. cleanup();
  333. return;
  334. }
  335. uhash_setKeyDeleter(fPartialLocationNamesMap, uprv_free);
  336. // no value deleter
  337. // target region
  338. const char* region = fLocale.getCountry();
  339. int32_t regionLen = static_cast<int32_t>(uprv_strlen(region));
  340. if (regionLen == 0) {
  341. CharString loc;
  342. {
  343. CharStringByteSink sink(&loc);
  344. ulocimp_addLikelySubtags(fLocale.getName(), sink, &status);
  345. }
  346. regionLen = uloc_getCountry(loc.data(), fTargetRegion, sizeof(fTargetRegion), &status);
  347. if (U_SUCCESS(status)) {
  348. fTargetRegion[regionLen] = 0;
  349. } else {
  350. cleanup();
  351. return;
  352. }
  353. } else if (regionLen < (int32_t)sizeof(fTargetRegion)) {
  354. uprv_strcpy(fTargetRegion, region);
  355. } else {
  356. fTargetRegion[0] = 0;
  357. }
  358. // preload generic names for the default zone
  359. TimeZone *tz = TimeZone::createDefault();
  360. const char16_t *tzID = ZoneMeta::getCanonicalCLDRID(*tz);
  361. if (tzID != nullptr) {
  362. loadStrings(UnicodeString(true, tzID, -1));
  363. }
  364. delete tz;
  365. }
  366. void
  367. TZGNCore::cleanup() {
  368. if (fLocaleDisplayNames != nullptr) {
  369. delete fLocaleDisplayNames;
  370. }
  371. if (fTimeZoneNames != nullptr) {
  372. delete fTimeZoneNames;
  373. }
  374. uhash_close(fLocationNamesMap);
  375. uhash_close(fPartialLocationNamesMap);
  376. }
  377. UnicodeString&
  378. TZGNCore::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const {
  379. name.setToBogus();
  380. switch (type) {
  381. case UTZGNM_LOCATION:
  382. {
  383. const char16_t* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz);
  384. if (tzCanonicalID != nullptr) {
  385. getGenericLocationName(UnicodeString(true, tzCanonicalID, -1), name);
  386. }
  387. }
  388. break;
  389. case UTZGNM_LONG:
  390. case UTZGNM_SHORT:
  391. formatGenericNonLocationName(tz, type, date, name);
  392. if (name.isEmpty()) {
  393. const char16_t* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz);
  394. if (tzCanonicalID != nullptr) {
  395. getGenericLocationName(UnicodeString(true, tzCanonicalID, -1), name);
  396. }
  397. }
  398. break;
  399. default:
  400. break;
  401. }
  402. return name;
  403. }
  404. UnicodeString&
  405. TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
  406. if (tzCanonicalID.isEmpty()) {
  407. name.setToBogus();
  408. return name;
  409. }
  410. const char16_t *locname = nullptr;
  411. TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
  412. umtx_lock(&gLock);
  413. {
  414. locname = nonConstThis->getGenericLocationName(tzCanonicalID);
  415. }
  416. umtx_unlock(&gLock);
  417. if (locname == nullptr) {
  418. name.setToBogus();
  419. } else {
  420. name.setTo(locname, u_strlen(locname));
  421. }
  422. return name;
  423. }
  424. /*
  425. * This method updates the cache and must be called with a lock
  426. */
  427. const char16_t*
  428. TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID) {
  429. U_ASSERT(!tzCanonicalID.isEmpty());
  430. if (tzCanonicalID.length() > ZID_KEY_MAX) {
  431. return nullptr;
  432. }
  433. UErrorCode status = U_ZERO_ERROR;
  434. char16_t tzIDKey[ZID_KEY_MAX + 1];
  435. int32_t tzIDKeyLen = tzCanonicalID.extract(tzIDKey, ZID_KEY_MAX + 1, status);
  436. U_ASSERT(status == U_ZERO_ERROR); // already checked length above
  437. tzIDKey[tzIDKeyLen] = 0;
  438. const char16_t *locname = (const char16_t *)uhash_get(fLocationNamesMap, tzIDKey);
  439. if (locname != nullptr) {
  440. // gEmpty indicate the name is not available
  441. if (locname == gEmpty) {
  442. return nullptr;
  443. }
  444. return locname;
  445. }
  446. // Construct location name
  447. UnicodeString name;
  448. UnicodeString usCountryCode;
  449. UBool isPrimary = false;
  450. ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode, &isPrimary);
  451. if (!usCountryCode.isEmpty()) {
  452. if (isPrimary) {
  453. // If this is the primary zone in the country, use the country name.
  454. char countryCode[ULOC_COUNTRY_CAPACITY];
  455. U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY);
  456. int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV);
  457. countryCode[ccLen] = 0;
  458. UnicodeString country;
  459. fLocaleDisplayNames->regionDisplayName(countryCode, country);
  460. fRegionFormat.format(country, name, status);
  461. } else {
  462. // If this is not the primary zone in the country,
  463. // use the exemplar city name.
  464. // getExemplarLocationName should return non-empty string
  465. // if the time zone is associated with a region
  466. UnicodeString city;
  467. fTimeZoneNames->getExemplarLocationName(tzCanonicalID, city);
  468. fRegionFormat.format(city, name, status);
  469. }
  470. if (U_FAILURE(status)) {
  471. return nullptr;
  472. }
  473. }
  474. locname = name.isEmpty() ? nullptr : fStringPool.get(name, status);
  475. if (U_SUCCESS(status)) {
  476. // Cache the result
  477. const char16_t* cacheID = ZoneMeta::findTimeZoneID(tzCanonicalID);
  478. U_ASSERT(cacheID != nullptr);
  479. if (locname == nullptr) {
  480. // gEmpty to indicate - no location name available
  481. uhash_put(fLocationNamesMap, (void *)cacheID, (void *)gEmpty, &status);
  482. } else {
  483. uhash_put(fLocationNamesMap, (void *)cacheID, (void *)locname, &status);
  484. if (U_FAILURE(status)) {
  485. locname = nullptr;
  486. } else {
  487. // put the name info into the trie
  488. GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo));
  489. if (nameinfo != nullptr) {
  490. nameinfo->type = UTZGNM_LOCATION;
  491. nameinfo->tzID = cacheID;
  492. fGNamesTrie.put(locname, nameinfo, status);
  493. }
  494. }
  495. }
  496. }
  497. return locname;
  498. }
  499. UnicodeString&
  500. TZGNCore::formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const {
  501. U_ASSERT(type == UTZGNM_LONG || type == UTZGNM_SHORT);
  502. name.setToBogus();
  503. const char16_t* uID = ZoneMeta::getCanonicalCLDRID(tz);
  504. if (uID == nullptr) {
  505. return name;
  506. }
  507. UnicodeString tzID(true, uID, -1);
  508. // Try to get a name from time zone first
  509. UTimeZoneNameType nameType = (type == UTZGNM_LONG) ? UTZNM_LONG_GENERIC : UTZNM_SHORT_GENERIC;
  510. fTimeZoneNames->getTimeZoneDisplayName(tzID, nameType, name);
  511. if (!name.isEmpty()) {
  512. return name;
  513. }
  514. // Try meta zone
  515. char16_t mzIDBuf[32];
  516. UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf));
  517. fTimeZoneNames->getMetaZoneID(tzID, date, mzID);
  518. if (!mzID.isEmpty()) {
  519. UErrorCode status = U_ZERO_ERROR;
  520. UBool useStandard = false;
  521. int32_t raw, sav;
  522. char16_t tmpNameBuf[ZONE_NAME_U16_MAX];
  523. tz.getOffset(date, false, raw, sav, status);
  524. if (U_FAILURE(status)) {
  525. return name;
  526. }
  527. if (sav == 0) {
  528. useStandard = true;
  529. TimeZone *tmptz = tz.clone();
  530. // Check if the zone actually uses daylight saving time around the time
  531. BasicTimeZone *btz = nullptr;
  532. if (dynamic_cast<OlsonTimeZone *>(tmptz) != nullptr
  533. || dynamic_cast<SimpleTimeZone *>(tmptz) != nullptr
  534. || dynamic_cast<RuleBasedTimeZone *>(tmptz) != nullptr
  535. || dynamic_cast<VTimeZone *>(tmptz) != nullptr) {
  536. btz = (BasicTimeZone*)tmptz;
  537. }
  538. if (btz != nullptr) {
  539. TimeZoneTransition before;
  540. UBool beforTrs = btz->getPreviousTransition(date, true, before);
  541. if (beforTrs
  542. && (date - before.getTime() < kDstCheckRange)
  543. && before.getFrom()->getDSTSavings() != 0) {
  544. useStandard = false;
  545. } else {
  546. TimeZoneTransition after;
  547. UBool afterTrs = btz->getNextTransition(date, false, after);
  548. if (afterTrs
  549. && (after.getTime() - date < kDstCheckRange)
  550. && after.getTo()->getDSTSavings() != 0) {
  551. useStandard = false;
  552. }
  553. }
  554. } else {
  555. // If not BasicTimeZone... only if the instance is not an ICU's implementation.
  556. // We may get a wrong answer in edge case, but it should practically work OK.
  557. tmptz->getOffset(date - kDstCheckRange, false, raw, sav, status);
  558. if (sav != 0) {
  559. useStandard = false;
  560. } else {
  561. tmptz->getOffset(date + kDstCheckRange, false, raw, sav, status);
  562. if (sav != 0){
  563. useStandard = false;
  564. }
  565. }
  566. if (U_FAILURE(status)) {
  567. delete tmptz;
  568. return name;
  569. }
  570. }
  571. delete tmptz;
  572. }
  573. if (useStandard) {
  574. UTimeZoneNameType stdNameType = (nameType == UTZNM_LONG_GENERIC)
  575. ? UTZNM_LONG_STANDARD : UTZNM_SHORT_STANDARD;
  576. UnicodeString stdName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf));
  577. fTimeZoneNames->getDisplayName(tzID, stdNameType, date, stdName);
  578. if (!stdName.isEmpty()) {
  579. name.setTo(stdName);
  580. // TODO: revisit this issue later
  581. // In CLDR, a same display name is used for both generic and standard
  582. // for some meta zones in some locales. This looks like a data bugs.
  583. // For now, we check if the standard name is different from its generic
  584. // name below.
  585. char16_t genNameBuf[ZONE_NAME_U16_MAX];
  586. UnicodeString mzGenericName(genNameBuf, 0, UPRV_LENGTHOF(genNameBuf));
  587. fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzGenericName);
  588. if (stdName.caseCompare(mzGenericName, 0) == 0) {
  589. name.setToBogus();
  590. }
  591. }
  592. }
  593. if (name.isEmpty()) {
  594. // Get a name from meta zone
  595. UnicodeString mzName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf));
  596. fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzName);
  597. if (!mzName.isEmpty()) {
  598. // Check if we need to use a partial location format.
  599. // This check is done by comparing offset with the meta zone's
  600. // golden zone at the given date.
  601. char16_t idBuf[32];
  602. UnicodeString goldenID(idBuf, 0, UPRV_LENGTHOF(idBuf));
  603. fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, goldenID);
  604. if (!goldenID.isEmpty() && goldenID != tzID) {
  605. TimeZone *goldenZone = TimeZone::createTimeZone(goldenID);
  606. int32_t raw1, sav1;
  607. // Check offset in the golden zone with wall time.
  608. // With getOffset(date, false, offsets1),
  609. // you may get incorrect results because of time overlap at DST->STD
  610. // transition.
  611. goldenZone->getOffset(date + raw + sav, true, raw1, sav1, status);
  612. delete goldenZone;
  613. if (U_SUCCESS(status)) {
  614. if (raw != raw1 || sav != sav1) {
  615. // Now we need to use a partial location format
  616. getPartialLocationName(tzID, mzID, (nameType == UTZNM_LONG_GENERIC), mzName, name);
  617. } else {
  618. name.setTo(mzName);
  619. }
  620. }
  621. } else {
  622. name.setTo(mzName);
  623. }
  624. }
  625. }
  626. }
  627. return name;
  628. }
  629. UnicodeString&
  630. TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID,
  631. const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName,
  632. UnicodeString& name) const {
  633. name.setToBogus();
  634. if (tzCanonicalID.isEmpty() || mzID.isEmpty() || mzDisplayName.isEmpty()) {
  635. return name;
  636. }
  637. const char16_t *uplname = nullptr;
  638. TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
  639. umtx_lock(&gLock);
  640. {
  641. uplname = nonConstThis->getPartialLocationName(tzCanonicalID, mzID, isLong, mzDisplayName);
  642. }
  643. umtx_unlock(&gLock);
  644. if (uplname == nullptr) {
  645. name.setToBogus();
  646. } else {
  647. name.setTo(true, uplname, -1);
  648. }
  649. return name;
  650. }
  651. /*
  652. * This method updates the cache and must be called with a lock
  653. */
  654. const char16_t*
  655. TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID,
  656. const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName) {
  657. U_ASSERT(!tzCanonicalID.isEmpty());
  658. U_ASSERT(!mzID.isEmpty());
  659. U_ASSERT(!mzDisplayName.isEmpty());
  660. PartialLocationKey key;
  661. key.tzID = ZoneMeta::findTimeZoneID(tzCanonicalID);
  662. key.mzID = ZoneMeta::findMetaZoneID(mzID);
  663. key.isLong = isLong;
  664. U_ASSERT(key.tzID != nullptr && key.mzID != nullptr);
  665. const char16_t* uplname = (const char16_t*)uhash_get(fPartialLocationNamesMap, (void *)&key);
  666. if (uplname != nullptr) {
  667. return uplname;
  668. }
  669. UnicodeString location;
  670. UnicodeString usCountryCode;
  671. ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode);
  672. if (!usCountryCode.isEmpty()) {
  673. char countryCode[ULOC_COUNTRY_CAPACITY];
  674. U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY);
  675. int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV);
  676. countryCode[ccLen] = 0;
  677. UnicodeString regionalGolden;
  678. fTimeZoneNames->getReferenceZoneID(mzID, countryCode, regionalGolden);
  679. if (tzCanonicalID == regionalGolden) {
  680. // Use country name
  681. fLocaleDisplayNames->regionDisplayName(countryCode, location);
  682. } else {
  683. // Otherwise, use exemplar city name
  684. fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);
  685. }
  686. } else {
  687. fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);
  688. if (location.isEmpty()) {
  689. // This could happen when the time zone is not associated with a country,
  690. // and its ID is not hierarchical, for example, CST6CDT.
  691. // We use the canonical ID itself as the location for this case.
  692. location.setTo(tzCanonicalID);
  693. }
  694. }
  695. UErrorCode status = U_ZERO_ERROR;
  696. UnicodeString name;
  697. fFallbackFormat.format(location, mzDisplayName, name, status);
  698. if (U_FAILURE(status)) {
  699. return nullptr;
  700. }
  701. uplname = fStringPool.get(name, status);
  702. if (U_SUCCESS(status)) {
  703. // Add the name to cache
  704. PartialLocationKey* cacheKey = (PartialLocationKey *)uprv_malloc(sizeof(PartialLocationKey));
  705. if (cacheKey != nullptr) {
  706. cacheKey->tzID = key.tzID;
  707. cacheKey->mzID = key.mzID;
  708. cacheKey->isLong = key.isLong;
  709. uhash_put(fPartialLocationNamesMap, (void *)cacheKey, (void *)uplname, &status);
  710. if (U_FAILURE(status)) {
  711. uprv_free(cacheKey);
  712. } else {
  713. // put the name to the local trie as well
  714. GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo));
  715. if (nameinfo != nullptr) {
  716. nameinfo->type = isLong ? UTZGNM_LONG : UTZGNM_SHORT;
  717. nameinfo->tzID = key.tzID;
  718. fGNamesTrie.put(uplname, nameinfo, status);
  719. }
  720. }
  721. }
  722. }
  723. return uplname;
  724. }
  725. /*
  726. * This method updates the cache and must be called with a lock,
  727. * except initializer.
  728. */
  729. void
  730. TZGNCore::loadStrings(const UnicodeString& tzCanonicalID) {
  731. // load the generic location name
  732. getGenericLocationName(tzCanonicalID);
  733. // partial location names
  734. UErrorCode status = U_ZERO_ERROR;
  735. const UnicodeString *mzID;
  736. UnicodeString goldenID;
  737. UnicodeString mzGenName;
  738. UTimeZoneNameType genNonLocTypes[] = {
  739. UTZNM_LONG_GENERIC, UTZNM_SHORT_GENERIC,
  740. UTZNM_UNKNOWN /*terminator*/
  741. };
  742. StringEnumeration *mzIDs = fTimeZoneNames->getAvailableMetaZoneIDs(tzCanonicalID, status);
  743. while ((mzID = mzIDs->snext(status)) != nullptr) {
  744. if (U_FAILURE(status)) {
  745. break;
  746. }
  747. // if this time zone is not the golden zone of the meta zone,
  748. // partial location name (such as "PT (Los Angeles)") might be
  749. // available.
  750. fTimeZoneNames->getReferenceZoneID(*mzID, fTargetRegion, goldenID);
  751. if (tzCanonicalID != goldenID) {
  752. for (int32_t i = 0; genNonLocTypes[i] != UTZNM_UNKNOWN; i++) {
  753. fTimeZoneNames->getMetaZoneDisplayName(*mzID, genNonLocTypes[i], mzGenName);
  754. if (!mzGenName.isEmpty()) {
  755. // getPartialLocationName formats a name and put it into the trie
  756. getPartialLocationName(tzCanonicalID, *mzID,
  757. (genNonLocTypes[i] == UTZNM_LONG_GENERIC), mzGenName);
  758. }
  759. }
  760. }
  761. }
  762. if (mzIDs != nullptr) {
  763. delete mzIDs;
  764. }
  765. }
  766. int32_t
  767. TZGNCore::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
  768. UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const {
  769. timeType = UTZFMT_TIME_TYPE_UNKNOWN;
  770. tzID.setToBogus();
  771. if (U_FAILURE(status)) {
  772. return 0;
  773. }
  774. // Find matches in the TimeZoneNames first
  775. TimeZoneNames::MatchInfoCollection *tznamesMatches = findTimeZoneNames(text, start, types, status);
  776. if (U_FAILURE(status)) {
  777. return 0;
  778. }
  779. int32_t bestMatchLen = 0;
  780. UTimeZoneFormatTimeType bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  781. UnicodeString bestMatchTzID;
  782. // UBool isLongStandard = false; // workaround - see the comments below
  783. UBool isStandard = false; // TODO: Temporary hack (on hack) for short standard name/location name conflict (found in zh_Hant), should be removed after CLDR 21m1 integration
  784. if (tznamesMatches != nullptr) {
  785. UnicodeString mzID;
  786. for (int32_t i = 0; i < tznamesMatches->size(); i++) {
  787. int32_t len = tznamesMatches->getMatchLengthAt(i);
  788. if (len > bestMatchLen) {
  789. bestMatchLen = len;
  790. if (!tznamesMatches->getTimeZoneIDAt(i, bestMatchTzID)) {
  791. // name for a meta zone
  792. if (tznamesMatches->getMetaZoneIDAt(i, mzID)) {
  793. fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, bestMatchTzID);
  794. }
  795. }
  796. UTimeZoneNameType nameType = tznamesMatches->getNameTypeAt(i);
  797. if (U_FAILURE(status)) {
  798. break;
  799. }
  800. switch (nameType) {
  801. case UTZNM_LONG_STANDARD:
  802. // isLongStandard = true;
  803. case UTZNM_SHORT_STANDARD: // this one is never used for generic, but just in case
  804. isStandard = true; // TODO: Remove this later, see the comments above.
  805. bestMatchTimeType = UTZFMT_TIME_TYPE_STANDARD;
  806. break;
  807. case UTZNM_LONG_DAYLIGHT:
  808. case UTZNM_SHORT_DAYLIGHT: // this one is never used for generic, but just in case
  809. bestMatchTimeType = UTZFMT_TIME_TYPE_DAYLIGHT;
  810. break;
  811. default:
  812. bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  813. }
  814. }
  815. }
  816. delete tznamesMatches;
  817. if (U_FAILURE(status)) {
  818. return 0;
  819. }
  820. if (bestMatchLen == (text.length() - start)) {
  821. // Full match
  822. //tzID.setTo(bestMatchTzID);
  823. //timeType = bestMatchTimeType;
  824. //return bestMatchLen;
  825. // TODO Some time zone uses a same name for the long standard name
  826. // and the location name. When the match is a long standard name,
  827. // then we need to check if the name is same with the location name.
  828. // This is probably a data error or a design bug.
  829. /*
  830. if (!isLongStandard) {
  831. tzID.setTo(bestMatchTzID);
  832. timeType = bestMatchTimeType;
  833. return bestMatchLen;
  834. }
  835. */
  836. // TODO The deprecation of commonlyUsed flag introduced the name
  837. // conflict not only for long standard names, but short standard names too.
  838. // These short names (found in zh_Hant) should be gone once we clean
  839. // up CLDR time zone display name data. Once the short name conflict
  840. // problem (with location name) is resolved, we should change the condition
  841. // below back to the original one above. -Yoshito (2011-09-14)
  842. if (!isStandard) {
  843. tzID.setTo(bestMatchTzID);
  844. timeType = bestMatchTimeType;
  845. return bestMatchLen;
  846. }
  847. }
  848. }
  849. // Find matches in the local trie
  850. TimeZoneGenericNameMatchInfo *localMatches = findLocal(text, start, types, status);
  851. if (U_FAILURE(status)) {
  852. return 0;
  853. }
  854. if (localMatches != nullptr) {
  855. for (int32_t i = 0; i < localMatches->size(); i++) {
  856. int32_t len = localMatches->getMatchLength(i);
  857. // TODO See the above TODO. We use len >= bestMatchLen
  858. // because of the long standard/location name collision
  859. // problem. If it is also a location name, carrying
  860. // timeType = UTZFMT_TIME_TYPE_STANDARD will cause a
  861. // problem in SimpleDateFormat
  862. if (len >= bestMatchLen) {
  863. bestMatchLen = localMatches->getMatchLength(i);
  864. bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; // because generic
  865. localMatches->getTimeZoneID(i, bestMatchTzID);
  866. }
  867. }
  868. delete localMatches;
  869. }
  870. if (bestMatchLen > 0) {
  871. timeType = bestMatchTimeType;
  872. tzID.setTo(bestMatchTzID);
  873. }
  874. return bestMatchLen;
  875. }
  876. TimeZoneGenericNameMatchInfo*
  877. TZGNCore::findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
  878. GNameSearchHandler handler(types);
  879. TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
  880. umtx_lock(&gLock);
  881. {
  882. fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
  883. }
  884. umtx_unlock(&gLock);
  885. if (U_FAILURE(status)) {
  886. return nullptr;
  887. }
  888. TimeZoneGenericNameMatchInfo *gmatchInfo = nullptr;
  889. int32_t maxLen = 0;
  890. UVector *results = handler.getMatches(maxLen);
  891. if (results != nullptr && ((maxLen == (text.length() - start)) || fGNamesTrieFullyLoaded)) {
  892. // perfect match
  893. gmatchInfo = new TimeZoneGenericNameMatchInfo(results);
  894. if (gmatchInfo == nullptr) {
  895. status = U_MEMORY_ALLOCATION_ERROR;
  896. delete results;
  897. return nullptr;
  898. }
  899. return gmatchInfo;
  900. }
  901. if (results != nullptr) {
  902. delete results;
  903. }
  904. // All names are not yet loaded into the local trie.
  905. // Load all available names into the trie. This could be very heavy.
  906. umtx_lock(&gLock);
  907. {
  908. if (!fGNamesTrieFullyLoaded) {
  909. StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, nullptr, nullptr, status);
  910. if (U_SUCCESS(status)) {
  911. const UnicodeString *tzID;
  912. while ((tzID = tzIDs->snext(status)) != nullptr) {
  913. if (U_FAILURE(status)) {
  914. break;
  915. }
  916. nonConstThis->loadStrings(*tzID);
  917. }
  918. }
  919. if (tzIDs != nullptr) {
  920. delete tzIDs;
  921. }
  922. if (U_SUCCESS(status)) {
  923. nonConstThis->fGNamesTrieFullyLoaded = true;
  924. }
  925. }
  926. }
  927. umtx_unlock(&gLock);
  928. if (U_FAILURE(status)) {
  929. return nullptr;
  930. }
  931. umtx_lock(&gLock);
  932. {
  933. // now try it again
  934. fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
  935. }
  936. umtx_unlock(&gLock);
  937. results = handler.getMatches(maxLen);
  938. if (results != nullptr && maxLen > 0) {
  939. gmatchInfo = new TimeZoneGenericNameMatchInfo(results);
  940. if (gmatchInfo == nullptr) {
  941. status = U_MEMORY_ALLOCATION_ERROR;
  942. delete results;
  943. return nullptr;
  944. }
  945. }
  946. return gmatchInfo;
  947. }
  948. TimeZoneNames::MatchInfoCollection*
  949. TZGNCore::findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
  950. // Check if the target name typs is really in the TimeZoneNames
  951. uint32_t nameTypes = 0;
  952. if (types & UTZGNM_LONG) {
  953. nameTypes |= (UTZNM_LONG_GENERIC | UTZNM_LONG_STANDARD);
  954. }
  955. if (types & UTZGNM_SHORT) {
  956. nameTypes |= (UTZNM_SHORT_GENERIC | UTZNM_SHORT_STANDARD);
  957. }
  958. if (types) {
  959. // Find matches in the TimeZoneNames
  960. return fTimeZoneNames->find(text, start, nameTypes, status);
  961. }
  962. return nullptr;
  963. }
  964. typedef struct TZGNCoreRef {
  965. TZGNCore* obj;
  966. int32_t refCount;
  967. double lastAccess;
  968. } TZGNCoreRef;
  969. // TZGNCore object cache handling
  970. static UMutex gTZGNLock;
  971. static UHashtable *gTZGNCoreCache = nullptr;
  972. static UBool gTZGNCoreCacheInitialized = false;
  973. // Access count - incremented every time up to SWEEP_INTERVAL,
  974. // then reset to 0
  975. static int32_t gAccessCount = 0;
  976. // Interval for calling the cache sweep function - every 100 times
  977. #define SWEEP_INTERVAL 100
  978. // Cache expiration in millisecond. When a cached entry is no
  979. // longer referenced and exceeding this threshold since last
  980. // access time, then the cache entry will be deleted by the sweep
  981. // function. For now, 3 minutes.
  982. #define CACHE_EXPIRATION 180000.0
  983. U_CDECL_BEGIN
  984. /**
  985. * Cleanup callback func
  986. */
  987. static UBool U_CALLCONV tzgnCore_cleanup()
  988. {
  989. if (gTZGNCoreCache != nullptr) {
  990. uhash_close(gTZGNCoreCache);
  991. gTZGNCoreCache = nullptr;
  992. }
  993. gTZGNCoreCacheInitialized = false;
  994. return true;
  995. }
  996. /**
  997. * Deleter for TZGNCoreRef
  998. */
  999. static void U_CALLCONV
  1000. deleteTZGNCoreRef(void *obj) {
  1001. icu::TZGNCoreRef *entry = (icu::TZGNCoreRef*)obj;
  1002. delete (icu::TZGNCore*) entry->obj;
  1003. uprv_free(entry);
  1004. }
  1005. U_CDECL_END
  1006. /**
  1007. * Function used for removing unreferrenced cache entries exceeding
  1008. * the expiration time. This function must be called with in the mutex
  1009. * block.
  1010. */
  1011. static void sweepCache() {
  1012. int32_t pos = UHASH_FIRST;
  1013. const UHashElement* elem;
  1014. double now = (double)uprv_getUTCtime();
  1015. while ((elem = uhash_nextElement(gTZGNCoreCache, &pos)) != nullptr) {
  1016. TZGNCoreRef *entry = (TZGNCoreRef *)elem->value.pointer;
  1017. if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) {
  1018. // delete this entry
  1019. uhash_removeElement(gTZGNCoreCache, elem);
  1020. }
  1021. }
  1022. }
  1023. TimeZoneGenericNames::TimeZoneGenericNames()
  1024. : fRef(0) {
  1025. }
  1026. TimeZoneGenericNames::~TimeZoneGenericNames() {
  1027. umtx_lock(&gTZGNLock);
  1028. {
  1029. U_ASSERT(fRef->refCount > 0);
  1030. // Just decrement the reference count
  1031. fRef->refCount--;
  1032. }
  1033. umtx_unlock(&gTZGNLock);
  1034. }
  1035. TimeZoneGenericNames*
  1036. TimeZoneGenericNames::createInstance(const Locale& locale, UErrorCode& status) {
  1037. if (U_FAILURE(status)) {
  1038. return nullptr;
  1039. }
  1040. TimeZoneGenericNames* instance = new TimeZoneGenericNames();
  1041. if (instance == nullptr) {
  1042. status = U_MEMORY_ALLOCATION_ERROR;
  1043. return nullptr;
  1044. }
  1045. TZGNCoreRef *cacheEntry = nullptr;
  1046. {
  1047. Mutex lock(&gTZGNLock);
  1048. if (!gTZGNCoreCacheInitialized) {
  1049. // Create empty hashtable
  1050. gTZGNCoreCache = uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &status);
  1051. if (U_SUCCESS(status)) {
  1052. uhash_setKeyDeleter(gTZGNCoreCache, uprv_free);
  1053. uhash_setValueDeleter(gTZGNCoreCache, deleteTZGNCoreRef);
  1054. gTZGNCoreCacheInitialized = true;
  1055. ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEGENERICNAMES, tzgnCore_cleanup);
  1056. }
  1057. }
  1058. if (U_FAILURE(status)) {
  1059. return nullptr;
  1060. }
  1061. // Check the cache, if not available, create new one and cache
  1062. const char *key = locale.getName();
  1063. cacheEntry = (TZGNCoreRef *)uhash_get(gTZGNCoreCache, key);
  1064. if (cacheEntry == nullptr) {
  1065. TZGNCore *tzgnCore = nullptr;
  1066. char *newKey = nullptr;
  1067. tzgnCore = new TZGNCore(locale, status);
  1068. if (tzgnCore == nullptr) {
  1069. status = U_MEMORY_ALLOCATION_ERROR;
  1070. }
  1071. if (U_SUCCESS(status)) {
  1072. newKey = (char *)uprv_malloc(uprv_strlen(key) + 1);
  1073. if (newKey == nullptr) {
  1074. status = U_MEMORY_ALLOCATION_ERROR;
  1075. } else {
  1076. uprv_strcpy(newKey, key);
  1077. }
  1078. }
  1079. if (U_SUCCESS(status)) {
  1080. cacheEntry = (TZGNCoreRef *)uprv_malloc(sizeof(TZGNCoreRef));
  1081. if (cacheEntry == nullptr) {
  1082. status = U_MEMORY_ALLOCATION_ERROR;
  1083. } else {
  1084. cacheEntry->obj = tzgnCore;
  1085. cacheEntry->refCount = 1;
  1086. cacheEntry->lastAccess = (double)uprv_getUTCtime();
  1087. uhash_put(gTZGNCoreCache, newKey, cacheEntry, &status);
  1088. }
  1089. }
  1090. if (U_FAILURE(status)) {
  1091. if (tzgnCore != nullptr) {
  1092. delete tzgnCore;
  1093. }
  1094. if (newKey != nullptr) {
  1095. uprv_free(newKey);
  1096. }
  1097. if (cacheEntry != nullptr) {
  1098. uprv_free(cacheEntry);
  1099. }
  1100. cacheEntry = nullptr;
  1101. }
  1102. } else {
  1103. // Update the reference count
  1104. cacheEntry->refCount++;
  1105. cacheEntry->lastAccess = (double)uprv_getUTCtime();
  1106. }
  1107. gAccessCount++;
  1108. if (gAccessCount >= SWEEP_INTERVAL) {
  1109. // sweep
  1110. sweepCache();
  1111. gAccessCount = 0;
  1112. }
  1113. } // End of mutex locked block
  1114. if (cacheEntry == nullptr) {
  1115. delete instance;
  1116. return nullptr;
  1117. }
  1118. instance->fRef = cacheEntry;
  1119. return instance;
  1120. }
  1121. bool
  1122. TimeZoneGenericNames::operator==(const TimeZoneGenericNames& other) const {
  1123. // Just compare if the other object also use the same
  1124. // ref entry
  1125. return fRef == other.fRef;
  1126. }
  1127. TimeZoneGenericNames*
  1128. TimeZoneGenericNames::clone() const {
  1129. TimeZoneGenericNames* other = new TimeZoneGenericNames();
  1130. if (other) {
  1131. umtx_lock(&gTZGNLock);
  1132. {
  1133. // Just increments the reference count
  1134. fRef->refCount++;
  1135. other->fRef = fRef;
  1136. }
  1137. umtx_unlock(&gTZGNLock);
  1138. }
  1139. return other;
  1140. }
  1141. UnicodeString&
  1142. TimeZoneGenericNames::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,
  1143. UDate date, UnicodeString& name) const {
  1144. return fRef->obj->getDisplayName(tz, type, date, name);
  1145. }
  1146. UnicodeString&
  1147. TimeZoneGenericNames::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
  1148. return fRef->obj->getGenericLocationName(tzCanonicalID, name);
  1149. }
  1150. int32_t
  1151. TimeZoneGenericNames::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
  1152. UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const {
  1153. return fRef->obj->findBestMatch(text, start, types, tzID, timeType, status);
  1154. }
  1155. U_NAMESPACE_END
  1156. #endif