summaryrefslogtreecommitdiffstats
path: root/intl/icu/source/i18n/tzgnames.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'intl/icu/source/i18n/tzgnames.cpp')
-rw-r--r--intl/icu/source/i18n/tzgnames.cpp1323
1 files changed, 1323 insertions, 0 deletions
diff --git a/intl/icu/source/i18n/tzgnames.cpp b/intl/icu/source/i18n/tzgnames.cpp
new file mode 100644
index 000000000..4fc726ea5
--- /dev/null
+++ b/intl/icu/source/i18n/tzgnames.cpp
@@ -0,0 +1,1323 @@
+// Copyright (C) 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+/*
+*******************************************************************************
+* Copyright (C) 2011-2016, International Business Machines Corporation and
+* others. All Rights Reserved.
+*******************************************************************************
+*/
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "tzgnames.h"
+
+#include "unicode/basictz.h"
+#include "unicode/locdspnm.h"
+#include "unicode/rbtz.h"
+#include "unicode/simpleformatter.h"
+#include "unicode/simpletz.h"
+#include "unicode/vtzone.h"
+
+#include "cmemory.h"
+#include "cstring.h"
+#include "mutex.h"
+#include "uhash.h"
+#include "uassert.h"
+#include "umutex.h"
+#include "uresimp.h"
+#include "ureslocs.h"
+#include "zonemeta.h"
+#include "tznames_impl.h"
+#include "olsontz.h"
+#include "ucln_in.h"
+
+U_NAMESPACE_BEGIN
+
+#define ZID_KEY_MAX 128
+
+static const char gZoneStrings[] = "zoneStrings";
+
+static const char gRegionFormatTag[] = "regionFormat";
+static const char gFallbackFormatTag[] = "fallbackFormat";
+
+static const UChar gEmpty[] = {0x00};
+
+static const UChar gDefRegionPattern[] = {0x7B, 0x30, 0x7D, 0x00}; // "{0}"
+static const UChar gDefFallbackPattern[] = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})"
+
+static const double kDstCheckRange = (double)184*U_MILLIS_PER_DAY;
+
+
+
+U_CDECL_BEGIN
+
+typedef struct PartialLocationKey {
+ const UChar* tzID;
+ const UChar* mzID;
+ UBool isLong;
+} PartialLocationKey;
+
+/**
+ * Hash function for partial location name hash key
+ */
+static int32_t U_CALLCONV
+hashPartialLocationKey(const UHashTok key) {
+ // <tzID>&<mzID>#[L|S]
+ PartialLocationKey *p = (PartialLocationKey *)key.pointer;
+ UnicodeString str(p->tzID);
+ str.append((UChar)0x26)
+ .append(p->mzID, -1)
+ .append((UChar)0x23)
+ .append((UChar)(p->isLong ? 0x4C : 0x53));
+ return str.hashCode();
+}
+
+/**
+ * Comparer for partial location name hash key
+ */
+static UBool U_CALLCONV
+comparePartialLocationKey(const UHashTok key1, const UHashTok key2) {
+ PartialLocationKey *p1 = (PartialLocationKey *)key1.pointer;
+ PartialLocationKey *p2 = (PartialLocationKey *)key2.pointer;
+
+ if (p1 == p2) {
+ return TRUE;
+ }
+ if (p1 == NULL || p2 == NULL) {
+ return FALSE;
+ }
+ // We just check identity of tzID/mzID
+ return (p1->tzID == p2->tzID && p1->mzID == p2->mzID && p1->isLong == p2->isLong);
+}
+
+/**
+ * Deleter for GNameInfo
+ */
+static void U_CALLCONV
+deleteGNameInfo(void *obj) {
+ uprv_free(obj);
+}
+
+/**
+ * GNameInfo stores zone name information in the local trie
+ */
+typedef struct GNameInfo {
+ UTimeZoneGenericNameType type;
+ const UChar* tzID;
+} ZNameInfo;
+
+/**
+ * GMatchInfo stores zone name match information used by find method
+ */
+typedef struct GMatchInfo {
+ const GNameInfo* gnameInfo;
+ int32_t matchLength;
+ UTimeZoneFormatTimeType timeType;
+} ZMatchInfo;
+
+U_CDECL_END
+
+// ---------------------------------------------------
+// The class stores time zone generic name match information
+// ---------------------------------------------------
+class TimeZoneGenericNameMatchInfo : public UMemory {
+public:
+ TimeZoneGenericNameMatchInfo(UVector* matches);
+ ~TimeZoneGenericNameMatchInfo();
+
+ int32_t size() const;
+ UTimeZoneGenericNameType getGenericNameType(int32_t index) const;
+ int32_t getMatchLength(int32_t index) const;
+ UnicodeString& getTimeZoneID(int32_t index, UnicodeString& tzID) const;
+
+private:
+ UVector* fMatches; // vector of MatchEntry
+};
+
+TimeZoneGenericNameMatchInfo::TimeZoneGenericNameMatchInfo(UVector* matches)
+: fMatches(matches) {
+}
+
+TimeZoneGenericNameMatchInfo::~TimeZoneGenericNameMatchInfo() {
+ if (fMatches != NULL) {
+ delete fMatches;
+ }
+}
+
+int32_t
+TimeZoneGenericNameMatchInfo::size() const {
+ if (fMatches == NULL) {
+ return 0;
+ }
+ return fMatches->size();
+}
+
+UTimeZoneGenericNameType
+TimeZoneGenericNameMatchInfo::getGenericNameType(int32_t index) const {
+ GMatchInfo *minfo = (GMatchInfo *)fMatches->elementAt(index);
+ if (minfo != NULL) {
+ return static_cast<UTimeZoneGenericNameType>(minfo->gnameInfo->type);
+ }
+ return UTZGNM_UNKNOWN;
+}
+
+int32_t
+TimeZoneGenericNameMatchInfo::getMatchLength(int32_t index) const {
+ ZMatchInfo *minfo = (ZMatchInfo *)fMatches->elementAt(index);
+ if (minfo != NULL) {
+ return minfo->matchLength;
+ }
+ return -1;
+}
+
+UnicodeString&
+TimeZoneGenericNameMatchInfo::getTimeZoneID(int32_t index, UnicodeString& tzID) const {
+ GMatchInfo *minfo = (GMatchInfo *)fMatches->elementAt(index);
+ if (minfo != NULL && minfo->gnameInfo->tzID != NULL) {
+ tzID.setTo(TRUE, minfo->gnameInfo->tzID, -1);
+ } else {
+ tzID.setToBogus();
+ }
+ return tzID;
+}
+
+// ---------------------------------------------------
+// GNameSearchHandler
+// ---------------------------------------------------
+class GNameSearchHandler : public TextTrieMapSearchResultHandler {
+public:
+ GNameSearchHandler(uint32_t types);
+ virtual ~GNameSearchHandler();
+
+ UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status);
+ UVector* getMatches(int32_t& maxMatchLen);
+
+private:
+ uint32_t fTypes;
+ UVector* fResults;
+ int32_t fMaxMatchLen;
+};
+
+GNameSearchHandler::GNameSearchHandler(uint32_t types)
+: fTypes(types), fResults(NULL), fMaxMatchLen(0) {
+}
+
+GNameSearchHandler::~GNameSearchHandler() {
+ if (fResults != NULL) {
+ delete fResults;
+ }
+}
+
+UBool
+GNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return FALSE;
+ }
+ if (node->hasValues()) {
+ int32_t valuesCount = node->countValues();
+ for (int32_t i = 0; i < valuesCount; i++) {
+ GNameInfo *nameinfo = (ZNameInfo *)node->getValue(i);
+ if (nameinfo == NULL) {
+ break;
+ }
+ if ((nameinfo->type & fTypes) != 0) {
+ // matches a requested type
+ if (fResults == NULL) {
+ fResults = new UVector(uprv_free, NULL, status);
+ if (fResults == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ }
+ }
+ if (U_SUCCESS(status)) {
+ U_ASSERT(fResults != NULL);
+ GMatchInfo *gmatch = (GMatchInfo *)uprv_malloc(sizeof(GMatchInfo));
+ if (gmatch == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ } else {
+ // add the match to the vector
+ gmatch->gnameInfo = nameinfo;
+ gmatch->matchLength = matchLength;
+ gmatch->timeType = UTZFMT_TIME_TYPE_UNKNOWN;
+ fResults->addElement(gmatch, status);
+ if (U_FAILURE(status)) {
+ uprv_free(gmatch);
+ } else {
+ if (matchLength > fMaxMatchLen) {
+ fMaxMatchLen = matchLength;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return TRUE;
+}
+
+UVector*
+GNameSearchHandler::getMatches(int32_t& maxMatchLen) {
+ // give the ownership to the caller
+ UVector *results = fResults;
+ maxMatchLen = fMaxMatchLen;
+
+ // reset
+ fResults = NULL;
+ fMaxMatchLen = 0;
+ return results;
+}
+
+static UMutex gLock = U_MUTEX_INITIALIZER;
+
+class TZGNCore : public UMemory {
+public:
+ TZGNCore(const Locale& locale, UErrorCode& status);
+ virtual ~TZGNCore();
+
+ UnicodeString& getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,
+ UDate date, UnicodeString& name) const;
+
+ UnicodeString& getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const;
+
+ int32_t findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
+ UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const;
+
+private:
+ Locale fLocale;
+ const TimeZoneNames* fTimeZoneNames;
+ UHashtable* fLocationNamesMap;
+ UHashtable* fPartialLocationNamesMap;
+
+ SimpleFormatter fRegionFormat;
+ SimpleFormatter fFallbackFormat;
+
+ LocaleDisplayNames* fLocaleDisplayNames;
+ ZNStringPool fStringPool;
+
+ TextTrieMap fGNamesTrie;
+ UBool fGNamesTrieFullyLoaded;
+
+ char fTargetRegion[ULOC_COUNTRY_CAPACITY];
+
+ void initialize(const Locale& locale, UErrorCode& status);
+ void cleanup();
+
+ void loadStrings(const UnicodeString& tzCanonicalID);
+
+ const UChar* getGenericLocationName(const UnicodeString& tzCanonicalID);
+
+ UnicodeString& formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type,
+ UDate date, UnicodeString& name) const;
+
+ UnicodeString& getPartialLocationName(const UnicodeString& tzCanonicalID,
+ const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName,
+ UnicodeString& name) const;
+
+ const UChar* getPartialLocationName(const UnicodeString& tzCanonicalID,
+ const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName);
+
+ TimeZoneGenericNameMatchInfo* findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
+
+ TimeZoneNames::MatchInfoCollection* findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
+};
+
+
+// ---------------------------------------------------
+// TZGNCore - core implmentation of TimeZoneGenericNames
+//
+// TimeZoneGenericNames is parallel to TimeZoneNames,
+// but handles run-time generated time zone names.
+// This is the main part of this module.
+// ---------------------------------------------------
+TZGNCore::TZGNCore(const Locale& locale, UErrorCode& status)
+: fLocale(locale),
+ fTimeZoneNames(NULL),
+ fLocationNamesMap(NULL),
+ fPartialLocationNamesMap(NULL),
+ fLocaleDisplayNames(NULL),
+ fStringPool(status),
+ fGNamesTrie(TRUE, deleteGNameInfo),
+ fGNamesTrieFullyLoaded(FALSE) {
+ initialize(locale, status);
+}
+
+TZGNCore::~TZGNCore() {
+ cleanup();
+}
+
+void
+TZGNCore::initialize(const Locale& locale, UErrorCode& status) {
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ // TimeZoneNames
+ fTimeZoneNames = TimeZoneNames::createInstance(locale, status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ // Initialize format patterns
+ UnicodeString rpat(TRUE, gDefRegionPattern, -1);
+ UnicodeString fpat(TRUE, gDefFallbackPattern, -1);
+
+ UErrorCode tmpsts = U_ZERO_ERROR; // OK with fallback warning..
+ UResourceBundle *zoneStrings = ures_open(U_ICUDATA_ZONE, locale.getName(), &tmpsts);
+ zoneStrings = ures_getByKeyWithFallback(zoneStrings, gZoneStrings, zoneStrings, &tmpsts);
+
+ if (U_SUCCESS(tmpsts)) {
+ const UChar *regionPattern = ures_getStringByKeyWithFallback(zoneStrings, gRegionFormatTag, NULL, &tmpsts);
+ if (U_SUCCESS(tmpsts) && u_strlen(regionPattern) > 0) {
+ rpat.setTo(regionPattern, -1);
+ }
+ tmpsts = U_ZERO_ERROR;
+ const UChar *fallbackPattern = ures_getStringByKeyWithFallback(zoneStrings, gFallbackFormatTag, NULL, &tmpsts);
+ if (U_SUCCESS(tmpsts) && u_strlen(fallbackPattern) > 0) {
+ fpat.setTo(fallbackPattern, -1);
+ }
+ }
+ ures_close(zoneStrings);
+
+ fRegionFormat.applyPatternMinMaxArguments(rpat, 1, 1, status);
+ fFallbackFormat.applyPatternMinMaxArguments(fpat, 2, 2, status);
+ if (U_FAILURE(status)) {
+ cleanup();
+ return;
+ }
+
+ // locale display names
+ fLocaleDisplayNames = LocaleDisplayNames::createInstance(locale);
+
+ // hash table for names - no key/value deleters
+ fLocationNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
+ if (U_FAILURE(status)) {
+ cleanup();
+ return;
+ }
+
+ fPartialLocationNamesMap = uhash_open(hashPartialLocationKey, comparePartialLocationKey, NULL, &status);
+ if (U_FAILURE(status)) {
+ cleanup();
+ return;
+ }
+ uhash_setKeyDeleter(fPartialLocationNamesMap, uprv_free);
+ // no value deleter
+
+ // target region
+ const char* region = fLocale.getCountry();
+ int32_t regionLen = uprv_strlen(region);
+ if (regionLen == 0) {
+ char loc[ULOC_FULLNAME_CAPACITY];
+ uloc_addLikelySubtags(fLocale.getName(), loc, sizeof(loc), &status);
+
+ regionLen = uloc_getCountry(loc, fTargetRegion, sizeof(fTargetRegion), &status);
+ if (U_SUCCESS(status)) {
+ fTargetRegion[regionLen] = 0;
+ } else {
+ cleanup();
+ return;
+ }
+ } else if (regionLen < (int32_t)sizeof(fTargetRegion)) {
+ uprv_strcpy(fTargetRegion, region);
+ } else {
+ fTargetRegion[0] = 0;
+ }
+
+ // preload generic names for the default zone
+ TimeZone *tz = TimeZone::createDefault();
+ const UChar *tzID = ZoneMeta::getCanonicalCLDRID(*tz);
+ if (tzID != NULL) {
+ loadStrings(UnicodeString(TRUE, tzID, -1));
+ }
+ delete tz;
+}
+
+void
+TZGNCore::cleanup() {
+ if (fLocaleDisplayNames != NULL) {
+ delete fLocaleDisplayNames;
+ }
+ if (fTimeZoneNames != NULL) {
+ delete fTimeZoneNames;
+ }
+
+ uhash_close(fLocationNamesMap);
+ uhash_close(fPartialLocationNamesMap);
+}
+
+
+UnicodeString&
+TZGNCore::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const {
+ name.setToBogus();
+ switch (type) {
+ case UTZGNM_LOCATION:
+ {
+ const UChar* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz);
+ if (tzCanonicalID != NULL) {
+ getGenericLocationName(UnicodeString(TRUE, tzCanonicalID, -1), name);
+ }
+ }
+ break;
+ case UTZGNM_LONG:
+ case UTZGNM_SHORT:
+ formatGenericNonLocationName(tz, type, date, name);
+ if (name.isEmpty()) {
+ const UChar* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz);
+ if (tzCanonicalID != NULL) {
+ getGenericLocationName(UnicodeString(TRUE, tzCanonicalID, -1), name);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return name;
+}
+
+UnicodeString&
+TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
+ if (tzCanonicalID.isEmpty()) {
+ name.setToBogus();
+ return name;
+ }
+
+ const UChar *locname = NULL;
+ TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
+ umtx_lock(&gLock);
+ {
+ locname = nonConstThis->getGenericLocationName(tzCanonicalID);
+ }
+ umtx_unlock(&gLock);
+
+ if (locname == NULL) {
+ name.setToBogus();
+ } else {
+ name.setTo(locname, u_strlen(locname));
+ }
+
+ return name;
+}
+
+/*
+ * This method updates the cache and must be called with a lock
+ */
+const UChar*
+TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID) {
+ U_ASSERT(!tzCanonicalID.isEmpty());
+ if (tzCanonicalID.length() > ZID_KEY_MAX) {
+ return NULL;
+ }
+
+ UErrorCode status = U_ZERO_ERROR;
+ UChar tzIDKey[ZID_KEY_MAX + 1];
+ int32_t tzIDKeyLen = tzCanonicalID.extract(tzIDKey, ZID_KEY_MAX + 1, status);
+ U_ASSERT(status == U_ZERO_ERROR); // already checked length above
+ tzIDKey[tzIDKeyLen] = 0;
+
+ const UChar *locname = (const UChar *)uhash_get(fLocationNamesMap, tzIDKey);
+
+ if (locname != NULL) {
+ // gEmpty indicate the name is not available
+ if (locname == gEmpty) {
+ return NULL;
+ }
+ return locname;
+ }
+
+ // Construct location name
+ UnicodeString name;
+ UnicodeString usCountryCode;
+ UBool isPrimary = FALSE;
+
+ ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode, &isPrimary);
+
+ if (!usCountryCode.isEmpty()) {
+ if (isPrimary) {
+ // If this is the primary zone in the country, use the country name.
+ char countryCode[ULOC_COUNTRY_CAPACITY];
+ U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY);
+ int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV);
+ countryCode[ccLen] = 0;
+
+ UnicodeString country;
+ fLocaleDisplayNames->regionDisplayName(countryCode, country);
+ fRegionFormat.format(country, name, status);
+ } else {
+ // If this is not the primary zone in the country,
+ // use the exemplar city name.
+
+ // getExemplarLocationName should retur non-empty string
+ // if the time zone is associated with a region
+
+ UnicodeString city;
+ fTimeZoneNames->getExemplarLocationName(tzCanonicalID, city);
+ fRegionFormat.format(city, name, status);
+ }
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ }
+
+ locname = name.isEmpty() ? NULL : fStringPool.get(name, status);
+ if (U_SUCCESS(status)) {
+ // Cache the result
+ const UChar* cacheID = ZoneMeta::findTimeZoneID(tzCanonicalID);
+ U_ASSERT(cacheID != NULL);
+ if (locname == NULL) {
+ // gEmpty to indicate - no location name available
+ uhash_put(fLocationNamesMap, (void *)cacheID, (void *)gEmpty, &status);
+ } else {
+ uhash_put(fLocationNamesMap, (void *)cacheID, (void *)locname, &status);
+ if (U_FAILURE(status)) {
+ locname = NULL;
+ } else {
+ // put the name info into the trie
+ GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo));
+ if (nameinfo != NULL) {
+ nameinfo->type = UTZGNM_LOCATION;
+ nameinfo->tzID = cacheID;
+ fGNamesTrie.put(locname, nameinfo, status);
+ }
+ }
+ }
+ }
+
+ return locname;
+}
+
+UnicodeString&
+TZGNCore::formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const {
+ U_ASSERT(type == UTZGNM_LONG || type == UTZGNM_SHORT);
+ name.setToBogus();
+
+ const UChar* uID = ZoneMeta::getCanonicalCLDRID(tz);
+ if (uID == NULL) {
+ return name;
+ }
+
+ UnicodeString tzID(TRUE, uID, -1);
+
+ // Try to get a name from time zone first
+ UTimeZoneNameType nameType = (type == UTZGNM_LONG) ? UTZNM_LONG_GENERIC : UTZNM_SHORT_GENERIC;
+ fTimeZoneNames->getTimeZoneDisplayName(tzID, nameType, name);
+
+ if (!name.isEmpty()) {
+ return name;
+ }
+
+ // Try meta zone
+ UChar mzIDBuf[32];
+ UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf));
+ fTimeZoneNames->getMetaZoneID(tzID, date, mzID);
+ if (!mzID.isEmpty()) {
+ UErrorCode status = U_ZERO_ERROR;
+ UBool useStandard = FALSE;
+ int32_t raw, sav;
+ UChar tmpNameBuf[64];
+
+ tz.getOffset(date, FALSE, raw, sav, status);
+ if (U_FAILURE(status)) {
+ return name;
+ }
+
+ if (sav == 0) {
+ useStandard = TRUE;
+
+ TimeZone *tmptz = tz.clone();
+ // Check if the zone actually uses daylight saving time around the time
+ BasicTimeZone *btz = NULL;
+ if (dynamic_cast<OlsonTimeZone *>(tmptz) != NULL
+ || dynamic_cast<SimpleTimeZone *>(tmptz) != NULL
+ || dynamic_cast<RuleBasedTimeZone *>(tmptz) != NULL
+ || dynamic_cast<VTimeZone *>(tmptz) != NULL) {
+ btz = (BasicTimeZone*)tmptz;
+ }
+
+ if (btz != NULL) {
+ TimeZoneTransition before;
+ UBool beforTrs = btz->getPreviousTransition(date, TRUE, before);
+ if (beforTrs
+ && (date - before.getTime() < kDstCheckRange)
+ && before.getFrom()->getDSTSavings() != 0) {
+ useStandard = FALSE;
+ } else {
+ TimeZoneTransition after;
+ UBool afterTrs = btz->getNextTransition(date, FALSE, after);
+ if (afterTrs
+ && (after.getTime() - date < kDstCheckRange)
+ && after.getTo()->getDSTSavings() != 0) {
+ useStandard = FALSE;
+ }
+ }
+ } else {
+ // If not BasicTimeZone... only if the instance is not an ICU's implementation.
+ // We may get a wrong answer in edge case, but it should practically work OK.
+ tmptz->getOffset(date - kDstCheckRange, FALSE, raw, sav, status);
+ if (sav != 0) {
+ useStandard = FALSE;
+ } else {
+ tmptz->getOffset(date + kDstCheckRange, FALSE, raw, sav, status);
+ if (sav != 0){
+ useStandard = FALSE;
+ }
+ }
+ if (U_FAILURE(status)) {
+ delete tmptz;
+ return name;
+ }
+ }
+ delete tmptz;
+ }
+ if (useStandard) {
+ UTimeZoneNameType stdNameType = (nameType == UTZNM_LONG_GENERIC)
+ ? UTZNM_LONG_STANDARD : UTZNM_SHORT_STANDARD;
+ UnicodeString stdName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf));
+ fTimeZoneNames->getDisplayName(tzID, stdNameType, date, stdName);
+ if (!stdName.isEmpty()) {
+ name.setTo(stdName);
+
+ // TODO: revisit this issue later
+ // In CLDR, a same display name is used for both generic and standard
+ // for some meta zones in some locales. This looks like a data bugs.
+ // For now, we check if the standard name is different from its generic
+ // name below.
+ UChar genNameBuf[64];
+ UnicodeString mzGenericName(genNameBuf, 0, UPRV_LENGTHOF(genNameBuf));
+ fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzGenericName);
+ if (stdName.caseCompare(mzGenericName, 0) == 0) {
+ name.setToBogus();
+ }
+ }
+ }
+ if (name.isEmpty()) {
+ // Get a name from meta zone
+ UnicodeString mzName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf));
+ fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzName);
+ if (!mzName.isEmpty()) {
+ // Check if we need to use a partial location format.
+ // This check is done by comparing offset with the meta zone's
+ // golden zone at the given date.
+ UChar idBuf[32];
+ UnicodeString goldenID(idBuf, 0, UPRV_LENGTHOF(idBuf));
+ fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, goldenID);
+ if (!goldenID.isEmpty() && goldenID != tzID) {
+ TimeZone *goldenZone = TimeZone::createTimeZone(goldenID);
+ int32_t raw1, sav1;
+
+ // Check offset in the golden zone with wall time.
+ // With getOffset(date, false, offsets1),
+ // you may get incorrect results because of time overlap at DST->STD
+ // transition.
+ goldenZone->getOffset(date + raw + sav, TRUE, raw1, sav1, status);
+ delete goldenZone;
+ if (U_SUCCESS(status)) {
+ if (raw != raw1 || sav != sav1) {
+ // Now we need to use a partial location format
+ getPartialLocationName(tzID, mzID, (nameType == UTZNM_LONG_GENERIC), mzName, name);
+ } else {
+ name.setTo(mzName);
+ }
+ }
+ } else {
+ name.setTo(mzName);
+ }
+ }
+ }
+ }
+ return name;
+}
+
+UnicodeString&
+TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID,
+ const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName,
+ UnicodeString& name) const {
+ name.setToBogus();
+ if (tzCanonicalID.isEmpty() || mzID.isEmpty() || mzDisplayName.isEmpty()) {
+ return name;
+ }
+
+ const UChar *uplname = NULL;
+ TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
+ umtx_lock(&gLock);
+ {
+ uplname = nonConstThis->getPartialLocationName(tzCanonicalID, mzID, isLong, mzDisplayName);
+ }
+ umtx_unlock(&gLock);
+
+ if (uplname == NULL) {
+ name.setToBogus();
+ } else {
+ name.setTo(TRUE, uplname, -1);
+ }
+ return name;
+}
+
+/*
+ * This method updates the cache and must be called with a lock
+ */
+const UChar*
+TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID,
+ const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName) {
+ U_ASSERT(!tzCanonicalID.isEmpty());
+ U_ASSERT(!mzID.isEmpty());
+ U_ASSERT(!mzDisplayName.isEmpty());
+
+ PartialLocationKey key;
+ key.tzID = ZoneMeta::findTimeZoneID(tzCanonicalID);
+ key.mzID = ZoneMeta::findMetaZoneID(mzID);
+ key.isLong = isLong;
+ U_ASSERT(key.tzID != NULL && key.mzID != NULL);
+
+ const UChar* uplname = (const UChar*)uhash_get(fPartialLocationNamesMap, (void *)&key);
+ if (uplname != NULL) {
+ return uplname;
+ }
+
+ UnicodeString location;
+ UnicodeString usCountryCode;
+ ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode);
+ if (!usCountryCode.isEmpty()) {
+ char countryCode[ULOC_COUNTRY_CAPACITY];
+ U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY);
+ int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INV);
+ countryCode[ccLen] = 0;
+
+ UnicodeString regionalGolden;
+ fTimeZoneNames->getReferenceZoneID(mzID, countryCode, regionalGolden);
+ if (tzCanonicalID == regionalGolden) {
+ // Use country name
+ fLocaleDisplayNames->regionDisplayName(countryCode, location);
+ } else {
+ // Otherwise, use exemplar city name
+ fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);
+ }
+ } else {
+ fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);
+ if (location.isEmpty()) {
+ // This could happen when the time zone is not associated with a country,
+ // and its ID is not hierarchical, for example, CST6CDT.
+ // We use the canonical ID itself as the location for this case.
+ location.setTo(tzCanonicalID);
+ }
+ }
+
+ UErrorCode status = U_ZERO_ERROR;
+ UnicodeString name;
+ fFallbackFormat.format(location, mzDisplayName, name, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+
+ uplname = fStringPool.get(name, status);
+ if (U_SUCCESS(status)) {
+ // Add the name to cache
+ PartialLocationKey* cacheKey = (PartialLocationKey *)uprv_malloc(sizeof(PartialLocationKey));
+ if (cacheKey != NULL) {
+ cacheKey->tzID = key.tzID;
+ cacheKey->mzID = key.mzID;
+ cacheKey->isLong = key.isLong;
+ uhash_put(fPartialLocationNamesMap, (void *)cacheKey, (void *)uplname, &status);
+ if (U_FAILURE(status)) {
+ uprv_free(cacheKey);
+ } else {
+ // put the name to the local trie as well
+ GNameInfo *nameinfo = (ZNameInfo *)uprv_malloc(sizeof(GNameInfo));
+ if (nameinfo != NULL) {
+ nameinfo->type = isLong ? UTZGNM_LONG : UTZGNM_SHORT;
+ nameinfo->tzID = key.tzID;
+ fGNamesTrie.put(uplname, nameinfo, status);
+ }
+ }
+ }
+ }
+ return uplname;
+}
+
+/*
+ * This method updates the cache and must be called with a lock,
+ * except initializer.
+ */
+void
+TZGNCore::loadStrings(const UnicodeString& tzCanonicalID) {
+ // load the generic location name
+ getGenericLocationName(tzCanonicalID);
+
+ // partial location names
+ UErrorCode status = U_ZERO_ERROR;
+
+ const UnicodeString *mzID;
+ UnicodeString goldenID;
+ UnicodeString mzGenName;
+ UTimeZoneNameType genNonLocTypes[] = {
+ UTZNM_LONG_GENERIC, UTZNM_SHORT_GENERIC,
+ UTZNM_UNKNOWN /*terminator*/
+ };
+
+ StringEnumeration *mzIDs = fTimeZoneNames->getAvailableMetaZoneIDs(tzCanonicalID, status);
+ while ((mzID = mzIDs->snext(status))) {
+ if (U_FAILURE(status)) {
+ break;
+ }
+ // if this time zone is not the golden zone of the meta zone,
+ // partial location name (such as "PT (Los Angeles)") might be
+ // available.
+ fTimeZoneNames->getReferenceZoneID(*mzID, fTargetRegion, goldenID);
+ if (tzCanonicalID != goldenID) {
+ for (int32_t i = 0; genNonLocTypes[i] != UTZNM_UNKNOWN; i++) {
+ fTimeZoneNames->getMetaZoneDisplayName(*mzID, genNonLocTypes[i], mzGenName);
+ if (!mzGenName.isEmpty()) {
+ // getPartialLocationName formats a name and put it into the trie
+ getPartialLocationName(tzCanonicalID, *mzID,
+ (genNonLocTypes[i] == UTZNM_LONG_GENERIC), mzGenName);
+ }
+ }
+ }
+ }
+ if (mzIDs != NULL) {
+ delete mzIDs;
+ }
+}
+
+int32_t
+TZGNCore::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
+ UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const {
+ timeType = UTZFMT_TIME_TYPE_UNKNOWN;
+ tzID.setToBogus();
+
+ if (U_FAILURE(status)) {
+ return 0;
+ }
+
+ // Find matches in the TimeZoneNames first
+ TimeZoneNames::MatchInfoCollection *tznamesMatches = findTimeZoneNames(text, start, types, status);
+ if (U_FAILURE(status)) {
+ return 0;
+ }
+
+ int32_t bestMatchLen = 0;
+ UTimeZoneFormatTimeType bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
+ UnicodeString bestMatchTzID;
+ // UBool isLongStandard = FALSE; // workaround - see the comments below
+ 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
+
+ if (tznamesMatches != NULL) {
+ UnicodeString mzID;
+ for (int32_t i = 0; i < tznamesMatches->size(); i++) {
+ int32_t len = tznamesMatches->getMatchLengthAt(i);
+ if (len > bestMatchLen) {
+ bestMatchLen = len;
+ if (!tznamesMatches->getTimeZoneIDAt(i, bestMatchTzID)) {
+ // name for a meta zone
+ if (tznamesMatches->getMetaZoneIDAt(i, mzID)) {
+ fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, bestMatchTzID);
+ }
+ }
+ UTimeZoneNameType nameType = tznamesMatches->getNameTypeAt(i);
+ if (U_FAILURE(status)) {
+ break;
+ }
+ switch (nameType) {
+ case UTZNM_LONG_STANDARD:
+ // isLongStandard = TRUE;
+ case UTZNM_SHORT_STANDARD: // this one is never used for generic, but just in case
+ isStandard = TRUE; // TODO: Remove this later, see the comments above.
+ bestMatchTimeType = UTZFMT_TIME_TYPE_STANDARD;
+ break;
+ case UTZNM_LONG_DAYLIGHT:
+ case UTZNM_SHORT_DAYLIGHT: // this one is never used for generic, but just in case
+ bestMatchTimeType = UTZFMT_TIME_TYPE_DAYLIGHT;
+ break;
+ default:
+ bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
+ }
+ }
+ }
+ delete tznamesMatches;
+ if (U_FAILURE(status)) {
+ return 0;
+ }
+
+ if (bestMatchLen == (text.length() - start)) {
+ // Full match
+
+ //tzID.setTo(bestMatchTzID);
+ //timeType = bestMatchTimeType;
+ //return bestMatchLen;
+
+ // TODO Some time zone uses a same name for the long standard name
+ // and the location name. When the match is a long standard name,
+ // then we need to check if the name is same with the location name.
+ // This is probably a data error or a design bug.
+/*
+ if (!isLongStandard) {
+ tzID.setTo(bestMatchTzID);
+ timeType = bestMatchTimeType;
+ return bestMatchLen;
+ }
+*/
+ // TODO The deprecation of commonlyUsed flag introduced the name
+ // conflict not only for long standard names, but short standard names too.
+ // These short names (found in zh_Hant) should be gone once we clean
+ // up CLDR time zone display name data. Once the short name conflict
+ // problem (with location name) is resolved, we should change the condition
+ // below back to the original one above. -Yoshito (2011-09-14)
+ if (!isStandard) {
+ tzID.setTo(bestMatchTzID);
+ timeType = bestMatchTimeType;
+ return bestMatchLen;
+ }
+ }
+ }
+
+ // Find matches in the local trie
+ TimeZoneGenericNameMatchInfo *localMatches = findLocal(text, start, types, status);
+ if (U_FAILURE(status)) {
+ return 0;
+ }
+ if (localMatches != NULL) {
+ for (int32_t i = 0; i < localMatches->size(); i++) {
+ int32_t len = localMatches->getMatchLength(i);
+
+ // TODO See the above TODO. We use len >= bestMatchLen
+ // because of the long standard/location name collision
+ // problem. If it is also a location name, carrying
+ // timeType = UTZFMT_TIME_TYPE_STANDARD will cause a
+ // problem in SimpleDateFormat
+ if (len >= bestMatchLen) {
+ bestMatchLen = localMatches->getMatchLength(i);
+ bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; // because generic
+ localMatches->getTimeZoneID(i, bestMatchTzID);
+ }
+ }
+ delete localMatches;
+ }
+
+ if (bestMatchLen > 0) {
+ timeType = bestMatchTimeType;
+ tzID.setTo(bestMatchTzID);
+ }
+ return bestMatchLen;
+}
+
+TimeZoneGenericNameMatchInfo*
+TZGNCore::findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
+ GNameSearchHandler handler(types);
+
+ TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
+
+ umtx_lock(&gLock);
+ {
+ fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
+ }
+ umtx_unlock(&gLock);
+
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+
+ TimeZoneGenericNameMatchInfo *gmatchInfo = NULL;
+
+ int32_t maxLen = 0;
+ UVector *results = handler.getMatches(maxLen);
+ if (results != NULL && ((maxLen == (text.length() - start)) || fGNamesTrieFullyLoaded)) {
+ // perfect match
+ gmatchInfo = new TimeZoneGenericNameMatchInfo(results);
+ if (gmatchInfo == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ delete results;
+ return NULL;
+ }
+ return gmatchInfo;
+ }
+
+ if (results != NULL) {
+ delete results;
+ }
+
+ // All names are not yet loaded into the local trie.
+ // Load all available names into the trie. This could be very heavy.
+ umtx_lock(&gLock);
+ {
+ if (!fGNamesTrieFullyLoaded) {
+ StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
+ if (U_SUCCESS(status)) {
+ const UnicodeString *tzID;
+ while ((tzID = tzIDs->snext(status))) {
+ if (U_FAILURE(status)) {
+ break;
+ }
+ nonConstThis->loadStrings(*tzID);
+ }
+ }
+ if (tzIDs != NULL) {
+ delete tzIDs;
+ }
+
+ if (U_SUCCESS(status)) {
+ nonConstThis->fGNamesTrieFullyLoaded = TRUE;
+ }
+ }
+ }
+ umtx_unlock(&gLock);
+
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+
+ umtx_lock(&gLock);
+ {
+ // now try it again
+ fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
+ }
+ umtx_unlock(&gLock);
+
+ results = handler.getMatches(maxLen);
+ if (results != NULL && maxLen > 0) {
+ gmatchInfo = new TimeZoneGenericNameMatchInfo(results);
+ if (gmatchInfo == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ delete results;
+ return NULL;
+ }
+ }
+
+ return gmatchInfo;
+}
+
+TimeZoneNames::MatchInfoCollection*
+TZGNCore::findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
+ // Check if the target name typs is really in the TimeZoneNames
+ uint32_t nameTypes = 0;
+ if (types & UTZGNM_LONG) {
+ nameTypes |= (UTZNM_LONG_GENERIC | UTZNM_LONG_STANDARD);
+ }
+ if (types & UTZGNM_SHORT) {
+ nameTypes |= (UTZNM_SHORT_GENERIC | UTZNM_SHORT_STANDARD);
+ }
+
+ if (types) {
+ // Find matches in the TimeZoneNames
+ return fTimeZoneNames->find(text, start, nameTypes, status);
+ }
+
+ return NULL;
+}
+
+typedef struct TZGNCoreRef {
+ TZGNCore* obj;
+ int32_t refCount;
+ double lastAccess;
+} TZGNCoreRef;
+
+// TZGNCore object cache handling
+static UMutex gTZGNLock = U_MUTEX_INITIALIZER;
+static UHashtable *gTZGNCoreCache = NULL;
+static UBool gTZGNCoreCacheInitialized = FALSE;
+
+// Access count - incremented every time up to SWEEP_INTERVAL,
+// then reset to 0
+static int32_t gAccessCount = 0;
+
+// Interval for calling the cache sweep function - every 100 times
+#define SWEEP_INTERVAL 100
+
+// Cache expiration in millisecond. When a cached entry is no
+// longer referenced and exceeding this threshold since last
+// access time, then the cache entry will be deleted by the sweep
+// function. For now, 3 minutes.
+#define CACHE_EXPIRATION 180000.0
+
+U_CDECL_BEGIN
+/**
+ * Cleanup callback func
+ */
+static UBool U_CALLCONV tzgnCore_cleanup(void)
+{
+ if (gTZGNCoreCache != NULL) {
+ uhash_close(gTZGNCoreCache);
+ gTZGNCoreCache = NULL;
+ }
+ gTZGNCoreCacheInitialized = FALSE;
+ return TRUE;
+}
+
+/**
+ * Deleter for TZGNCoreRef
+ */
+static void U_CALLCONV
+deleteTZGNCoreRef(void *obj) {
+ icu::TZGNCoreRef *entry = (icu::TZGNCoreRef*)obj;
+ delete (icu::TZGNCore*) entry->obj;
+ uprv_free(entry);
+}
+U_CDECL_END
+
+/**
+ * Function used for removing unreferrenced cache entries exceeding
+ * the expiration time. This function must be called with in the mutex
+ * block.
+ */
+static void sweepCache() {
+ int32_t pos = UHASH_FIRST;
+ const UHashElement* elem;
+ double now = (double)uprv_getUTCtime();
+
+ while ((elem = uhash_nextElement(gTZGNCoreCache, &pos))) {
+ TZGNCoreRef *entry = (TZGNCoreRef *)elem->value.pointer;
+ if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) {
+ // delete this entry
+ uhash_removeElement(gTZGNCoreCache, elem);
+ }
+ }
+}
+
+TimeZoneGenericNames::TimeZoneGenericNames()
+: fRef(0) {
+}
+
+TimeZoneGenericNames::~TimeZoneGenericNames() {
+ umtx_lock(&gTZGNLock);
+ {
+ U_ASSERT(fRef->refCount > 0);
+ // Just decrement the reference count
+ fRef->refCount--;
+ }
+ umtx_unlock(&gTZGNLock);
+}
+
+TimeZoneGenericNames*
+TimeZoneGenericNames::createInstance(const Locale& locale, UErrorCode& status) {
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ TimeZoneGenericNames* instance = new TimeZoneGenericNames();
+ if (instance == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
+ }
+
+ TZGNCoreRef *cacheEntry = NULL;
+ {
+ Mutex lock(&gTZGNLock);
+
+ if (!gTZGNCoreCacheInitialized) {
+ // Create empty hashtable
+ gTZGNCoreCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
+ if (U_SUCCESS(status)) {
+ uhash_setKeyDeleter(gTZGNCoreCache, uprv_free);
+ uhash_setValueDeleter(gTZGNCoreCache, deleteTZGNCoreRef);
+ gTZGNCoreCacheInitialized = TRUE;
+ ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEGENERICNAMES, tzgnCore_cleanup);
+ }
+ }
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+
+ // Check the cache, if not available, create new one and cache
+ const char *key = locale.getName();
+ cacheEntry = (TZGNCoreRef *)uhash_get(gTZGNCoreCache, key);
+ if (cacheEntry == NULL) {
+ TZGNCore *tzgnCore = NULL;
+ char *newKey = NULL;
+
+ tzgnCore = new TZGNCore(locale, status);
+ if (tzgnCore == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ }
+ if (U_SUCCESS(status)) {
+ newKey = (char *)uprv_malloc(uprv_strlen(key) + 1);
+ if (newKey == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ } else {
+ uprv_strcpy(newKey, key);
+ }
+ }
+ if (U_SUCCESS(status)) {
+ cacheEntry = (TZGNCoreRef *)uprv_malloc(sizeof(TZGNCoreRef));
+ if (cacheEntry == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ } else {
+ cacheEntry->obj = tzgnCore;
+ cacheEntry->refCount = 1;
+ cacheEntry->lastAccess = (double)uprv_getUTCtime();
+
+ uhash_put(gTZGNCoreCache, newKey, cacheEntry, &status);
+ }
+ }
+ if (U_FAILURE(status)) {
+ if (tzgnCore != NULL) {
+ delete tzgnCore;
+ }
+ if (newKey != NULL) {
+ uprv_free(newKey);
+ }
+ if (cacheEntry != NULL) {
+ uprv_free(cacheEntry);
+ }
+ cacheEntry = NULL;
+ }
+ } else {
+ // Update the reference count
+ cacheEntry->refCount++;
+ cacheEntry->lastAccess = (double)uprv_getUTCtime();
+ }
+ gAccessCount++;
+ if (gAccessCount >= SWEEP_INTERVAL) {
+ // sweep
+ sweepCache();
+ gAccessCount = 0;
+ }
+ } // End of mutex locked block
+
+ if (cacheEntry == NULL) {
+ delete instance;
+ return NULL;
+ }
+
+ instance->fRef = cacheEntry;
+ return instance;
+}
+
+UBool
+TimeZoneGenericNames::operator==(const TimeZoneGenericNames& other) const {
+ // Just compare if the other object also use the same
+ // ref entry
+ return fRef == other.fRef;
+}
+
+TimeZoneGenericNames*
+TimeZoneGenericNames::clone() const {
+ TimeZoneGenericNames* other = new TimeZoneGenericNames();
+ if (other) {
+ umtx_lock(&gTZGNLock);
+ {
+ // Just increments the reference count
+ fRef->refCount++;
+ other->fRef = fRef;
+ }
+ umtx_unlock(&gTZGNLock);
+ }
+ return other;
+}
+
+UnicodeString&
+TimeZoneGenericNames::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,
+ UDate date, UnicodeString& name) const {
+ return fRef->obj->getDisplayName(tz, type, date, name);
+}
+
+UnicodeString&
+TimeZoneGenericNames::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
+ return fRef->obj->getGenericLocationName(tzCanonicalID, name);
+}
+
+int32_t
+TimeZoneGenericNames::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
+ UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const {
+ return fRef->obj->findBestMatch(text, start, types, tzID, timeType, status);
+}
+
+U_NAMESPACE_END
+#endif