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