Home | History | Annotate | Download | only in i18n
      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-2015, International Business Machines Corporation and
      6 * others. All Rights Reserved.
      7 *******************************************************************************
      8 */
      9 
     10 #include "unicode/utypes.h"
     11 
     12 #if !UCONFIG_NO_FORMATTING
     13 
     14 #include "unicode/calendar.h"
     15 #include "unicode/tzfmt.h"
     16 #include "unicode/numsys.h"
     17 #include "unicode/strenum.h"
     18 #include "unicode/uchar.h"
     19 #include "unicode/udat.h"
     20 #include "unicode/ustring.h"
     21 #include "unicode/utf16.h"
     22 #include "tzgnames.h"
     23 #include "cmemory.h"
     24 #include "cstring.h"
     25 #include "putilimp.h"
     26 #include "uassert.h"
     27 #include "ucln_in.h"
     28 #include "umutex.h"
     29 #include "uresimp.h"
     30 #include "ureslocs.h"
     31 #include "uvector.h"
     32 #include "zonemeta.h"
     33 #include "tznames_impl.h"   // TextTrieMap
     34 #include "patternprops.h"
     35 
     36 U_NAMESPACE_BEGIN
     37 
     38 // Bit flags used by the parse method.
     39 // The order must match UTimeZoneFormatStyle enum.
     40 #define ISO_Z_STYLE_FLAG 0x0080
     41 #define ISO_LOCAL_STYLE_FLAG 0x0100
     42 static const int16_t STYLE_PARSE_FLAGS[] = {
     43     0x0001, // UTZFMT_STYLE_GENERIC_LOCATION,
     44     0x0002, // UTZFMT_STYLE_GENERIC_LONG,
     45     0x0004, // UTZFMT_STYLE_GENERIC_SHORT,
     46     0x0008, // UTZFMT_STYLE_SPECIFIC_LONG,
     47     0x0010, // UTZFMT_STYLE_SPECIFIC_SHORT,
     48     0x0020, // UTZFMT_STYLE_LOCALIZED_GMT,
     49     0x0040, // UTZFMT_STYLE_LOCALIZED_GMT_SHORT,
     50     ISO_Z_STYLE_FLAG,       // UTZFMT_STYLE_ISO_BASIC_SHORT,
     51     ISO_LOCAL_STYLE_FLAG,   // UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT,
     52     ISO_Z_STYLE_FLAG,       // UTZFMT_STYLE_ISO_BASIC_FIXED,
     53     ISO_LOCAL_STYLE_FLAG,   // UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED,
     54     ISO_Z_STYLE_FLAG,       // UTZFMT_STYLE_ISO_BASIC_FULL,
     55     ISO_LOCAL_STYLE_FLAG,   // UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL,
     56     ISO_Z_STYLE_FLAG,       // UTZFMT_STYLE_ISO_EXTENDED_FIXED,
     57     ISO_LOCAL_STYLE_FLAG,   // UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED,
     58     ISO_Z_STYLE_FLAG,       // UTZFMT_STYLE_ISO_EXTENDED_FULL,
     59     ISO_LOCAL_STYLE_FLAG,   // UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL,
     60     0x0200, // UTZFMT_STYLE_ZONE_ID,
     61     0x0400, // UTZFMT_STYLE_ZONE_ID_SHORT,
     62     0x0800  // UTZFMT_STYLE_EXEMPLAR_LOCATION
     63 };
     64 
     65 static const char gZoneStringsTag[] = "zoneStrings";
     66 static const char gGmtFormatTag[]= "gmtFormat";
     67 static const char gGmtZeroFormatTag[] = "gmtZeroFormat";
     68 static const char gHourFormatTag[]= "hourFormat";
     69 
     70 static const UChar TZID_GMT[] = {0x0045, 0x0074, 0x0063, 0x002F, 0x0047, 0x004D, 0x0054, 0};    // Etc/GMT
     71 static const UChar UNKNOWN_ZONE_ID[] = {
     72     0x0045, 0x0074, 0x0063, 0x002F, 0x0055, 0x006E, 0x006B, 0x006E, 0x006F, 0x0077, 0x006E, 0}; // Etc/Unknown
     73 static const UChar UNKNOWN_SHORT_ZONE_ID[] = {0x0075, 0x006E, 0x006B, 0};   // unk
     74 static const UChar UNKNOWN_LOCATION[] = {0x0055, 0x006E, 0x006B, 0x006E, 0x006F, 0x0077, 0x006E, 0};    // Unknown
     75 
     76 static const UChar DEFAULT_GMT_PATTERN[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0}; // GMT{0}
     77 //static const UChar DEFAULT_GMT_ZERO[] = {0x0047, 0x004D, 0x0054, 0}; // GMT
     78 static const UChar DEFAULT_GMT_POSITIVE_HM[] = {0x002B, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // +H:mm
     79 static const UChar DEFAULT_GMT_POSITIVE_HMS[] = {0x002B, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // +H:mm:ss
     80 static const UChar DEFAULT_GMT_NEGATIVE_HM[] = {0x002D, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // -H:mm
     81 static const UChar DEFAULT_GMT_NEGATIVE_HMS[] = {0x002D, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // -H:mm:ss
     82 static const UChar DEFAULT_GMT_POSITIVE_H[] = {0x002B, 0x0048, 0}; // +H
     83 static const UChar DEFAULT_GMT_NEGATIVE_H[] = {0x002D, 0x0048, 0}; // -H
     84 
     85 static const UChar32 DEFAULT_GMT_DIGITS[] = {
     86     0x0030, 0x0031, 0x0032, 0x0033, 0x0034,
     87     0x0035, 0x0036, 0x0037, 0x0038, 0x0039
     88 };
     89 
     90 static const UChar DEFAULT_GMT_OFFSET_SEP = 0x003A; // ':'
     91 
     92 static const UChar ARG0[] = {0x007B, 0x0030, 0x007D};   // "{0}"
     93 static const int32_t ARG0_LEN = 3;
     94 
     95 static const UChar DEFAULT_GMT_OFFSET_MINUTE_PATTERN[] = {0x006D, 0x006D, 0};   // "mm"
     96 static const UChar DEFAULT_GMT_OFFSET_SECOND_PATTERN[] = {0x0073, 0x0073, 0};   // "ss"
     97 
     98 static const UChar ALT_GMT_STRINGS[][4] = {
     99     {0x0047, 0x004D, 0x0054, 0},    // GMT
    100     {0x0055, 0x0054, 0x0043, 0},    // UTC
    101     {0x0055, 0x0054, 0, 0},         // UT
    102     {0, 0, 0, 0}
    103 };
    104 
    105 // Order of GMT offset pattern parsing, *_HMS must be evaluated first
    106 // because *_HM is most likely a substring of *_HMS
    107 static const int32_t PARSE_GMT_OFFSET_TYPES[] = {
    108     UTZFMT_PAT_POSITIVE_HMS,
    109     UTZFMT_PAT_NEGATIVE_HMS,
    110     UTZFMT_PAT_POSITIVE_HM,
    111     UTZFMT_PAT_NEGATIVE_HM,
    112     UTZFMT_PAT_POSITIVE_H,
    113     UTZFMT_PAT_NEGATIVE_H,
    114     -1
    115 };
    116 
    117 static const UChar SINGLEQUOTE  = 0x0027;
    118 static const UChar PLUS         = 0x002B;
    119 static const UChar MINUS        = 0x002D;
    120 static const UChar ISO8601_UTC  = 0x005A;   // 'Z'
    121 static const UChar ISO8601_SEP  = 0x003A;   // ':'
    122 
    123 static const int32_t MILLIS_PER_HOUR = 60 * 60 * 1000;
    124 static const int32_t MILLIS_PER_MINUTE = 60 * 1000;
    125 static const int32_t MILLIS_PER_SECOND = 1000;
    126 
    127 // Maximum offset (exclusive) in millisecond supported by offset formats
    128 static int32_t MAX_OFFSET = 24 * MILLIS_PER_HOUR;
    129 
    130 // Maximum values for GMT offset fields
    131 static const int32_t MAX_OFFSET_HOUR = 23;
    132 static const int32_t MAX_OFFSET_MINUTE = 59;
    133 static const int32_t MAX_OFFSET_SECOND = 59;
    134 
    135 static const int32_t UNKNOWN_OFFSET = 0x7FFFFFFF;
    136 
    137 static const int32_t ALL_SIMPLE_NAME_TYPES = UTZNM_LONG_STANDARD | UTZNM_LONG_DAYLIGHT | UTZNM_SHORT_STANDARD | UTZNM_SHORT_DAYLIGHT | UTZNM_EXEMPLAR_LOCATION;
    138 static const int32_t ALL_GENERIC_NAME_TYPES = UTZGNM_LOCATION | UTZGNM_LONG | UTZGNM_SHORT;
    139 
    140 #define DIGIT_VAL(c) (0x0030 <= (c) && (c) <= 0x0039 ? (c) - 0x0030 : -1)
    141 #define MAX_OFFSET_DIGITS 6
    142 
    143 // Time Zone ID/Short ID trie
    144 static TextTrieMap *gZoneIdTrie = NULL;
    145 static icu::UInitOnce gZoneIdTrieInitOnce = U_INITONCE_INITIALIZER;
    146 
    147 static TextTrieMap *gShortZoneIdTrie = NULL;
    148 static icu::UInitOnce gShortZoneIdTrieInitOnce = U_INITONCE_INITIALIZER;
    149 
    150 static UMutex gLock = U_MUTEX_INITIALIZER;
    151 
    152 U_CDECL_BEGIN
    153 /**
    154  * Cleanup callback func
    155  */
    156 static UBool U_CALLCONV tzfmt_cleanup(void)
    157 {
    158     if (gZoneIdTrie != NULL) {
    159         delete gZoneIdTrie;
    160     }
    161     gZoneIdTrie = NULL;
    162     gZoneIdTrieInitOnce.reset();
    163 
    164     if (gShortZoneIdTrie != NULL) {
    165         delete gShortZoneIdTrie;
    166     }
    167     gShortZoneIdTrie = NULL;
    168     gShortZoneIdTrieInitOnce.reset();
    169 
    170     return TRUE;
    171 }
    172 U_CDECL_END
    173 
    174 // ------------------------------------------------------------------
    175 // GMTOffsetField
    176 //
    177 // This class represents a localized GMT offset pattern
    178 // item and used by TimeZoneFormat
    179 // ------------------------------------------------------------------
    180 class GMTOffsetField : public UMemory {
    181 public:
    182     enum FieldType {
    183         TEXT = 0,
    184         HOUR = 1,
    185         MINUTE = 2,
    186         SECOND = 4
    187     };
    188 
    189     virtual ~GMTOffsetField();
    190 
    191     static GMTOffsetField* createText(const UnicodeString& text, UErrorCode& status);
    192     static GMTOffsetField* createTimeField(FieldType type, uint8_t width, UErrorCode& status);
    193     static UBool isValid(FieldType type, int32_t width);
    194     static FieldType getTypeByLetter(UChar ch);
    195 
    196     FieldType getType() const;
    197     uint8_t getWidth() const;
    198     const UChar* getPatternText(void) const;
    199 
    200 private:
    201     UChar* fText;
    202     FieldType fType;
    203     uint8_t fWidth;
    204 
    205     GMTOffsetField();
    206 };
    207 
    208 GMTOffsetField::GMTOffsetField()
    209 : fText(NULL), fType(TEXT), fWidth(0) {
    210 }
    211 
    212 GMTOffsetField::~GMTOffsetField() {
    213     if (fText) {
    214         uprv_free(fText);
    215     }
    216 }
    217 
    218 GMTOffsetField*
    219 GMTOffsetField::createText(const UnicodeString& text, UErrorCode& status) {
    220     if (U_FAILURE(status)) {
    221         return NULL;
    222     }
    223     GMTOffsetField* result = new GMTOffsetField();
    224     if (result == NULL) {
    225         status = U_MEMORY_ALLOCATION_ERROR;
    226         return NULL;
    227     }
    228 
    229     int32_t len = text.length();
    230     result->fText = (UChar*)uprv_malloc((len + 1) * sizeof(UChar));
    231     if (result->fText == NULL) {
    232         status = U_MEMORY_ALLOCATION_ERROR;
    233         delete result;
    234         return NULL;
    235     }
    236     u_strncpy(result->fText, text.getBuffer(), len);
    237     result->fText[len] = 0;
    238     result->fType = TEXT;
    239 
    240     return result;
    241 }
    242 
    243 GMTOffsetField*
    244 GMTOffsetField::createTimeField(FieldType type, uint8_t width, UErrorCode& status) {
    245     U_ASSERT(type != TEXT);
    246     if (U_FAILURE(status)) {
    247         return NULL;
    248     }
    249     GMTOffsetField* result = new GMTOffsetField();
    250     if (result == NULL) {
    251         status = U_MEMORY_ALLOCATION_ERROR;
    252         return NULL;
    253     }
    254 
    255     result->fType = type;
    256     result->fWidth = width;
    257 
    258     return result;
    259 }
    260 
    261 UBool
    262 GMTOffsetField::isValid(FieldType type, int32_t width) {
    263     switch (type) {
    264     case HOUR:
    265         return (width == 1 || width == 2);
    266     case MINUTE:
    267     case SECOND:
    268         return (width == 2);
    269     default:
    270         U_ASSERT(FALSE);
    271     }
    272     return (width > 0);
    273 }
    274 
    275 GMTOffsetField::FieldType
    276 GMTOffsetField::getTypeByLetter(UChar ch) {
    277     if (ch == 0x0048 /* H */) {
    278         return HOUR;
    279     } else if (ch == 0x006D /* m */) {
    280         return MINUTE;
    281     } else if (ch == 0x0073 /* s */) {
    282         return SECOND;
    283     }
    284     return TEXT;
    285 }
    286 
    287 inline GMTOffsetField::FieldType
    288 GMTOffsetField::getType() const {
    289      return fType;
    290  }
    291 
    292 inline uint8_t
    293 GMTOffsetField::getWidth() const {
    294     return fWidth;
    295 }
    296 
    297 inline const UChar*
    298 GMTOffsetField::getPatternText(void) const {
    299     return fText;
    300 }
    301 
    302 
    303 U_CDECL_BEGIN
    304 static void U_CALLCONV
    305 deleteGMTOffsetField(void *obj) {
    306     delete static_cast<GMTOffsetField *>(obj);
    307 }
    308 U_CDECL_END
    309 
    310 
    311 // ------------------------------------------------------------------
    312 // TimeZoneFormat
    313 // ------------------------------------------------------------------
    314 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeZoneFormat)
    315 
    316 TimeZoneFormat::TimeZoneFormat(const Locale& locale, UErrorCode& status)
    317 : fLocale(locale), fTimeZoneNames(NULL), fTimeZoneGenericNames(NULL),
    318   fDefParseOptionFlags(0), fTZDBTimeZoneNames(NULL) {
    319 
    320     for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
    321         fGMTOffsetPatternItems[i] = NULL;
    322     }
    323 
    324     const char* region = fLocale.getCountry();
    325     int32_t regionLen = static_cast<int32_t>(uprv_strlen(region));
    326     if (regionLen == 0) {
    327         char loc[ULOC_FULLNAME_CAPACITY];
    328         uloc_addLikelySubtags(fLocale.getName(), loc, sizeof(loc), &status);
    329 
    330         regionLen = uloc_getCountry(loc, fTargetRegion, sizeof(fTargetRegion), &status);
    331         if (U_SUCCESS(status)) {
    332             fTargetRegion[regionLen] = 0;
    333         } else {
    334             return;
    335         }
    336     } else if (regionLen < (int32_t)sizeof(fTargetRegion)) {
    337         uprv_strcpy(fTargetRegion, region);
    338     } else {
    339         fTargetRegion[0] = 0;
    340     }
    341 
    342     fTimeZoneNames = TimeZoneNames::createInstance(locale, status);
    343     // fTimeZoneGenericNames is lazily instantiated
    344     if (U_FAILURE(status)) {
    345         return;
    346     }
    347 
    348     const UChar* gmtPattern = NULL;
    349     const UChar* hourFormats = NULL;
    350 
    351     UResourceBundle *zoneBundle = ures_open(U_ICUDATA_ZONE, locale.getName(), &status);
    352     UResourceBundle *zoneStringsArray = ures_getByKeyWithFallback(zoneBundle, gZoneStringsTag, NULL, &status);
    353     if (U_SUCCESS(status)) {
    354         const UChar* resStr;
    355         int32_t len;
    356         resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gGmtFormatTag, &len, &status);
    357         if (len > 0) {
    358             gmtPattern = resStr;
    359         }
    360         resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gGmtZeroFormatTag, &len, &status);
    361         if (len > 0) {
    362             fGMTZeroFormat.setTo(TRUE, resStr, len);
    363         }
    364         resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gHourFormatTag, &len, &status);
    365         if (len > 0) {
    366             hourFormats = resStr;
    367         }
    368         ures_close(zoneStringsArray);
    369         ures_close(zoneBundle);
    370     }
    371 
    372     if (gmtPattern == NULL) {
    373         gmtPattern = DEFAULT_GMT_PATTERN;
    374     }
    375     initGMTPattern(UnicodeString(TRUE, gmtPattern, -1), status);
    376 
    377     UBool useDefaultOffsetPatterns = TRUE;
    378     if (hourFormats) {
    379         UChar *sep = u_strchr(hourFormats, (UChar)0x003B /* ';' */);
    380         if (sep != NULL) {
    381             UErrorCode tmpStatus = U_ZERO_ERROR;
    382             fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM].setTo(FALSE, hourFormats, (int32_t)(sep - hourFormats));
    383             fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM].setTo(TRUE, sep + 1, -1);
    384             expandOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HMS], tmpStatus);
    385             expandOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HMS], tmpStatus);
    386             truncateOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_H], tmpStatus);
    387             truncateOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_H], tmpStatus);
    388             if (U_SUCCESS(tmpStatus)) {
    389                 useDefaultOffsetPatterns = FALSE;
    390             }
    391         }
    392     }
    393     if (useDefaultOffsetPatterns) {
    394         fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_H].setTo(TRUE, DEFAULT_GMT_POSITIVE_H, -1);
    395         fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM].setTo(TRUE, DEFAULT_GMT_POSITIVE_HM, -1);
    396         fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HMS].setTo(TRUE, DEFAULT_GMT_POSITIVE_HMS, -1);
    397         fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_H].setTo(TRUE, DEFAULT_GMT_NEGATIVE_H, -1);
    398         fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM].setTo(TRUE, DEFAULT_GMT_NEGATIVE_HM, -1);
    399         fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HMS].setTo(TRUE, DEFAULT_GMT_NEGATIVE_HMS, -1);
    400     }
    401     initGMTOffsetPatterns(status);
    402 
    403     NumberingSystem* ns = NumberingSystem::createInstance(locale, status);
    404     UBool useDefDigits = TRUE;
    405     if (ns && !ns->isAlgorithmic()) {
    406         UnicodeString digits = ns->getDescription();
    407         useDefDigits = !toCodePoints(digits, fGMTOffsetDigits, 10);
    408     }
    409     if (useDefDigits) {
    410         uprv_memcpy(fGMTOffsetDigits, DEFAULT_GMT_DIGITS, sizeof(UChar32) * 10);
    411     }
    412     delete ns;
    413 }
    414 
    415 TimeZoneFormat::TimeZoneFormat(const TimeZoneFormat& other)
    416 : Format(other), fTimeZoneNames(NULL), fTimeZoneGenericNames(NULL),
    417   fTZDBTimeZoneNames(NULL) {
    418 
    419     for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
    420         fGMTOffsetPatternItems[i] = NULL;
    421     }
    422     *this = other;
    423 }
    424 
    425 
    426 TimeZoneFormat::~TimeZoneFormat() {
    427     delete fTimeZoneNames;
    428     delete fTimeZoneGenericNames;
    429     delete fTZDBTimeZoneNames;
    430     for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
    431         delete fGMTOffsetPatternItems[i];
    432     }
    433 }
    434 
    435 TimeZoneFormat&
    436 TimeZoneFormat::operator=(const TimeZoneFormat& other) {
    437     if (this == &other) {
    438         return *this;
    439     }
    440 
    441     delete fTimeZoneNames;
    442     delete fTimeZoneGenericNames;
    443     fTimeZoneGenericNames = NULL;
    444     delete fTZDBTimeZoneNames;
    445     fTZDBTimeZoneNames = NULL;
    446 
    447     fLocale = other.fLocale;
    448     uprv_memcpy(fTargetRegion, other.fTargetRegion, sizeof(fTargetRegion));
    449 
    450     fTimeZoneNames = other.fTimeZoneNames->clone();
    451     if (other.fTimeZoneGenericNames) {
    452         // TODO: this test has dubious thread safety.
    453         fTimeZoneGenericNames = other.fTimeZoneGenericNames->clone();
    454     }
    455 
    456     fGMTPattern = other.fGMTPattern;
    457     fGMTPatternPrefix = other.fGMTPatternPrefix;
    458     fGMTPatternSuffix = other.fGMTPatternSuffix;
    459 
    460     UErrorCode status = U_ZERO_ERROR;
    461     for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
    462         fGMTOffsetPatterns[i] = other.fGMTOffsetPatterns[i];
    463         delete fGMTOffsetPatternItems[i];
    464         fGMTOffsetPatternItems[i] = NULL;
    465     }
    466     initGMTOffsetPatterns(status);
    467     U_ASSERT(U_SUCCESS(status));
    468 
    469     fGMTZeroFormat = other.fGMTZeroFormat;
    470 
    471     uprv_memcpy(fGMTOffsetDigits, other.fGMTOffsetDigits, sizeof(fGMTOffsetDigits));
    472 
    473     fDefParseOptionFlags = other.fDefParseOptionFlags;
    474 
    475     return *this;
    476 }
    477 
    478 
    479 UBool
    480 TimeZoneFormat::operator==(const Format& other) const {
    481     TimeZoneFormat* tzfmt = (TimeZoneFormat*)&other;
    482 
    483     UBool isEqual =
    484             fLocale == tzfmt->fLocale
    485             && fGMTPattern == tzfmt->fGMTPattern
    486             && fGMTZeroFormat == tzfmt->fGMTZeroFormat
    487             && *fTimeZoneNames == *tzfmt->fTimeZoneNames;
    488 
    489     for (int32_t i = 0; i < UTZFMT_PAT_COUNT && isEqual; i++) {
    490         isEqual = fGMTOffsetPatterns[i] == tzfmt->fGMTOffsetPatterns[i];
    491     }
    492     for (int32_t i = 0; i < 10 && isEqual; i++) {
    493         isEqual = fGMTOffsetDigits[i] == tzfmt->fGMTOffsetDigits[i];
    494     }
    495     // TODO
    496     // Check fTimeZoneGenericNames. For now,
    497     // if fTimeZoneNames is same, fTimeZoneGenericNames should
    498     // be also equivalent.
    499     return isEqual;
    500 }
    501 
    502 Format*
    503 TimeZoneFormat::clone() const {
    504     return new TimeZoneFormat(*this);
    505 }
    506 
    507 TimeZoneFormat* U_EXPORT2
    508 TimeZoneFormat::createInstance(const Locale& locale, UErrorCode& status) {
    509     TimeZoneFormat* tzfmt = new TimeZoneFormat(locale, status);
    510     if (U_SUCCESS(status)) {
    511         return tzfmt;
    512     }
    513     delete tzfmt;
    514     return NULL;
    515 }
    516 
    517 // ------------------------------------------------------------------
    518 // Setter and Getter
    519 
    520 const TimeZoneNames*
    521 TimeZoneFormat::getTimeZoneNames() const {
    522     return (const TimeZoneNames*)fTimeZoneNames;
    523 }
    524 
    525 void
    526 TimeZoneFormat::adoptTimeZoneNames(TimeZoneNames *tznames) {
    527     delete fTimeZoneNames;
    528     fTimeZoneNames = tznames;
    529 
    530     // TODO - We should also update fTimeZoneGenericNames
    531 }
    532 
    533 void
    534 TimeZoneFormat::setTimeZoneNames(const TimeZoneNames &tznames) {
    535     delete fTimeZoneNames;
    536     fTimeZoneNames = tznames.clone();
    537 
    538     // TODO - We should also update fTimeZoneGenericNames
    539 }
    540 
    541 void
    542 TimeZoneFormat::setDefaultParseOptions(uint32_t flags) {
    543     fDefParseOptionFlags = flags;
    544 }
    545 
    546 uint32_t
    547 TimeZoneFormat::getDefaultParseOptions(void) const {
    548     return fDefParseOptionFlags;
    549 }
    550 
    551 
    552 UnicodeString&
    553 TimeZoneFormat::getGMTPattern(UnicodeString& pattern) const {
    554     return pattern.setTo(fGMTPattern);
    555 }
    556 
    557 void
    558 TimeZoneFormat::setGMTPattern(const UnicodeString& pattern, UErrorCode& status) {
    559     initGMTPattern(pattern, status);
    560 }
    561 
    562 UnicodeString&
    563 TimeZoneFormat::getGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type, UnicodeString& pattern) const {
    564     return pattern.setTo(fGMTOffsetPatterns[type]);
    565 }
    566 
    567 void
    568 TimeZoneFormat::setGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type, const UnicodeString& pattern, UErrorCode& status) {
    569     if (U_FAILURE(status)) {
    570         return;
    571     }
    572     if (pattern == fGMTOffsetPatterns[type]) {
    573         // No need to reset
    574         return;
    575     }
    576 
    577     OffsetFields required = FIELDS_HM;
    578     switch (type) {
    579     case UTZFMT_PAT_POSITIVE_H:
    580     case UTZFMT_PAT_NEGATIVE_H:
    581         required = FIELDS_H;
    582         break;
    583     case UTZFMT_PAT_POSITIVE_HM:
    584     case UTZFMT_PAT_NEGATIVE_HM:
    585         required = FIELDS_HM;
    586         break;
    587     case UTZFMT_PAT_POSITIVE_HMS:
    588     case UTZFMT_PAT_NEGATIVE_HMS:
    589         required = FIELDS_HMS;
    590         break;
    591     default:
    592         U_ASSERT(FALSE);
    593         break;
    594     }
    595 
    596     UVector* patternItems = parseOffsetPattern(pattern, required, status);
    597     if (patternItems == NULL) {
    598         return;
    599     }
    600 
    601     fGMTOffsetPatterns[type].setTo(pattern);
    602     delete fGMTOffsetPatternItems[type];
    603     fGMTOffsetPatternItems[type] = patternItems;
    604     checkAbuttingHoursAndMinutes();
    605 }
    606 
    607 UnicodeString&
    608 TimeZoneFormat::getGMTOffsetDigits(UnicodeString& digits) const {
    609     digits.remove();
    610     for (int32_t i = 0; i < 10; i++) {
    611         digits.append(fGMTOffsetDigits[i]);
    612     }
    613     return digits;
    614 }
    615 
    616 void
    617 TimeZoneFormat::setGMTOffsetDigits(const UnicodeString& digits, UErrorCode& status) {
    618     if (U_FAILURE(status)) {
    619         return;
    620     }
    621     UChar32 digitArray[10];
    622     if (!toCodePoints(digits, digitArray, 10)) {
    623         status = U_ILLEGAL_ARGUMENT_ERROR;
    624         return;
    625     }
    626     uprv_memcpy(fGMTOffsetDigits, digitArray, sizeof(UChar32)*10);
    627 }
    628 
    629 UnicodeString&
    630 TimeZoneFormat::getGMTZeroFormat(UnicodeString& gmtZeroFormat) const {
    631     return gmtZeroFormat.setTo(fGMTZeroFormat);
    632 }
    633 
    634 void
    635 TimeZoneFormat::setGMTZeroFormat(const UnicodeString& gmtZeroFormat, UErrorCode& status) {
    636     if (U_SUCCESS(status)) {
    637         if (gmtZeroFormat.isEmpty()) {
    638             status = U_ILLEGAL_ARGUMENT_ERROR;
    639         } else if (gmtZeroFormat != fGMTZeroFormat) {
    640             fGMTZeroFormat.setTo(gmtZeroFormat);
    641         }
    642     }
    643 }
    644 
    645 // ------------------------------------------------------------------
    646 // Format and Parse
    647 
    648 UnicodeString&
    649 TimeZoneFormat::format(UTimeZoneFormatStyle style, const TimeZone& tz, UDate date,
    650         UnicodeString& name, UTimeZoneFormatTimeType* timeType /* = NULL */) const {
    651     if (timeType) {
    652         *timeType = UTZFMT_TIME_TYPE_UNKNOWN;
    653     }
    654 
    655     UBool noOffsetFormatFallback = FALSE;
    656 
    657     switch (style) {
    658     case UTZFMT_STYLE_GENERIC_LOCATION:
    659         formatGeneric(tz, UTZGNM_LOCATION, date, name);
    660         break;
    661     case UTZFMT_STYLE_GENERIC_LONG:
    662         formatGeneric(tz, UTZGNM_LONG, date, name);
    663         break;
    664     case UTZFMT_STYLE_GENERIC_SHORT:
    665         formatGeneric(tz, UTZGNM_SHORT, date, name);
    666         break;
    667     case UTZFMT_STYLE_SPECIFIC_LONG:
    668         formatSpecific(tz, UTZNM_LONG_STANDARD, UTZNM_LONG_DAYLIGHT, date, name, timeType);
    669         break;
    670     case UTZFMT_STYLE_SPECIFIC_SHORT:
    671         formatSpecific(tz, UTZNM_SHORT_STANDARD, UTZNM_SHORT_DAYLIGHT, date, name, timeType);
    672         break;
    673 
    674     case UTZFMT_STYLE_ZONE_ID:
    675         tz.getID(name);
    676         noOffsetFormatFallback = TRUE;
    677         break;
    678     case UTZFMT_STYLE_ZONE_ID_SHORT:
    679         {
    680             const UChar* shortID = ZoneMeta::getShortID(tz);
    681             if (shortID == NULL) {
    682                 shortID = UNKNOWN_SHORT_ZONE_ID;
    683             }
    684             name.setTo(shortID, -1);
    685         }
    686         noOffsetFormatFallback = TRUE;
    687         break;
    688 
    689     case UTZFMT_STYLE_EXEMPLAR_LOCATION:
    690         formatExemplarLocation(tz, name);
    691         noOffsetFormatFallback = TRUE;
    692         break;
    693 
    694     default:
    695         // will be handled below
    696         break;
    697     }
    698 
    699     if (name.isEmpty() && !noOffsetFormatFallback) {
    700         UErrorCode status = U_ZERO_ERROR;
    701         int32_t rawOffset, dstOffset;
    702         tz.getOffset(date, FALSE, rawOffset, dstOffset, status);
    703         int32_t offset = rawOffset + dstOffset;
    704         if (U_SUCCESS(status)) {
    705             switch (style) {
    706             case UTZFMT_STYLE_GENERIC_LOCATION:
    707             case UTZFMT_STYLE_GENERIC_LONG:
    708             case UTZFMT_STYLE_SPECIFIC_LONG:
    709             case UTZFMT_STYLE_LOCALIZED_GMT:
    710                 formatOffsetLocalizedGMT(offset, name, status);
    711                 break;
    712 
    713             case UTZFMT_STYLE_GENERIC_SHORT:
    714             case UTZFMT_STYLE_SPECIFIC_SHORT:
    715             case UTZFMT_STYLE_LOCALIZED_GMT_SHORT:
    716                 formatOffsetShortLocalizedGMT(offset, name, status);
    717                 break;
    718 
    719             case UTZFMT_STYLE_ISO_BASIC_SHORT:
    720                 formatOffsetISO8601Basic(offset, TRUE, TRUE, TRUE, name, status);
    721                 break;
    722 
    723             case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT:
    724                 formatOffsetISO8601Basic(offset, FALSE, TRUE, TRUE, name, status);
    725                 break;
    726 
    727             case UTZFMT_STYLE_ISO_BASIC_FIXED:
    728                 formatOffsetISO8601Basic(offset, TRUE, FALSE, TRUE, name, status);
    729                 break;
    730 
    731             case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED:
    732                 formatOffsetISO8601Basic(offset, FALSE, FALSE, TRUE, name, status);
    733                 break;
    734 
    735             case UTZFMT_STYLE_ISO_EXTENDED_FIXED:
    736                 formatOffsetISO8601Extended(offset, TRUE, FALSE, TRUE, name, status);
    737                 break;
    738 
    739             case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED:
    740                 formatOffsetISO8601Extended(offset, FALSE, FALSE, TRUE, name, status);
    741                 break;
    742 
    743             case UTZFMT_STYLE_ISO_BASIC_FULL:
    744                 formatOffsetISO8601Basic(offset, TRUE, FALSE, FALSE, name, status);
    745                 break;
    746 
    747             case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL:
    748                 formatOffsetISO8601Basic(offset, FALSE, FALSE, FALSE, name, status);
    749                 break;
    750 
    751             case UTZFMT_STYLE_ISO_EXTENDED_FULL:
    752                 formatOffsetISO8601Extended(offset, TRUE, FALSE, FALSE, name, status);
    753                 break;
    754 
    755             case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL:
    756                 formatOffsetISO8601Extended(offset, FALSE, FALSE, FALSE, name, status);
    757                 break;
    758 
    759             default:
    760               // UTZFMT_STYLE_ZONE_ID, UTZFMT_STYLE_ZONE_ID_SHORT, UTZFMT_STYLE_EXEMPLAR_LOCATION
    761               break;
    762             }
    763 
    764             if (timeType) {
    765                 *timeType = (dstOffset != 0) ? UTZFMT_TIME_TYPE_DAYLIGHT : UTZFMT_TIME_TYPE_STANDARD;
    766             }
    767         }
    768     }
    769 
    770     return name;
    771 }
    772 
    773 UnicodeString&
    774 TimeZoneFormat::format(const Formattable& obj, UnicodeString& appendTo,
    775         FieldPosition& pos, UErrorCode& status) const {
    776     if (U_FAILURE(status)) {
    777         return appendTo;
    778     }
    779     UDate date = Calendar::getNow();
    780     if (obj.getType() == Formattable::kObject) {
    781         const UObject* formatObj = obj.getObject();
    782         const TimeZone* tz = dynamic_cast<const TimeZone*>(formatObj);
    783         if (tz == NULL) {
    784             const Calendar* cal = dynamic_cast<const Calendar*>(formatObj);
    785             if (cal != NULL) {
    786                 tz = &cal->getTimeZone();
    787                 date = cal->getTime(status);
    788             }
    789         }
    790         if (tz != NULL) {
    791             int32_t rawOffset, dstOffset;
    792             tz->getOffset(date, FALSE, rawOffset, dstOffset, status);
    793             UChar buf[ZONE_NAME_U16_MAX];
    794             UnicodeString result(buf, 0, UPRV_LENGTHOF(buf));
    795             formatOffsetLocalizedGMT(rawOffset + dstOffset, result, status);
    796             if (U_SUCCESS(status)) {
    797                 appendTo.append(result);
    798                 if (pos.getField() == UDAT_TIMEZONE_FIELD) {
    799                     pos.setBeginIndex(0);
    800                     pos.setEndIndex(result.length());
    801                 }
    802             }
    803         }
    804     }
    805     return appendTo;
    806 }
    807 
    808 TimeZone*
    809 TimeZoneFormat::parse(UTimeZoneFormatStyle style, const UnicodeString& text, ParsePosition& pos,
    810         UTimeZoneFormatTimeType* timeType /*= NULL*/) const {
    811     return parse(style, text, pos, getDefaultParseOptions(), timeType);
    812 }
    813 
    814 TimeZone*
    815 TimeZoneFormat::parse(UTimeZoneFormatStyle style, const UnicodeString& text, ParsePosition& pos,
    816         int32_t parseOptions, UTimeZoneFormatTimeType* timeType /* = NULL */) const {
    817     if (timeType) {
    818         *timeType = UTZFMT_TIME_TYPE_UNKNOWN;
    819     }
    820 
    821     int32_t startIdx = pos.getIndex();
    822     int32_t maxPos = text.length();
    823     int32_t offset;
    824 
    825     // Styles using localized GMT format as fallback
    826     UBool fallbackLocalizedGMT =
    827         (style == UTZFMT_STYLE_SPECIFIC_LONG || style == UTZFMT_STYLE_GENERIC_LONG || style == UTZFMT_STYLE_GENERIC_LOCATION);
    828     UBool fallbackShortLocalizedGMT =
    829         (style == UTZFMT_STYLE_SPECIFIC_SHORT || style == UTZFMT_STYLE_GENERIC_SHORT);
    830 
    831     int32_t evaluated = 0;  // bit flags representing already evaluated styles
    832     ParsePosition tmpPos(startIdx);
    833 
    834     int32_t parsedOffset = UNKNOWN_OFFSET;  // stores successfully parsed offset for later use
    835     int32_t parsedPos = -1;                 // stores successfully parsed offset position for later use
    836 
    837     // Try localized GMT format first if necessary
    838     if (fallbackLocalizedGMT || fallbackShortLocalizedGMT) {
    839         UBool hasDigitOffset = FALSE;
    840         offset = parseOffsetLocalizedGMT(text, tmpPos, fallbackShortLocalizedGMT, &hasDigitOffset);
    841         if (tmpPos.getErrorIndex() == -1) {
    842             // Even when the input text was successfully parsed as a localized GMT format text,
    843             // we may still need to evaluate the specified style if -
    844             //   1) GMT zero format was used, and
    845             //   2) The input text was not completely processed
    846             if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
    847                 pos.setIndex(tmpPos.getIndex());
    848                 return createTimeZoneForOffset(offset);
    849             }
    850             parsedOffset = offset;
    851             parsedPos = tmpPos.getIndex();
    852         }
    853         // Note: For now, no distinction between long/short localized GMT format in the parser.
    854         // This might be changed in future.
    855         // evaluated |= (fallbackLocalizedGMT ? STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT] : STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT]);
    856         evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT] | STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT];
    857     }
    858 
    859     UErrorCode status = U_ZERO_ERROR;
    860     UChar tzIDBuf[32];
    861     UnicodeString tzID(tzIDBuf, 0, UPRV_LENGTHOF(tzIDBuf));
    862 
    863     UBool parseTZDBAbbrev = ((parseOptions & UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS) != 0);
    864 
    865     // Try the specified style
    866     switch (style) {
    867     case UTZFMT_STYLE_LOCALIZED_GMT:
    868         {
    869             tmpPos.setIndex(startIdx);
    870             tmpPos.setErrorIndex(-1);
    871 
    872             offset = parseOffsetLocalizedGMT(text, tmpPos);
    873             if (tmpPos.getErrorIndex() == -1) {
    874                 pos.setIndex(tmpPos.getIndex());
    875                 return createTimeZoneForOffset(offset);
    876             }
    877 
    878             // Note: For now, no distinction between long/short localized GMT format in the parser.
    879             // This might be changed in future.
    880             evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT];
    881 
    882             break;
    883         }
    884     case UTZFMT_STYLE_LOCALIZED_GMT_SHORT:
    885         {
    886             tmpPos.setIndex(startIdx);
    887             tmpPos.setErrorIndex(-1);
    888 
    889             offset = parseOffsetShortLocalizedGMT(text, tmpPos);
    890             if (tmpPos.getErrorIndex() == -1) {
    891                 pos.setIndex(tmpPos.getIndex());
    892                 return createTimeZoneForOffset(offset);
    893             }
    894 
    895             // Note: For now, no distinction between long/short localized GMT format in the parser.
    896             // This might be changed in future.
    897             evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT];
    898 
    899             break;
    900         }
    901     case UTZFMT_STYLE_ISO_BASIC_SHORT:
    902     case UTZFMT_STYLE_ISO_BASIC_FIXED:
    903     case UTZFMT_STYLE_ISO_BASIC_FULL:
    904     case UTZFMT_STYLE_ISO_EXTENDED_FIXED:
    905     case UTZFMT_STYLE_ISO_EXTENDED_FULL:
    906         {
    907             tmpPos.setIndex(startIdx);
    908             tmpPos.setErrorIndex(-1);
    909 
    910             offset = parseOffsetISO8601(text, tmpPos);
    911             if (tmpPos.getErrorIndex() == -1) {
    912                 pos.setIndex(tmpPos.getIndex());
    913                 return createTimeZoneForOffset(offset);
    914             }
    915 
    916             break;
    917         }
    918 
    919     case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT:
    920     case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED:
    921     case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL:
    922     case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED:
    923     case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL:
    924         {
    925             tmpPos.setIndex(startIdx);
    926             tmpPos.setErrorIndex(-1);
    927 
    928             // Exclude the case of UTC Indicator "Z" here
    929             UBool hasDigitOffset = FALSE;
    930             offset = parseOffsetISO8601(text, tmpPos, FALSE, &hasDigitOffset);
    931             if (tmpPos.getErrorIndex() == -1 && hasDigitOffset) {
    932                 pos.setIndex(tmpPos.getIndex());
    933                 return createTimeZoneForOffset(offset);
    934             }
    935 
    936             break;
    937         }
    938 
    939     case UTZFMT_STYLE_SPECIFIC_LONG:
    940     case UTZFMT_STYLE_SPECIFIC_SHORT:
    941         {
    942             // Specific styles
    943             int32_t nameTypes = 0;
    944             if (style == UTZFMT_STYLE_SPECIFIC_LONG) {
    945                 nameTypes = (UTZNM_LONG_STANDARD | UTZNM_LONG_DAYLIGHT);
    946             } else {
    947                 U_ASSERT(style == UTZFMT_STYLE_SPECIFIC_SHORT);
    948                 nameTypes = (UTZNM_SHORT_STANDARD | UTZNM_SHORT_DAYLIGHT);
    949             }
    950             LocalPointer<TimeZoneNames::MatchInfoCollection> specificMatches(fTimeZoneNames->find(text, startIdx, nameTypes, status));
    951             if (U_FAILURE(status)) {
    952                 pos.setErrorIndex(startIdx);
    953                 return NULL;
    954             }
    955             if (!specificMatches.isNull()) {
    956                 int32_t matchIdx = -1;
    957                 int32_t matchPos = -1;
    958                 for (int32_t i = 0; i < specificMatches->size(); i++) {
    959                     matchPos  = startIdx + specificMatches->getMatchLengthAt(i);
    960                     if (matchPos > parsedPos) {
    961                         matchIdx = i;
    962                         parsedPos = matchPos;
    963                     }
    964                 }
    965                 if (matchIdx >= 0) {
    966                     if (timeType) {
    967                         *timeType = getTimeType(specificMatches->getNameTypeAt(matchIdx));
    968                     }
    969                     pos.setIndex(matchPos);
    970                     getTimeZoneID(specificMatches.getAlias(), matchIdx, tzID);
    971                     U_ASSERT(!tzID.isEmpty());
    972                     return TimeZone::createTimeZone(tzID);
    973                 }
    974             }
    975 
    976             if (parseTZDBAbbrev && style == UTZFMT_STYLE_SPECIFIC_SHORT) {
    977                 U_ASSERT((nameTypes & UTZNM_SHORT_STANDARD) != 0);
    978                 U_ASSERT((nameTypes & UTZNM_SHORT_DAYLIGHT) != 0);
    979 
    980                 const TZDBTimeZoneNames *tzdbTimeZoneNames = getTZDBTimeZoneNames(status);
    981                 if (U_SUCCESS(status)) {
    982                     LocalPointer<TimeZoneNames::MatchInfoCollection> tzdbNameMatches(
    983                         tzdbTimeZoneNames->find(text, startIdx, nameTypes, status));
    984                     if (U_FAILURE(status)) {
    985                         pos.setErrorIndex(startIdx);
    986                         return NULL;
    987                     }
    988                     if (!tzdbNameMatches.isNull()) {
    989                         int32_t matchIdx = -1;
    990                         int32_t matchPos = -1;
    991                         for (int32_t i = 0; i < tzdbNameMatches->size(); i++) {
    992                             matchPos = startIdx + tzdbNameMatches->getMatchLengthAt(i);
    993                             if (matchPos > parsedPos) {
    994                                 matchIdx = i;
    995                                 parsedPos = matchPos;
    996                             }
    997                         }
    998                         if (matchIdx >= 0) {
    999                             if (timeType) {
   1000                                 *timeType = getTimeType(tzdbNameMatches->getNameTypeAt(matchIdx));
   1001                             }
   1002                             pos.setIndex(matchPos);
   1003                             getTimeZoneID(tzdbNameMatches.getAlias(), matchIdx, tzID);
   1004                             U_ASSERT(!tzID.isEmpty());
   1005                             return TimeZone::createTimeZone(tzID);
   1006                         }
   1007                     }
   1008                 }
   1009             }
   1010             break;
   1011         }
   1012     case UTZFMT_STYLE_GENERIC_LONG:
   1013     case UTZFMT_STYLE_GENERIC_SHORT:
   1014     case UTZFMT_STYLE_GENERIC_LOCATION:
   1015         {
   1016             int32_t genericNameTypes = 0;
   1017             switch (style) {
   1018             case UTZFMT_STYLE_GENERIC_LOCATION:
   1019                 genericNameTypes = UTZGNM_LOCATION;
   1020                 break;
   1021 
   1022             case UTZFMT_STYLE_GENERIC_LONG:
   1023                 genericNameTypes = UTZGNM_LONG | UTZGNM_LOCATION;
   1024                 break;
   1025 
   1026             case UTZFMT_STYLE_GENERIC_SHORT:
   1027                 genericNameTypes = UTZGNM_SHORT | UTZGNM_LOCATION;
   1028                 break;
   1029 
   1030             default:
   1031                 U_ASSERT(FALSE);
   1032             }
   1033 
   1034             int32_t len = 0;
   1035             UTimeZoneFormatTimeType tt = UTZFMT_TIME_TYPE_UNKNOWN;
   1036             const TimeZoneGenericNames *gnames = getTimeZoneGenericNames(status);
   1037             if (U_SUCCESS(status)) {
   1038                 len = gnames->findBestMatch(text, startIdx, genericNameTypes, tzID, tt, status);
   1039             }
   1040             if (U_FAILURE(status)) {
   1041                 pos.setErrorIndex(startIdx);
   1042                 return NULL;
   1043             }
   1044             if (len > 0) {
   1045                 // Found a match
   1046                 if (timeType) {
   1047                     *timeType = tt;
   1048                 }
   1049                 pos.setIndex(startIdx + len);
   1050                 U_ASSERT(!tzID.isEmpty());
   1051                 return TimeZone::createTimeZone(tzID);
   1052             }
   1053 
   1054             break;
   1055         }
   1056     case UTZFMT_STYLE_ZONE_ID:
   1057         {
   1058             tmpPos.setIndex(startIdx);
   1059             tmpPos.setErrorIndex(-1);
   1060 
   1061             parseZoneID(text, tmpPos, tzID);
   1062             if (tmpPos.getErrorIndex() == -1) {
   1063                 pos.setIndex(tmpPos.getIndex());
   1064                 return TimeZone::createTimeZone(tzID);
   1065             }
   1066             break;
   1067         }
   1068     case UTZFMT_STYLE_ZONE_ID_SHORT:
   1069         {
   1070             tmpPos.setIndex(startIdx);
   1071             tmpPos.setErrorIndex(-1);
   1072 
   1073             parseShortZoneID(text, tmpPos, tzID);
   1074             if (tmpPos.getErrorIndex() == -1) {
   1075                 pos.setIndex(tmpPos.getIndex());
   1076                 return TimeZone::createTimeZone(tzID);
   1077             }
   1078             break;
   1079         }
   1080     case UTZFMT_STYLE_EXEMPLAR_LOCATION:
   1081         {
   1082             tmpPos.setIndex(startIdx);
   1083             tmpPos.setErrorIndex(-1);
   1084 
   1085             parseExemplarLocation(text, tmpPos, tzID);
   1086             if (tmpPos.getErrorIndex() == -1) {
   1087                 pos.setIndex(tmpPos.getIndex());
   1088                 return TimeZone::createTimeZone(tzID);
   1089             }
   1090             break;
   1091         }
   1092     }
   1093     evaluated |= STYLE_PARSE_FLAGS[style];
   1094 
   1095 
   1096     if (parsedPos > startIdx) {
   1097         // When the specified style is one of SPECIFIC_XXX or GENERIC_XXX, we tried to parse the input
   1098         // as localized GMT format earlier. If parsedOffset is positive, it means it was successfully
   1099         // parsed as localized GMT format, but offset digits were not detected (more specifically, GMT
   1100         // zero format). Then, it tried to find a match within the set of display names, but could not
   1101         // find a match. At this point, we can safely assume the input text contains the localized
   1102         // GMT format.
   1103         U_ASSERT(parsedOffset != UNKNOWN_OFFSET);
   1104         pos.setIndex(parsedPos);
   1105         return createTimeZoneForOffset(parsedOffset);
   1106     }
   1107 
   1108     // Failed to parse the input text as the time zone format in the specified style.
   1109     // Check the longest match among other styles below.
   1110     UChar parsedIDBuf[32];
   1111     UnicodeString parsedID(parsedIDBuf, 0, UPRV_LENGTHOF(parsedIDBuf));
   1112     UTimeZoneFormatTimeType parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
   1113 
   1114     U_ASSERT(parsedPos < 0);
   1115     U_ASSERT(parsedOffset == UNKNOWN_OFFSET);
   1116 
   1117     // ISO 8601
   1118     if (parsedPos < maxPos &&
   1119         ((evaluated & ISO_Z_STYLE_FLAG) == 0 || (evaluated & ISO_LOCAL_STYLE_FLAG) == 0)) {
   1120         tmpPos.setIndex(startIdx);
   1121         tmpPos.setErrorIndex(-1);
   1122 
   1123         UBool hasDigitOffset = FALSE;
   1124         offset = parseOffsetISO8601(text, tmpPos, FALSE, &hasDigitOffset);
   1125         if (tmpPos.getErrorIndex() == -1) {
   1126             if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
   1127                 pos.setIndex(tmpPos.getIndex());
   1128                 return createTimeZoneForOffset(offset);
   1129             }
   1130             // Note: When ISO 8601 format contains offset digits, it should not
   1131             // collide with other formats. However, ISO 8601 UTC format "Z" (single letter)
   1132             // may collide with other names. In this case, we need to evaluate other names.
   1133             if (parsedPos < tmpPos.getIndex()) {
   1134                 parsedOffset = offset;
   1135                 parsedID.setToBogus();
   1136                 parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
   1137                 parsedPos = tmpPos.getIndex();
   1138                 U_ASSERT(parsedPos == startIdx + 1);    // only when "Z" is used
   1139             }
   1140         }
   1141     }
   1142 
   1143     // Localized GMT format
   1144     if (parsedPos < maxPos &&
   1145         (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT]) == 0) {
   1146         tmpPos.setIndex(startIdx);
   1147         tmpPos.setErrorIndex(-1);
   1148 
   1149         UBool hasDigitOffset = FALSE;
   1150         offset = parseOffsetLocalizedGMT(text, tmpPos, FALSE, &hasDigitOffset);
   1151         if (tmpPos.getErrorIndex() == -1) {
   1152             if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
   1153                 pos.setIndex(tmpPos.getIndex());
   1154                 return createTimeZoneForOffset(offset);
   1155             }
   1156             // Evaluate other names - see the comment earlier in this method.
   1157             if (parsedPos < tmpPos.getIndex()) {
   1158                 parsedOffset = offset;
   1159                 parsedID.setToBogus();
   1160                 parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
   1161                 parsedPos = tmpPos.getIndex();
   1162             }
   1163         }
   1164     }
   1165 
   1166     if (parsedPos < maxPos &&
   1167         (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT]) == 0) {
   1168         tmpPos.setIndex(startIdx);
   1169         tmpPos.setErrorIndex(-1);
   1170 
   1171         UBool hasDigitOffset = FALSE;
   1172         offset = parseOffsetLocalizedGMT(text, tmpPos, TRUE, &hasDigitOffset);
   1173         if (tmpPos.getErrorIndex() == -1) {
   1174             if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
   1175                 pos.setIndex(tmpPos.getIndex());
   1176                 return createTimeZoneForOffset(offset);
   1177             }
   1178             // Evaluate other names - see the comment earlier in this method.
   1179             if (parsedPos < tmpPos.getIndex()) {
   1180                 parsedOffset = offset;
   1181                 parsedID.setToBogus();
   1182                 parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
   1183                 parsedPos = tmpPos.getIndex();
   1184             }
   1185         }
   1186     }
   1187 
   1188     // When ParseOption.ALL_STYLES is available, we also try to look all possible display names and IDs.
   1189     // For example, when style is GENERIC_LONG, "EST" (SPECIFIC_SHORT) is never
   1190     // used for America/New_York. With parseAllStyles true, this code parses "EST"
   1191     // as America/New_York.
   1192 
   1193     // Note: Adding all possible names into the trie used by the implementation is quite heavy operation,
   1194     // which we want to avoid normally (note that we cache the trie, so this is applicable to the
   1195     // first time only as long as the cache does not expire).
   1196 
   1197     if (parseOptions & UTZFMT_PARSE_OPTION_ALL_STYLES) {
   1198         // Try all specific names and exemplar location names
   1199         if (parsedPos < maxPos) {
   1200             LocalPointer<TimeZoneNames::MatchInfoCollection> specificMatches(fTimeZoneNames->find(text, startIdx, ALL_SIMPLE_NAME_TYPES, status));
   1201             if (U_FAILURE(status)) {
   1202                 pos.setErrorIndex(startIdx);
   1203                 return NULL;
   1204             }
   1205             int32_t specificMatchIdx = -1;
   1206             int32_t matchPos = -1;
   1207             if (!specificMatches.isNull()) {
   1208                 for (int32_t i = 0; i < specificMatches->size(); i++) {
   1209                     if (startIdx + specificMatches->getMatchLengthAt(i) > matchPos) {
   1210                         specificMatchIdx = i;
   1211                         matchPos = startIdx + specificMatches->getMatchLengthAt(i);
   1212                     }
   1213                 }
   1214             }
   1215             if (parsedPos < matchPos) {
   1216                 U_ASSERT(specificMatchIdx >= 0);
   1217                 parsedPos = matchPos;
   1218                 getTimeZoneID(specificMatches.getAlias(), specificMatchIdx, parsedID);
   1219                 parsedTimeType = getTimeType(specificMatches->getNameTypeAt(specificMatchIdx));
   1220                 parsedOffset = UNKNOWN_OFFSET;
   1221             }
   1222         }
   1223         if (parseTZDBAbbrev && parsedPos < maxPos && (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_SPECIFIC_SHORT]) == 0) {
   1224             const TZDBTimeZoneNames *tzdbTimeZoneNames = getTZDBTimeZoneNames(status);
   1225             if (U_SUCCESS(status)) {
   1226                 LocalPointer<TimeZoneNames::MatchInfoCollection> tzdbNameMatches(
   1227                     tzdbTimeZoneNames->find(text, startIdx, ALL_SIMPLE_NAME_TYPES, status));
   1228                 if (U_FAILURE(status)) {
   1229                     pos.setErrorIndex(startIdx);
   1230                     return NULL;
   1231                 }
   1232                 int32_t tzdbNameMatchIdx = -1;
   1233                 int32_t matchPos = -1;
   1234                 if (!tzdbNameMatches.isNull()) {
   1235                     for (int32_t i = 0; i < tzdbNameMatches->size(); i++) {
   1236                         if (startIdx + tzdbNameMatches->getMatchLengthAt(i) > matchPos) {
   1237                             tzdbNameMatchIdx = i;
   1238                             matchPos = startIdx + tzdbNameMatches->getMatchLengthAt(i);
   1239                         }
   1240                     }
   1241                 }
   1242                 if (parsedPos < matchPos) {
   1243                     U_ASSERT(tzdbNameMatchIdx >= 0);
   1244                     parsedPos = matchPos;
   1245                     getTimeZoneID(tzdbNameMatches.getAlias(), tzdbNameMatchIdx, parsedID);
   1246                     parsedTimeType = getTimeType(tzdbNameMatches->getNameTypeAt(tzdbNameMatchIdx));
   1247                     parsedOffset = UNKNOWN_OFFSET;
   1248                 }
   1249             }
   1250         }
   1251         // Try generic names
   1252         if (parsedPos < maxPos) {
   1253             int32_t genMatchLen = -1;
   1254             UTimeZoneFormatTimeType tt = UTZFMT_TIME_TYPE_UNKNOWN;
   1255 
   1256             const TimeZoneGenericNames *gnames = getTimeZoneGenericNames(status);
   1257             if (U_SUCCESS(status)) {
   1258                 genMatchLen = gnames->findBestMatch(text, startIdx, ALL_GENERIC_NAME_TYPES, tzID, tt, status);
   1259             }
   1260             if (U_FAILURE(status)) {
   1261                 pos.setErrorIndex(startIdx);
   1262                 return NULL;
   1263             }
   1264 
   1265             if (genMatchLen > 0 && parsedPos < startIdx + genMatchLen) {
   1266                 parsedPos = startIdx + genMatchLen;
   1267                 parsedID.setTo(tzID);
   1268                 parsedTimeType = tt;
   1269                 parsedOffset = UNKNOWN_OFFSET;
   1270             }
   1271         }
   1272 
   1273         // Try time zone ID
   1274         if (parsedPos < maxPos && (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_ZONE_ID]) == 0) {
   1275             tmpPos.setIndex(startIdx);
   1276             tmpPos.setErrorIndex(-1);
   1277 
   1278             parseZoneID(text, tmpPos, tzID);
   1279             if (tmpPos.getErrorIndex() == -1 && parsedPos < tmpPos.getIndex()) {
   1280                 parsedPos = tmpPos.getIndex();
   1281                 parsedID.setTo(tzID);
   1282                 parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
   1283                 parsedOffset = UNKNOWN_OFFSET;
   1284             }
   1285         }
   1286         // Try short time zone ID
   1287         if (parsedPos < maxPos && (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_ZONE_ID]) == 0) {
   1288             tmpPos.setIndex(startIdx);
   1289             tmpPos.setErrorIndex(-1);
   1290 
   1291             parseShortZoneID(text, tmpPos, tzID);
   1292             if (tmpPos.getErrorIndex() == -1 && parsedPos < tmpPos.getIndex()) {
   1293                 parsedPos = tmpPos.getIndex();
   1294                 parsedID.setTo(tzID);
   1295                 parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
   1296                 parsedOffset = UNKNOWN_OFFSET;
   1297             }
   1298         }
   1299     }
   1300 
   1301     if (parsedPos > startIdx) {
   1302         // Parsed successfully
   1303         TimeZone* parsedTZ;
   1304         if (parsedID.length() > 0) {
   1305             parsedTZ = TimeZone::createTimeZone(parsedID);
   1306         } else {
   1307             U_ASSERT(parsedOffset != UNKNOWN_OFFSET);
   1308             parsedTZ = createTimeZoneForOffset(parsedOffset);
   1309         }
   1310         if (timeType) {
   1311             *timeType = parsedTimeType;
   1312         }
   1313         pos.setIndex(parsedPos);
   1314         return parsedTZ;
   1315     }
   1316 
   1317     pos.setErrorIndex(startIdx);
   1318     return NULL;
   1319 }
   1320 
   1321 void
   1322 TimeZoneFormat::parseObject(const UnicodeString& source, Formattable& result,
   1323         ParsePosition& parse_pos) const {
   1324     result.adoptObject(parse(UTZFMT_STYLE_GENERIC_LOCATION, source, parse_pos, UTZFMT_PARSE_OPTION_ALL_STYLES));
   1325 }
   1326 
   1327 
   1328 // ------------------------------------------------------------------
   1329 // Private zone name format/parse implementation
   1330 
   1331 UnicodeString&
   1332 TimeZoneFormat::formatGeneric(const TimeZone& tz, int32_t genType, UDate date, UnicodeString& name) const {
   1333     UErrorCode status = U_ZERO_ERROR;
   1334     const TimeZoneGenericNames* gnames = getTimeZoneGenericNames(status);
   1335     if (U_FAILURE(status)) {
   1336         name.setToBogus();
   1337         return name;
   1338     }
   1339 
   1340     if (genType == UTZGNM_LOCATION) {
   1341         const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(tz);
   1342         if (canonicalID == NULL) {
   1343             name.setToBogus();
   1344             return name;
   1345         }
   1346         return gnames->getGenericLocationName(UnicodeString(TRUE, canonicalID, -1), name);
   1347     }
   1348     return gnames->getDisplayName(tz, (UTimeZoneGenericNameType)genType, date, name);
   1349 }
   1350 
   1351 UnicodeString&
   1352 TimeZoneFormat::formatSpecific(const TimeZone& tz, UTimeZoneNameType stdType, UTimeZoneNameType dstType,
   1353         UDate date, UnicodeString& name, UTimeZoneFormatTimeType *timeType) const {
   1354     if (fTimeZoneNames == NULL) {
   1355         name.setToBogus();
   1356         return name;
   1357     }
   1358 
   1359     UErrorCode status = U_ZERO_ERROR;
   1360     UBool isDaylight = tz.inDaylightTime(date, status);
   1361     const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(tz);
   1362 
   1363     if (U_FAILURE(status) || canonicalID == NULL) {
   1364         name.setToBogus();
   1365         return name;
   1366     }
   1367 
   1368     if (isDaylight) {
   1369         fTimeZoneNames->getDisplayName(UnicodeString(TRUE, canonicalID, -1), dstType, date, name);
   1370     } else {
   1371         fTimeZoneNames->getDisplayName(UnicodeString(TRUE, canonicalID, -1), stdType, date, name);
   1372     }
   1373 
   1374     if (timeType && !name.isEmpty()) {
   1375         *timeType = isDaylight ? UTZFMT_TIME_TYPE_DAYLIGHT : UTZFMT_TIME_TYPE_STANDARD;
   1376     }
   1377     return name;
   1378 }
   1379 
   1380 const TimeZoneGenericNames*
   1381 TimeZoneFormat::getTimeZoneGenericNames(UErrorCode& status) const {
   1382     if (U_FAILURE(status)) {
   1383         return NULL;
   1384     }
   1385 
   1386     umtx_lock(&gLock);
   1387     if (fTimeZoneGenericNames == NULL) {
   1388         TimeZoneFormat *nonConstThis = const_cast<TimeZoneFormat *>(this);
   1389         nonConstThis->fTimeZoneGenericNames = TimeZoneGenericNames::createInstance(fLocale, status);
   1390     }
   1391     umtx_unlock(&gLock);
   1392 
   1393     return fTimeZoneGenericNames;
   1394 }
   1395 
   1396 const TZDBTimeZoneNames*
   1397 TimeZoneFormat::getTZDBTimeZoneNames(UErrorCode& status) const {
   1398     if (U_FAILURE(status)) {
   1399         return NULL;
   1400     }
   1401 
   1402     umtx_lock(&gLock);
   1403     if (fTZDBTimeZoneNames == NULL) {
   1404         TZDBTimeZoneNames *tzdbNames = new TZDBTimeZoneNames(fLocale);
   1405         if (tzdbNames == NULL) {
   1406             status = U_MEMORY_ALLOCATION_ERROR;
   1407         } else {
   1408             TimeZoneFormat *nonConstThis = const_cast<TimeZoneFormat *>(this);
   1409             nonConstThis->fTZDBTimeZoneNames = tzdbNames;
   1410         }
   1411     }
   1412     umtx_unlock(&gLock);
   1413 
   1414     return fTZDBTimeZoneNames;
   1415 }
   1416 
   1417 UnicodeString&
   1418 TimeZoneFormat::formatExemplarLocation(const TimeZone& tz, UnicodeString& name) const {
   1419     UChar locationBuf[ZONE_NAME_U16_MAX];
   1420     UnicodeString location(locationBuf, 0, UPRV_LENGTHOF(locationBuf));
   1421     const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(tz);
   1422 
   1423     if (canonicalID) {
   1424         fTimeZoneNames->getExemplarLocationName(UnicodeString(TRUE, canonicalID, -1), location);
   1425     }
   1426     if (location.length() > 0) {
   1427         name.setTo(location);
   1428     } else {
   1429         // Use "unknown" location
   1430         fTimeZoneNames->getExemplarLocationName(UnicodeString(TRUE, UNKNOWN_ZONE_ID, -1), location);
   1431         if (location.length() > 0) {
   1432             name.setTo(location);
   1433         } else {
   1434             // last resort
   1435             name.setTo(UNKNOWN_LOCATION, -1);
   1436         }
   1437     }
   1438     return name;
   1439 }
   1440 
   1441 
   1442 // ------------------------------------------------------------------
   1443 // Zone offset format and parse
   1444 
   1445 UnicodeString&
   1446 TimeZoneFormat::formatOffsetISO8601Basic(int32_t offset, UBool useUtcIndicator, UBool isShort, UBool ignoreSeconds,
   1447         UnicodeString& result, UErrorCode& status) const {
   1448     return formatOffsetISO8601(offset, TRUE, useUtcIndicator, isShort, ignoreSeconds, result, status);
   1449 }
   1450 
   1451 UnicodeString&
   1452 TimeZoneFormat::formatOffsetISO8601Extended(int32_t offset, UBool useUtcIndicator, UBool isShort, UBool ignoreSeconds,
   1453         UnicodeString& result, UErrorCode& status) const {
   1454     return formatOffsetISO8601(offset, FALSE, useUtcIndicator, isShort, ignoreSeconds, result, status);
   1455 }
   1456 
   1457 UnicodeString&
   1458 TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset, UnicodeString& result, UErrorCode& status) const {
   1459     return formatOffsetLocalizedGMT(offset, FALSE, result, status);
   1460 }
   1461 
   1462 UnicodeString&
   1463 TimeZoneFormat::formatOffsetShortLocalizedGMT(int32_t offset, UnicodeString& result, UErrorCode& status) const {
   1464     return formatOffsetLocalizedGMT(offset, TRUE, result, status);
   1465 }
   1466 
   1467 int32_t
   1468 TimeZoneFormat::parseOffsetISO8601(const UnicodeString& text, ParsePosition& pos) const {
   1469     return parseOffsetISO8601(text, pos, FALSE);
   1470 }
   1471 
   1472 int32_t
   1473 TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString& text, ParsePosition& pos) const {
   1474     return parseOffsetLocalizedGMT(text, pos, FALSE, NULL);
   1475 }
   1476 
   1477 int32_t
   1478 TimeZoneFormat::parseOffsetShortLocalizedGMT(const UnicodeString& text, ParsePosition& pos) const {
   1479     return parseOffsetLocalizedGMT(text, pos, TRUE, NULL);
   1480 }
   1481 
   1482 // ------------------------------------------------------------------
   1483 // Private zone offset format/parse implementation
   1484 
   1485 UnicodeString&
   1486 TimeZoneFormat::formatOffsetISO8601(int32_t offset, UBool isBasic, UBool useUtcIndicator,
   1487         UBool isShort, UBool ignoreSeconds, UnicodeString& result, UErrorCode& status) const {
   1488     if (U_FAILURE(status)) {
   1489         result.setToBogus();
   1490         return result;
   1491     }
   1492     int32_t absOffset = offset < 0 ? -offset : offset;
   1493     if (useUtcIndicator && (absOffset < MILLIS_PER_SECOND || (ignoreSeconds && absOffset < MILLIS_PER_MINUTE))) {
   1494         result.setTo(ISO8601_UTC);
   1495         return result;
   1496     }
   1497 
   1498     OffsetFields minFields = isShort ? FIELDS_H : FIELDS_HM;
   1499     OffsetFields maxFields = ignoreSeconds ? FIELDS_HM : FIELDS_HMS;
   1500     UChar sep = isBasic ? 0 : ISO8601_SEP;
   1501 
   1502     // Note: FIELDS_HMS as maxFields is a CLDR/ICU extension. ISO 8601 specification does
   1503     // not support seconds field.
   1504 
   1505     if (absOffset >= MAX_OFFSET) {
   1506         result.setToBogus();
   1507         status = U_ILLEGAL_ARGUMENT_ERROR;
   1508         return result;
   1509     }
   1510 
   1511     int fields[3];
   1512     fields[0] = absOffset / MILLIS_PER_HOUR;
   1513     absOffset = absOffset % MILLIS_PER_HOUR;
   1514     fields[1] = absOffset / MILLIS_PER_MINUTE;
   1515     absOffset = absOffset % MILLIS_PER_MINUTE;
   1516     fields[2] = absOffset / MILLIS_PER_SECOND;
   1517 
   1518     U_ASSERT(fields[0] >= 0 && fields[0] <= MAX_OFFSET_HOUR);
   1519     U_ASSERT(fields[1] >= 0 && fields[1] <= MAX_OFFSET_MINUTE);
   1520     U_ASSERT(fields[2] >= 0 && fields[2] <= MAX_OFFSET_SECOND);
   1521 
   1522     int32_t lastIdx = maxFields;
   1523     while (lastIdx > minFields) {
   1524         if (fields[lastIdx] != 0) {
   1525             break;
   1526         }
   1527         lastIdx--;
   1528     }
   1529 
   1530     UChar sign = PLUS;
   1531     if (offset < 0) {
   1532         // if all output fields are 0s, do not use negative sign
   1533         for (int32_t idx = 0; idx <= lastIdx; idx++) {
   1534             if (fields[idx] != 0) {
   1535                 sign = MINUS;
   1536                 break;
   1537             }
   1538         }
   1539     }
   1540     result.setTo(sign);
   1541 
   1542     for (int32_t idx = 0; idx <= lastIdx; idx++) {
   1543         if (sep && idx != 0) {
   1544             result.append(sep);
   1545         }
   1546         result.append((UChar)(0x0030 + fields[idx]/10));
   1547         result.append((UChar)(0x0030 + fields[idx]%10));
   1548     }
   1549 
   1550     return result;
   1551 }
   1552 
   1553 UnicodeString&
   1554 TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset, UBool isShort, UnicodeString& result, UErrorCode& status) const {
   1555     if (U_FAILURE(status)) {
   1556         result.setToBogus();
   1557         return result;
   1558     }
   1559     if (offset <= -MAX_OFFSET || offset >= MAX_OFFSET) {
   1560         result.setToBogus();
   1561         status = U_ILLEGAL_ARGUMENT_ERROR;
   1562         return result;
   1563     }
   1564 
   1565     if (offset == 0) {
   1566         result.setTo(fGMTZeroFormat);
   1567         return result;
   1568     }
   1569 
   1570     UBool positive = TRUE;
   1571     if (offset < 0) {
   1572         offset = -offset;
   1573         positive = FALSE;
   1574     }
   1575 
   1576     int32_t offsetH = offset / MILLIS_PER_HOUR;
   1577     offset = offset % MILLIS_PER_HOUR;
   1578     int32_t offsetM = offset / MILLIS_PER_MINUTE;
   1579     offset = offset % MILLIS_PER_MINUTE;
   1580     int32_t offsetS = offset / MILLIS_PER_SECOND;
   1581 
   1582     U_ASSERT(offsetH <= MAX_OFFSET_HOUR && offsetM <= MAX_OFFSET_MINUTE && offsetS <= MAX_OFFSET_SECOND);
   1583 
   1584     const UVector* offsetPatternItems = NULL;
   1585     if (positive) {
   1586         if (offsetS != 0) {
   1587             offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_HMS];
   1588         } else if (offsetM != 0 || !isShort) {
   1589             offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_HM];
   1590         } else {
   1591             offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_H];
   1592         }
   1593     } else {
   1594         if (offsetS != 0) {
   1595             offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_HMS];
   1596         } else if (offsetM != 0 || !isShort) {
   1597             offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_HM];
   1598         } else {
   1599             offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_H];
   1600         }
   1601     }
   1602 
   1603     U_ASSERT(offsetPatternItems != NULL);
   1604 
   1605     // Building the GMT format string
   1606     result.setTo(fGMTPatternPrefix);
   1607 
   1608     for (int32_t i = 0; i < offsetPatternItems->size(); i++) {
   1609         const GMTOffsetField* item = (GMTOffsetField*)offsetPatternItems->elementAt(i);
   1610         GMTOffsetField::FieldType type = item->getType();
   1611 
   1612         switch (type) {
   1613         case GMTOffsetField::TEXT:
   1614             result.append(item->getPatternText(), -1);
   1615             break;
   1616 
   1617         case GMTOffsetField::HOUR:
   1618             appendOffsetDigits(result, offsetH, (isShort ? 1 : 2));
   1619             break;
   1620 
   1621         case GMTOffsetField::MINUTE:
   1622             appendOffsetDigits(result, offsetM, 2);
   1623             break;
   1624 
   1625         case GMTOffsetField::SECOND:
   1626             appendOffsetDigits(result, offsetS, 2);
   1627             break;
   1628         }
   1629     }
   1630 
   1631     result.append(fGMTPatternSuffix);
   1632     return result;
   1633 }
   1634 
   1635 int32_t
   1636 TimeZoneFormat::parseOffsetISO8601(const UnicodeString& text, ParsePosition& pos, UBool extendedOnly, UBool* hasDigitOffset /* = NULL */) const {
   1637     if (hasDigitOffset) {
   1638         *hasDigitOffset = FALSE;
   1639     }
   1640     int32_t start = pos.getIndex();
   1641     if (start >= text.length()) {
   1642         pos.setErrorIndex(start);
   1643         return 0;
   1644     }
   1645 
   1646     UChar firstChar = text.charAt(start);
   1647     if (firstChar == ISO8601_UTC || firstChar == (UChar)(ISO8601_UTC + 0x20)) {
   1648         // "Z" (or "z") - indicates UTC
   1649         pos.setIndex(start + 1);
   1650         return 0;
   1651     }
   1652 
   1653     int32_t sign = 1;
   1654     if (firstChar == PLUS) {
   1655         sign = 1;
   1656     } else if (firstChar == MINUS) {
   1657         sign = -1;
   1658     } else {
   1659         // Not an ISO 8601 offset string
   1660         pos.setErrorIndex(start);
   1661         return 0;
   1662     }
   1663     ParsePosition posOffset(start + 1);
   1664     int32_t offset = parseAsciiOffsetFields(text, posOffset, ISO8601_SEP, FIELDS_H, FIELDS_HMS);
   1665     if (posOffset.getErrorIndex() == -1 && !extendedOnly && (posOffset.getIndex() - start <= 3)) {
   1666         // If the text is successfully parsed as extended format with the options above, it can be also parsed
   1667         // as basic format. For example, "0230" can be parsed as offset 2:00 (only first digits are valid for
   1668         // extended format), but it can be parsed as offset 2:30 with basic format. We use longer result.
   1669         ParsePosition posBasic(start + 1);
   1670         int32_t tmpOffset = parseAbuttingAsciiOffsetFields(text, posBasic, FIELDS_H, FIELDS_HMS, FALSE);
   1671         if (posBasic.getErrorIndex() == -1 && posBasic.getIndex() > posOffset.getIndex()) {
   1672             offset = tmpOffset;
   1673             posOffset.setIndex(posBasic.getIndex());
   1674         }
   1675     }
   1676 
   1677     if (posOffset.getErrorIndex() != -1) {
   1678         pos.setErrorIndex(start);
   1679         return 0;
   1680     }
   1681 
   1682     pos.setIndex(posOffset.getIndex());
   1683     if (hasDigitOffset) {
   1684         *hasDigitOffset = TRUE;
   1685     }
   1686     return sign * offset;
   1687 }
   1688 
   1689 int32_t
   1690 TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString& text, ParsePosition& pos, UBool isShort, UBool* hasDigitOffset) const {
   1691     int32_t start = pos.getIndex();
   1692     int32_t offset = 0;
   1693     int32_t parsedLength = 0;
   1694 
   1695     if (hasDigitOffset) {
   1696         *hasDigitOffset = FALSE;
   1697     }
   1698 
   1699     offset = parseOffsetLocalizedGMTPattern(text, start, isShort, parsedLength);
   1700 
   1701     // For now, parseOffsetLocalizedGMTPattern handles both long and short
   1702     // formats, no matter isShort is true or false. This might be changed in future
   1703     // when strict parsing is necessary, or different set of patterns are used for
   1704     // short/long formats.
   1705 #if 0
   1706     if (parsedLength == 0) {
   1707         offset = parseOffsetLocalizedGMTPattern(text, start, !isShort, parsedLength);
   1708     }
   1709 #endif
   1710 
   1711     if (parsedLength > 0) {
   1712         if (hasDigitOffset) {
   1713             *hasDigitOffset = TRUE;
   1714         }
   1715         pos.setIndex(start + parsedLength);
   1716         return offset;
   1717     }
   1718 
   1719     // Try the default patterns
   1720     offset = parseOffsetDefaultLocalizedGMT(text, start, parsedLength);
   1721     if (parsedLength > 0) {
   1722         if (hasDigitOffset) {
   1723             *hasDigitOffset = TRUE;
   1724         }
   1725         pos.setIndex(start + parsedLength);
   1726         return offset;
   1727     }
   1728 
   1729     // Check if this is a GMT zero format
   1730     if (text.caseCompare(start, fGMTZeroFormat.length(), fGMTZeroFormat, 0) == 0) {
   1731         pos.setIndex(start + fGMTZeroFormat.length());
   1732         return 0;
   1733     }
   1734 
   1735     // Check if this is a default GMT zero format
   1736     for (int32_t i = 0; ALT_GMT_STRINGS[i][0] != 0; i++) {
   1737         const UChar* defGMTZero = ALT_GMT_STRINGS[i];
   1738         int32_t defGMTZeroLen = u_strlen(defGMTZero);
   1739         if (text.caseCompare(start, defGMTZeroLen, defGMTZero, 0) == 0) {
   1740             pos.setIndex(start + defGMTZeroLen);
   1741             return 0;
   1742         }
   1743     }
   1744 
   1745     // Nothing matched
   1746     pos.setErrorIndex(start);
   1747     return 0;
   1748 }
   1749 
   1750 int32_t
   1751 TimeZoneFormat::parseOffsetLocalizedGMTPattern(const UnicodeString& text, int32_t start, UBool /*isShort*/, int32_t& parsedLen) const {
   1752     int32_t idx = start;
   1753     int32_t offset = 0;
   1754     UBool parsed = FALSE;
   1755 
   1756     do {
   1757         // Prefix part
   1758         int32_t len = fGMTPatternPrefix.length();
   1759         if (len > 0 && text.caseCompare(idx, len, fGMTPatternPrefix, 0) != 0) {
   1760             // prefix match failed
   1761             break;
   1762         }
   1763         idx += len;
   1764 
   1765         // Offset part
   1766         offset = parseOffsetFields(text, idx, FALSE, len);
   1767         if (len == 0) {
   1768             // offset field match failed
   1769             break;
   1770         }
   1771         idx += len;
   1772 
   1773         len = fGMTPatternSuffix.length();
   1774         if (len > 0 && text.caseCompare(idx, len, fGMTPatternSuffix, 0) != 0) {
   1775             // no suffix match
   1776             break;
   1777         }
   1778         idx += len;
   1779         parsed = TRUE;
   1780     } while (FALSE);
   1781 
   1782     parsedLen = parsed ? idx - start : 0;
   1783     return offset;
   1784 }
   1785 
   1786 int32_t
   1787 TimeZoneFormat::parseOffsetFields(const UnicodeString& text, int32_t start, UBool /*isShort*/, int32_t& parsedLen) const {
   1788     int32_t outLen = 0;
   1789     int32_t offset = 0;
   1790     int32_t sign = 1;
   1791 
   1792     parsedLen = 0;
   1793 
   1794     int32_t offsetH, offsetM, offsetS;
   1795     offsetH = offsetM = offsetS = 0;
   1796 
   1797     for (int32_t patidx = 0; PARSE_GMT_OFFSET_TYPES[patidx] >= 0; patidx++) {
   1798         int32_t gmtPatType = PARSE_GMT_OFFSET_TYPES[patidx];
   1799         UVector* items = fGMTOffsetPatternItems[gmtPatType];
   1800         U_ASSERT(items != NULL);
   1801 
   1802         outLen = parseOffsetFieldsWithPattern(text, start, items, FALSE, offsetH, offsetM, offsetS);
   1803         if (outLen > 0) {
   1804             sign = (gmtPatType == UTZFMT_PAT_POSITIVE_H || gmtPatType == UTZFMT_PAT_POSITIVE_HM || gmtPatType == UTZFMT_PAT_POSITIVE_HMS) ?
   1805                 1 : -1;
   1806             break;
   1807         }
   1808     }
   1809 
   1810     if (outLen > 0 && fAbuttingOffsetHoursAndMinutes) {
   1811         // When hours field is sabutting minutes field,
   1812         // the parse result above may not be appropriate.
   1813         // For example, "01020" is parsed as 01:02: above,
   1814         // but it should be parsed as 00:10:20.
   1815         int32_t tmpLen = 0;
   1816         int32_t tmpSign = 1;
   1817         int32_t tmpH = 0;
   1818         int32_t tmpM = 0;
   1819         int32_t tmpS = 0;
   1820 
   1821         for (int32_t patidx = 0; PARSE_GMT_OFFSET_TYPES[patidx] >= 0; patidx++) {
   1822             int32_t gmtPatType = PARSE_GMT_OFFSET_TYPES[patidx];
   1823             UVector* items = fGMTOffsetPatternItems[gmtPatType];
   1824             U_ASSERT(items != NULL);
   1825 
   1826             // forcing parse to use single hour digit
   1827             tmpLen = parseOffsetFieldsWithPattern(text, start, items, TRUE, tmpH, tmpM, tmpS);
   1828             if (tmpLen > 0) {
   1829                 tmpSign = (gmtPatType == UTZFMT_PAT_POSITIVE_H || gmtPatType == UTZFMT_PAT_POSITIVE_HM || gmtPatType == UTZFMT_PAT_POSITIVE_HMS) ?
   1830                     1 : -1;
   1831                 break;
   1832             }
   1833         }
   1834         if (tmpLen > outLen) {
   1835             // Better parse result with single hour digit
   1836             outLen = tmpLen;
   1837             sign = tmpSign;
   1838             offsetH = tmpH;
   1839             offsetM = tmpM;
   1840             offsetS = tmpS;
   1841         }
   1842     }
   1843 
   1844     if (outLen > 0) {
   1845         offset = ((((offsetH * 60) + offsetM) * 60) + offsetS) * 1000 * sign;
   1846         parsedLen = outLen;
   1847     }
   1848 
   1849     return offset;
   1850 }
   1851 
   1852 int32_t
   1853 TimeZoneFormat::parseOffsetFieldsWithPattern(const UnicodeString& text, int32_t start,
   1854         UVector* patternItems, UBool forceSingleHourDigit, int32_t& hour, int32_t& min, int32_t& sec) const {
   1855     UBool failed = FALSE;
   1856     int32_t offsetH, offsetM, offsetS;
   1857     offsetH = offsetM = offsetS = 0;
   1858     int32_t idx = start;
   1859 
   1860     for (int32_t i = 0; i < patternItems->size(); i++) {
   1861         int32_t len = 0;
   1862         const GMTOffsetField* field = (const GMTOffsetField*)patternItems->elementAt(i);
   1863         GMTOffsetField::FieldType fieldType = field->getType();
   1864         if (fieldType == GMTOffsetField::TEXT) {
   1865             const UChar* patStr = field->getPatternText();
   1866             len = u_strlen(patStr);
   1867             if (i == 0) {
   1868                 // When TimeZoneFormat parse() is called from SimpleDateFormat,
   1869                 // leading space characters might be truncated. If the first pattern text
   1870                 // starts with such character (e.g. Bidi control), then we need to
   1871                 // skip the leading space charcters.
   1872                 if (idx < text.length() && !PatternProps::isWhiteSpace(text.char32At(idx))) {
   1873                     while (len > 0) {
   1874                         UChar32 ch;
   1875                         int32_t chLen;
   1876                         U16_GET(patStr, 0, 0, len, ch)
   1877                         if (PatternProps::isWhiteSpace(ch)) {
   1878                             chLen = U16_LENGTH(ch);
   1879                             len -= chLen;
   1880                             patStr += chLen;
   1881                         }
   1882                         else {
   1883                             break;
   1884                         }
   1885                     }
   1886                 }
   1887             }
   1888             if (text.caseCompare(idx, len, patStr, 0) != 0) {
   1889                 failed = TRUE;
   1890                 break;
   1891             }
   1892             idx += len;
   1893         } else {
   1894             if (fieldType == GMTOffsetField::HOUR) {
   1895                 uint8_t maxDigits = forceSingleHourDigit ? 1 : 2;
   1896                 offsetH = parseOffsetFieldWithLocalizedDigits(text, idx, 1, maxDigits, 0, MAX_OFFSET_HOUR, len);
   1897             } else if (fieldType == GMTOffsetField::MINUTE) {
   1898                 offsetM = parseOffsetFieldWithLocalizedDigits(text, idx, 2, 2, 0, MAX_OFFSET_MINUTE, len);
   1899             } else if (fieldType == GMTOffsetField::SECOND) {
   1900                 offsetS = parseOffsetFieldWithLocalizedDigits(text, idx, 2, 2, 0, MAX_OFFSET_SECOND, len);
   1901             }
   1902 
   1903             if (len == 0) {
   1904                 failed = TRUE;
   1905                 break;
   1906             }
   1907             idx += len;
   1908         }
   1909     }
   1910 
   1911     if (failed) {
   1912         hour = min = sec = 0;
   1913         return 0;
   1914     }
   1915 
   1916     hour = offsetH;
   1917     min = offsetM;
   1918     sec = offsetS;
   1919 
   1920     return idx - start;
   1921 }
   1922 
   1923 int32_t
   1924 TimeZoneFormat::parseAbuttingOffsetFields(const UnicodeString& text, int32_t start, int32_t& parsedLen) const {
   1925     int32_t digits[MAX_OFFSET_DIGITS];
   1926     int32_t parsed[MAX_OFFSET_DIGITS];  // accumulative offsets
   1927 
   1928     // Parse digits into int[]
   1929     int32_t idx = start;
   1930     int32_t len = 0;
   1931     int32_t numDigits = 0;
   1932     for (int32_t i = 0; i < MAX_OFFSET_DIGITS; i++) {
   1933         digits[i] = parseSingleLocalizedDigit(text, idx, len);
   1934         if (digits[i] < 0) {
   1935             break;
   1936         }
   1937         idx += len;
   1938         parsed[i] = idx - start;
   1939         numDigits++;
   1940     }
   1941 
   1942     if (numDigits == 0) {
   1943         parsedLen = 0;
   1944         return 0;
   1945     }
   1946 
   1947     int32_t offset = 0;
   1948     while (numDigits > 0) {
   1949         int32_t hour = 0;
   1950         int32_t min = 0;
   1951         int32_t sec = 0;
   1952 
   1953         U_ASSERT(numDigits > 0 && numDigits <= MAX_OFFSET_DIGITS);
   1954         switch (numDigits) {
   1955         case 1: // H
   1956             hour = digits[0];
   1957             break;
   1958         case 2: // HH
   1959             hour = digits[0] * 10 + digits[1];
   1960             break;
   1961         case 3: // Hmm
   1962             hour = digits[0];
   1963             min = digits[1] * 10 + digits[2];
   1964             break;
   1965         case 4: // HHmm
   1966             hour = digits[0] * 10 + digits[1];
   1967             min = digits[2] * 10 + digits[3];
   1968             break;
   1969         case 5: // Hmmss
   1970             hour = digits[0];
   1971             min = digits[1] * 10 + digits[2];
   1972             sec = digits[3] * 10 + digits[4];
   1973             break;
   1974         case 6: // HHmmss
   1975             hour = digits[0] * 10 + digits[1];
   1976             min = digits[2] * 10 + digits[3];
   1977             sec = digits[4] * 10 + digits[5];
   1978             break;
   1979         }
   1980         if (hour <= MAX_OFFSET_HOUR && min <= MAX_OFFSET_MINUTE && sec <= MAX_OFFSET_SECOND) {
   1981             // found a valid combination
   1982             offset = hour * MILLIS_PER_HOUR + min * MILLIS_PER_MINUTE + sec * MILLIS_PER_SECOND;
   1983             parsedLen = parsed[numDigits - 1];
   1984             break;
   1985         }
   1986         numDigits--;
   1987     }
   1988     return offset;
   1989 }
   1990 
   1991 int32_t
   1992 TimeZoneFormat::parseOffsetDefaultLocalizedGMT(const UnicodeString& text, int start, int32_t& parsedLen) const {
   1993     int32_t idx = start;
   1994     int32_t offset = 0;
   1995     int32_t parsed = 0;
   1996 
   1997     do {
   1998         // check global default GMT alternatives
   1999         int32_t gmtLen = 0;
   2000 
   2001         for (int32_t i = 0; ALT_GMT_STRINGS[i][0] != 0; i++) {
   2002             const UChar* gmt = ALT_GMT_STRINGS[i];
   2003             int32_t len = u_strlen(gmt);
   2004             if (text.caseCompare(start, len, gmt, 0) == 0) {
   2005                 gmtLen = len;
   2006                 break;
   2007             }
   2008         }
   2009         if (gmtLen == 0) {
   2010             break;
   2011         }
   2012         idx += gmtLen;
   2013 
   2014         // offset needs a sign char and a digit at minimum
   2015         if (idx + 1 >= text.length()) {
   2016             break;
   2017         }
   2018 
   2019         // parse sign
   2020         int32_t sign = 1;
   2021         UChar c = text.charAt(idx);
   2022         if (c == PLUS) {
   2023             sign = 1;
   2024         } else if (c == MINUS) {
   2025             sign = -1;
   2026         } else {
   2027             break;
   2028         }
   2029         idx++;
   2030 
   2031         // offset part
   2032         // try the default pattern with the separator first
   2033         int32_t lenWithSep = 0;
   2034         int32_t offsetWithSep = parseDefaultOffsetFields(text, idx, DEFAULT_GMT_OFFSET_SEP, lenWithSep);
   2035         if (lenWithSep == text.length() - idx) {
   2036             // maximum match
   2037             offset = offsetWithSep * sign;
   2038             idx += lenWithSep;
   2039         } else {
   2040             // try abutting field pattern
   2041             int32_t lenAbut = 0;
   2042             int32_t offsetAbut = parseAbuttingOffsetFields(text, idx, lenAbut);
   2043 
   2044             if (lenWithSep > lenAbut) {
   2045                 offset = offsetWithSep * sign;
   2046                 idx += lenWithSep;
   2047             } else {
   2048                 offset = offsetAbut * sign;
   2049                 idx += lenAbut;
   2050             }
   2051         }
   2052         parsed = idx - start;
   2053     } while (false);
   2054 
   2055     parsedLen = parsed;
   2056     return offset;
   2057 }
   2058 
   2059 int32_t
   2060 TimeZoneFormat::parseDefaultOffsetFields(const UnicodeString& text, int32_t start, UChar separator, int32_t& parsedLen) const {
   2061     int32_t max = text.length();
   2062     int32_t idx = start;
   2063     int32_t len = 0;
   2064     int32_t hour = 0, min = 0, sec = 0;
   2065 
   2066     parsedLen = 0;
   2067 
   2068     do {
   2069         hour = parseOffsetFieldWithLocalizedDigits(text, idx, 1, 2, 0, MAX_OFFSET_HOUR, len);
   2070         if (len == 0) {
   2071             break;
   2072         }
   2073         idx += len;
   2074 
   2075         if (idx + 1 < max && text.charAt(idx) == separator) {
   2076             min = parseOffsetFieldWithLocalizedDigits(text, idx + 1, 2, 2, 0, MAX_OFFSET_MINUTE, len);
   2077             if (len == 0) {
   2078                 break;
   2079             }
   2080             idx += (1 + len);
   2081 
   2082             if (idx + 1 < max && text.charAt(idx) == separator) {
   2083                 sec = parseOffsetFieldWithLocalizedDigits(text, idx + 1, 2, 2, 0, MAX_OFFSET_SECOND, len);
   2084                 if (len == 0) {
   2085                     break;
   2086                 }
   2087                 idx += (1 + len);
   2088             }
   2089         }
   2090     } while (FALSE);
   2091 
   2092     if (idx == start) {
   2093         return 0;
   2094     }
   2095 
   2096     parsedLen = idx - start;
   2097     return hour * MILLIS_PER_HOUR + min * MILLIS_PER_MINUTE + sec * MILLIS_PER_SECOND;
   2098 }
   2099 
   2100 int32_t
   2101 TimeZoneFormat::parseOffsetFieldWithLocalizedDigits(const UnicodeString& text, int32_t start, uint8_t minDigits, uint8_t maxDigits, uint16_t minVal, uint16_t maxVal, int32_t& parsedLen) const {
   2102     parsedLen = 0;
   2103 
   2104     int32_t decVal = 0;
   2105     int32_t numDigits = 0;
   2106     int32_t idx = start;
   2107     int32_t digitLen = 0;
   2108 
   2109     while (idx < text.length() && numDigits < maxDigits) {
   2110         int32_t digit = parseSingleLocalizedDigit(text, idx, digitLen);
   2111         if (digit < 0) {
   2112             break;
   2113         }
   2114         int32_t tmpVal = decVal * 10 + digit;
   2115         if (tmpVal > maxVal) {
   2116             break;
   2117         }
   2118         decVal = tmpVal;
   2119         numDigits++;
   2120         idx += digitLen;
   2121     }
   2122 
   2123     // Note: maxVal is checked in the while loop
   2124     if (numDigits < minDigits || decVal < minVal) {
   2125         decVal = -1;
   2126         numDigits = 0;
   2127     } else {
   2128         parsedLen = idx - start;
   2129     }
   2130 
   2131     return decVal;
   2132 }
   2133 
   2134 int32_t
   2135 TimeZoneFormat::parseSingleLocalizedDigit(const UnicodeString& text, int32_t start, int32_t& len) const {
   2136     int32_t digit = -1;
   2137     len = 0;
   2138     if (start < text.length()) {
   2139         UChar32 cp = text.char32At(start);
   2140 
   2141         // First, try digits configured for this instance
   2142         for (int32_t i = 0; i < 10; i++) {
   2143             if (cp == fGMTOffsetDigits[i]) {
   2144                 digit = i;
   2145                 break;
   2146             }
   2147         }
   2148         // If failed, check if this is a Unicode digit
   2149         if (digit < 0) {
   2150             int32_t tmp = u_charDigitValue(cp);
   2151             digit = (tmp >= 0 && tmp <= 9) ? tmp : -1;
   2152         }
   2153 
   2154         if (digit >= 0) {
   2155             int32_t next = text.moveIndex32(start, 1);
   2156             len = next - start;
   2157         }
   2158     }
   2159     return digit;
   2160 }
   2161 
   2162 UnicodeString&
   2163 TimeZoneFormat::formatOffsetWithAsciiDigits(int32_t offset, UChar sep, OffsetFields minFields, OffsetFields maxFields, UnicodeString& result) {
   2164     U_ASSERT(maxFields >= minFields);
   2165     U_ASSERT(offset > -MAX_OFFSET && offset < MAX_OFFSET);
   2166 
   2167     UChar sign = PLUS;
   2168     if (offset < 0) {
   2169         sign = MINUS;
   2170         offset = -offset;
   2171     }
   2172     result.setTo(sign);
   2173 
   2174     int fields[3];
   2175     fields[0] = offset / MILLIS_PER_HOUR;
   2176     offset = offset % MILLIS_PER_HOUR;
   2177     fields[1] = offset / MILLIS_PER_MINUTE;
   2178     offset = offset % MILLIS_PER_MINUTE;
   2179     fields[2] = offset / MILLIS_PER_SECOND;
   2180 
   2181     U_ASSERT(fields[0] >= 0 && fields[0] <= MAX_OFFSET_HOUR);
   2182     U_ASSERT(fields[1] >= 0 && fields[1] <= MAX_OFFSET_MINUTE);
   2183     U_ASSERT(fields[2] >= 0 && fields[2] <= MAX_OFFSET_SECOND);
   2184 
   2185     int32_t lastIdx = maxFields;
   2186     while (lastIdx > minFields) {
   2187         if (fields[lastIdx] != 0) {
   2188             break;
   2189         }
   2190         lastIdx--;
   2191     }
   2192 
   2193     for (int32_t idx = 0; idx <= lastIdx; idx++) {
   2194         if (sep && idx != 0) {
   2195             result.append(sep);
   2196         }
   2197         result.append((UChar)(0x0030 + fields[idx]/10));
   2198         result.append((UChar)(0x0030 + fields[idx]%10));
   2199     }
   2200 
   2201     return result;
   2202 }
   2203 
   2204 int32_t
   2205 TimeZoneFormat::parseAbuttingAsciiOffsetFields(const UnicodeString& text, ParsePosition& pos, OffsetFields minFields, OffsetFields maxFields, UBool fixedHourWidth) {
   2206     int32_t start = pos.getIndex();
   2207 
   2208     int32_t minDigits = 2 * (minFields + 1) - (fixedHourWidth ? 0 : 1);
   2209     int32_t maxDigits = 2 * (maxFields + 1);
   2210 
   2211     U_ASSERT(maxDigits <= MAX_OFFSET_DIGITS);
   2212 
   2213     int32_t digits[MAX_OFFSET_DIGITS] = {};
   2214     int32_t numDigits = 0;
   2215     int32_t idx = start;
   2216     while (numDigits < maxDigits && idx < text.length()) {
   2217         UChar uch = text.charAt(idx);
   2218         int32_t digit = DIGIT_VAL(uch);
   2219         if (digit < 0) {
   2220             break;
   2221         }
   2222         digits[numDigits] = digit;
   2223         numDigits++;
   2224         idx++;
   2225     }
   2226 
   2227     if (fixedHourWidth && (numDigits & 1)) {
   2228         // Fixed digits, so the number of digits must be even number. Truncating.
   2229         numDigits--;
   2230     }
   2231 
   2232     if (numDigits < minDigits) {
   2233         pos.setErrorIndex(start);
   2234         return 0;
   2235     }
   2236 
   2237     int32_t hour = 0, min = 0, sec = 0;
   2238     UBool bParsed = FALSE;
   2239     while (numDigits >= minDigits) {
   2240         switch (numDigits) {
   2241         case 1: //H
   2242             hour = digits[0];
   2243             break;
   2244         case 2: //HH
   2245             hour = digits[0] * 10 + digits[1];
   2246             break;
   2247         case 3: //Hmm
   2248             hour = digits[0];
   2249             min = digits[1] * 10 + digits[2];
   2250             break;
   2251         case 4: //HHmm
   2252             hour = digits[0] * 10 + digits[1];
   2253             min = digits[2] * 10 + digits[3];
   2254             break;
   2255         case 5: //Hmmss
   2256             hour = digits[0];
   2257             min = digits[1] * 10 + digits[2];
   2258             sec = digits[3] * 10 + digits[4];
   2259             break;
   2260         case 6: //HHmmss
   2261             hour = digits[0] * 10 + digits[1];
   2262             min = digits[2] * 10 + digits[3];
   2263             sec = digits[4] * 10 + digits[5];
   2264             break;
   2265         }
   2266 
   2267         if (hour <= MAX_OFFSET_HOUR && min <= MAX_OFFSET_MINUTE && sec <= MAX_OFFSET_SECOND) {
   2268             // Successfully parsed
   2269             bParsed = true;
   2270             break;
   2271         }
   2272 
   2273         // Truncating
   2274         numDigits -= (fixedHourWidth ? 2 : 1);
   2275         hour = min = sec = 0;
   2276     }
   2277 
   2278     if (!bParsed) {
   2279         pos.setErrorIndex(start);
   2280         return 0;
   2281     }
   2282     pos.setIndex(start + numDigits);
   2283     return ((((hour * 60) + min) * 60) + sec) * 1000;
   2284 }
   2285 
   2286 int32_t
   2287 TimeZoneFormat::parseAsciiOffsetFields(const UnicodeString& text, ParsePosition& pos, UChar sep, OffsetFields minFields, OffsetFields maxFields) {
   2288     int32_t start = pos.getIndex();
   2289     int32_t fieldVal[] = {0, 0, 0};
   2290     int32_t fieldLen[] = {0, -1, -1};
   2291     for (int32_t idx = start, fieldIdx = 0; idx < text.length() && fieldIdx <= maxFields; idx++) {
   2292         UChar c = text.charAt(idx);
   2293         if (c == sep) {
   2294             if (fieldIdx == 0) {
   2295                 if (fieldLen[0] == 0) {
   2296                     // no hours field
   2297                     break;
   2298                 }
   2299                 // 1 digit hour, move to next field
   2300             } else {
   2301                 if (fieldLen[fieldIdx] != -1) {
   2302                     // premature minute or seconds field
   2303                     break;
   2304                 }
   2305                 fieldLen[fieldIdx] = 0;
   2306             }
   2307             continue;
   2308         } else if (fieldLen[fieldIdx] == -1) {
   2309             // no separator after 2 digit field
   2310             break;
   2311         }
   2312         int32_t digit = DIGIT_VAL(c);
   2313         if (digit < 0) {
   2314             // not a digit
   2315             break;
   2316         }
   2317         fieldVal[fieldIdx] = fieldVal[fieldIdx] * 10 + digit;
   2318         fieldLen[fieldIdx]++;
   2319         if (fieldLen[fieldIdx] >= 2) {
   2320             // parsed 2 digits, move to next field
   2321             fieldIdx++;
   2322         }
   2323     }
   2324 
   2325     int32_t offset = 0;
   2326     int32_t parsedLen = 0;
   2327     int32_t parsedFields = -1;
   2328     do {
   2329         // hour
   2330         if (fieldLen[0] == 0) {
   2331             break;
   2332         }
   2333         if (fieldVal[0] > MAX_OFFSET_HOUR) {
   2334             offset = (fieldVal[0] / 10) * MILLIS_PER_HOUR;
   2335             parsedFields = FIELDS_H;
   2336             parsedLen = 1;
   2337             break;
   2338         }
   2339         offset = fieldVal[0] * MILLIS_PER_HOUR;
   2340         parsedLen = fieldLen[0];
   2341         parsedFields = FIELDS_H;
   2342 
   2343         // minute
   2344         if (fieldLen[1] != 2 || fieldVal[1] > MAX_OFFSET_MINUTE) {
   2345             break;
   2346         }
   2347         offset += fieldVal[1] * MILLIS_PER_MINUTE;
   2348         parsedLen += (1 + fieldLen[1]);
   2349         parsedFields = FIELDS_HM;
   2350 
   2351         // second
   2352         if (fieldLen[2] != 2 || fieldVal[2] > MAX_OFFSET_SECOND) {
   2353             break;
   2354         }
   2355         offset += fieldVal[2] * MILLIS_PER_SECOND;
   2356         parsedLen += (1 + fieldLen[2]);
   2357         parsedFields = FIELDS_HMS;
   2358     } while (false);
   2359 
   2360     if (parsedFields < minFields) {
   2361         pos.setErrorIndex(start);
   2362         return 0;
   2363     }
   2364 
   2365     pos.setIndex(start + parsedLen);
   2366     return offset;
   2367 }
   2368 
   2369 void
   2370 TimeZoneFormat::appendOffsetDigits(UnicodeString& buf, int32_t n, uint8_t minDigits) const {
   2371     U_ASSERT(n >= 0 && n < 60);
   2372     int32_t numDigits = n >= 10 ? 2 : 1;
   2373     for (int32_t i = 0; i < minDigits - numDigits; i++) {
   2374         buf.append(fGMTOffsetDigits[0]);
   2375     }
   2376     if (numDigits == 2) {
   2377         buf.append(fGMTOffsetDigits[n / 10]);
   2378     }
   2379     buf.append(fGMTOffsetDigits[n % 10]);
   2380 }
   2381 
   2382 // ------------------------------------------------------------------
   2383 // Private misc
   2384 void
   2385 TimeZoneFormat::initGMTPattern(const UnicodeString& gmtPattern, UErrorCode& status) {
   2386     if (U_FAILURE(status)) {
   2387         return;
   2388     }
   2389     // This implementation not perfect, but sufficient practically.
   2390     int32_t idx = gmtPattern.indexOf(ARG0, ARG0_LEN, 0);
   2391     if (idx < 0) {
   2392         status = U_ILLEGAL_ARGUMENT_ERROR;
   2393         return;
   2394     }
   2395     fGMTPattern.setTo(gmtPattern);
   2396     unquote(gmtPattern.tempSubString(0, idx), fGMTPatternPrefix);
   2397     unquote(gmtPattern.tempSubString(idx + ARG0_LEN), fGMTPatternSuffix);
   2398 }
   2399 
   2400 UnicodeString&
   2401 TimeZoneFormat::unquote(const UnicodeString& pattern, UnicodeString& result) {
   2402     if (pattern.indexOf(SINGLEQUOTE) < 0) {
   2403         result.setTo(pattern);
   2404         return result;
   2405     }
   2406     result.remove();
   2407     UBool isPrevQuote = FALSE;
   2408     UBool inQuote = FALSE;
   2409     for (int32_t i = 0; i < pattern.length(); i++) {
   2410         UChar c = pattern.charAt(i);
   2411         if (c == SINGLEQUOTE) {
   2412             if (isPrevQuote) {
   2413                 result.append(c);
   2414                 isPrevQuote = FALSE;
   2415             } else {
   2416                 isPrevQuote = TRUE;
   2417             }
   2418             inQuote = !inQuote;
   2419         } else {
   2420             isPrevQuote = FALSE;
   2421             result.append(c);
   2422         }
   2423     }
   2424     return result;
   2425 }
   2426 
   2427 UVector*
   2428 TimeZoneFormat::parseOffsetPattern(const UnicodeString& pattern, OffsetFields required, UErrorCode& status) {
   2429     if (U_FAILURE(status)) {
   2430         return NULL;
   2431     }
   2432     UVector* result = new UVector(deleteGMTOffsetField, NULL, status);
   2433     if (result == NULL) {
   2434         status = U_MEMORY_ALLOCATION_ERROR;
   2435         return NULL;
   2436     }
   2437 
   2438     int32_t checkBits = 0;
   2439     UBool isPrevQuote = FALSE;
   2440     UBool inQuote = FALSE;
   2441     UChar textBuf[32];
   2442     UnicodeString text(textBuf, 0, UPRV_LENGTHOF(textBuf));
   2443     GMTOffsetField::FieldType itemType = GMTOffsetField::TEXT;
   2444     int32_t itemLength = 1;
   2445 
   2446     for (int32_t i = 0; i < pattern.length(); i++) {
   2447         UChar ch = pattern.charAt(i);
   2448         if (ch == SINGLEQUOTE) {
   2449             if (isPrevQuote) {
   2450                 text.append(SINGLEQUOTE);
   2451                 isPrevQuote = FALSE;
   2452             } else {
   2453                 isPrevQuote = TRUE;
   2454                 if (itemType != GMTOffsetField::TEXT) {
   2455                     if (GMTOffsetField::isValid(itemType, itemLength)) {
   2456                         GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, static_cast<uint8_t>(itemLength), status);
   2457                         result->addElement(fld, status);
   2458                         if (U_FAILURE(status)) {
   2459                             break;
   2460                         }
   2461                     } else {
   2462                         status = U_ILLEGAL_ARGUMENT_ERROR;
   2463                         break;
   2464                     }
   2465                     itemType = GMTOffsetField::TEXT;
   2466                 }
   2467             }
   2468             inQuote = !inQuote;
   2469         } else {
   2470             isPrevQuote = FALSE;
   2471             if (inQuote) {
   2472                 text.append(ch);
   2473             } else {
   2474                 GMTOffsetField::FieldType tmpType = GMTOffsetField::getTypeByLetter(ch);
   2475                 if (tmpType != GMTOffsetField::TEXT) {
   2476                     // an offset time pattern character
   2477                     if (tmpType == itemType) {
   2478                         itemLength++;
   2479                     } else {
   2480                         if (itemType == GMTOffsetField::TEXT) {
   2481                             if (text.length() > 0) {
   2482                                 GMTOffsetField* textfld = GMTOffsetField::createText(text, status);
   2483                                 result->addElement(textfld, status);
   2484                                 if (U_FAILURE(status)) {
   2485                                     break;
   2486                                 }
   2487                                 text.remove();
   2488                             }
   2489                         } else {
   2490                             if (GMTOffsetField::isValid(itemType, itemLength)) {
   2491                                 GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, static_cast<uint8_t>(itemLength), status);
   2492                                 result->addElement(fld, status);
   2493                                 if (U_FAILURE(status)) {
   2494                                     break;
   2495                                 }
   2496                             } else {
   2497                                 status = U_ILLEGAL_ARGUMENT_ERROR;
   2498                                 break;
   2499                             }
   2500                         }
   2501                         itemType = tmpType;
   2502                         itemLength = 1;
   2503                         checkBits |= tmpType;
   2504                     }
   2505                 } else {
   2506                     // a string literal
   2507                     if (itemType != GMTOffsetField::TEXT) {
   2508                         if (GMTOffsetField::isValid(itemType, itemLength)) {
   2509                             GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, static_cast<uint8_t>(itemLength), status);
   2510                             result->addElement(fld, status);
   2511                             if (U_FAILURE(status)) {
   2512                                 break;
   2513                             }
   2514                         } else {
   2515                             status = U_ILLEGAL_ARGUMENT_ERROR;
   2516                             break;
   2517                         }
   2518                         itemType = GMTOffsetField::TEXT;
   2519                     }
   2520                     text.append(ch);
   2521                 }
   2522             }
   2523         }
   2524     }
   2525     // handle last item
   2526     if (U_SUCCESS(status)) {
   2527         if (itemType == GMTOffsetField::TEXT) {
   2528             if (text.length() > 0) {
   2529                 GMTOffsetField* tfld = GMTOffsetField::createText(text, status);
   2530                 result->addElement(tfld, status);
   2531             }
   2532         } else {
   2533             if (GMTOffsetField::isValid(itemType, itemLength)) {
   2534                 GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, static_cast<uint8_t>(itemLength), status);
   2535                 result->addElement(fld, status);
   2536             } else {
   2537                 status = U_ILLEGAL_ARGUMENT_ERROR;
   2538             }
   2539         }
   2540 
   2541         // Check all required fields are set
   2542         if (U_SUCCESS(status)) {
   2543             int32_t reqBits = 0;
   2544             switch (required) {
   2545             case FIELDS_H:
   2546                 reqBits = GMTOffsetField::HOUR;
   2547                 break;
   2548             case FIELDS_HM:
   2549                 reqBits = GMTOffsetField::HOUR | GMTOffsetField::MINUTE;
   2550                 break;
   2551             case FIELDS_HMS:
   2552                 reqBits = GMTOffsetField::HOUR | GMTOffsetField::MINUTE | GMTOffsetField::SECOND;
   2553                 break;
   2554             }
   2555             if (checkBits == reqBits) {
   2556                 // all required fields are set, no extra fields
   2557                 return result;
   2558             }
   2559         }
   2560     }
   2561 
   2562     // error
   2563     delete result;
   2564     return NULL;
   2565 }
   2566 
   2567 UnicodeString&
   2568 TimeZoneFormat::expandOffsetPattern(const UnicodeString& offsetHM, UnicodeString& result, UErrorCode& status) {
   2569     result.setToBogus();
   2570     if (U_FAILURE(status)) {
   2571         return result;
   2572     }
   2573     U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN) == 2);
   2574 
   2575     int32_t idx_mm = offsetHM.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN, 2, 0);
   2576     if (idx_mm < 0) {
   2577         // Bad time zone hour pattern data
   2578         status = U_ILLEGAL_ARGUMENT_ERROR;
   2579         return result;
   2580     }
   2581 
   2582     UnicodeString sep;
   2583     int32_t idx_H = offsetHM.tempSubString(0, idx_mm).lastIndexOf((UChar)0x0048 /* H */);
   2584     if (idx_H >= 0) {
   2585         sep = offsetHM.tempSubString(idx_H + 1, idx_mm - (idx_H + 1));
   2586     }
   2587     result.setTo(offsetHM.tempSubString(0, idx_mm + 2));
   2588     result.append(sep);
   2589     result.append(DEFAULT_GMT_OFFSET_SECOND_PATTERN, -1);
   2590     result.append(offsetHM.tempSubString(idx_mm + 2));
   2591     return result;
   2592 }
   2593 
   2594 UnicodeString&
   2595 TimeZoneFormat::truncateOffsetPattern(const UnicodeString& offsetHM, UnicodeString& result, UErrorCode& status) {
   2596     result.setToBogus();
   2597     if (U_FAILURE(status)) {
   2598         return result;
   2599     }
   2600     U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN) == 2);
   2601 
   2602     int32_t idx_mm = offsetHM.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN, 2, 0);
   2603     if (idx_mm < 0) {
   2604         // Bad time zone hour pattern data
   2605         status = U_ILLEGAL_ARGUMENT_ERROR;
   2606         return result;
   2607     }
   2608     UChar HH[] = {0x0048, 0x0048};
   2609     int32_t idx_HH = offsetHM.tempSubString(0, idx_mm).lastIndexOf(HH, 2, 0);
   2610     if (idx_HH >= 0) {
   2611         return result.setTo(offsetHM.tempSubString(0, idx_HH + 2));
   2612     }
   2613     int32_t idx_H = offsetHM.tempSubString(0, idx_mm).lastIndexOf((UChar)0x0048, 0);
   2614     if (idx_H >= 0) {
   2615         return result.setTo(offsetHM.tempSubString(0, idx_H + 1));
   2616     }
   2617     // Bad time zone hour pattern data
   2618     status = U_ILLEGAL_ARGUMENT_ERROR;
   2619     return result;
   2620 }
   2621 
   2622 void
   2623 TimeZoneFormat::initGMTOffsetPatterns(UErrorCode& status) {
   2624     for (int32_t type = 0; type < UTZFMT_PAT_COUNT; type++) {
   2625         switch (type) {
   2626         case UTZFMT_PAT_POSITIVE_H:
   2627         case UTZFMT_PAT_NEGATIVE_H:
   2628             fGMTOffsetPatternItems[type] = parseOffsetPattern(fGMTOffsetPatterns[type], FIELDS_H, status);
   2629             break;
   2630         case UTZFMT_PAT_POSITIVE_HM:
   2631         case UTZFMT_PAT_NEGATIVE_HM:
   2632             fGMTOffsetPatternItems[type] = parseOffsetPattern(fGMTOffsetPatterns[type], FIELDS_HM, status);
   2633             break;
   2634         case UTZFMT_PAT_POSITIVE_HMS:
   2635         case UTZFMT_PAT_NEGATIVE_HMS:
   2636             fGMTOffsetPatternItems[type] = parseOffsetPattern(fGMTOffsetPatterns[type], FIELDS_HMS, status);
   2637             break;
   2638         }
   2639     }
   2640     checkAbuttingHoursAndMinutes();
   2641 }
   2642 
   2643 void
   2644 TimeZoneFormat::checkAbuttingHoursAndMinutes() {
   2645     fAbuttingOffsetHoursAndMinutes= FALSE;
   2646     for (int32_t type = 0; type < UTZFMT_PAT_COUNT; type++) {
   2647         UBool afterH = FALSE;
   2648         UVector *items = fGMTOffsetPatternItems[type];
   2649         for (int32_t i = 0; i < items->size(); i++) {
   2650             const GMTOffsetField* item = (GMTOffsetField*)items->elementAt(i);
   2651             GMTOffsetField::FieldType type = item->getType();
   2652             if (type != GMTOffsetField::TEXT) {
   2653                 if (afterH) {
   2654                     fAbuttingOffsetHoursAndMinutes = TRUE;
   2655                     break;
   2656                 } else if (type == GMTOffsetField::HOUR) {
   2657                     afterH = TRUE;
   2658                 }
   2659             } else if (afterH) {
   2660                 break;
   2661             }
   2662         }
   2663         if (fAbuttingOffsetHoursAndMinutes) {
   2664             break;
   2665         }
   2666     }
   2667 }
   2668 
   2669 UBool
   2670 TimeZoneFormat::toCodePoints(const UnicodeString& str, UChar32* codeArray, int32_t size) {
   2671     int32_t count = str.countChar32();
   2672     if (count != size) {
   2673         return FALSE;
   2674     }
   2675 
   2676     for (int32_t idx = 0, start = 0; idx < size; idx++) {
   2677         codeArray[idx] = str.char32At(start);
   2678         start = str.moveIndex32(start, 1);
   2679     }
   2680 
   2681     return TRUE;
   2682 }
   2683 
   2684 TimeZone*
   2685 TimeZoneFormat::createTimeZoneForOffset(int32_t offset) const {
   2686     if (offset == 0) {
   2687         // when offset is 0, we should use "Etc/GMT"
   2688         return TimeZone::createTimeZone(UnicodeString(TRUE, TZID_GMT, -1));
   2689     }
   2690     return ZoneMeta::createCustomTimeZone(offset);
   2691 }
   2692 
   2693 UTimeZoneFormatTimeType
   2694 TimeZoneFormat::getTimeType(UTimeZoneNameType nameType) {
   2695     switch (nameType) {
   2696     case UTZNM_LONG_STANDARD:
   2697     case UTZNM_SHORT_STANDARD:
   2698         return UTZFMT_TIME_TYPE_STANDARD;
   2699 
   2700     case UTZNM_LONG_DAYLIGHT:
   2701     case UTZNM_SHORT_DAYLIGHT:
   2702         return UTZFMT_TIME_TYPE_DAYLIGHT;
   2703 
   2704     default:
   2705         return UTZFMT_TIME_TYPE_UNKNOWN;
   2706     }
   2707 }
   2708 
   2709 UnicodeString&
   2710 TimeZoneFormat::getTimeZoneID(const TimeZoneNames::MatchInfoCollection* matches, int32_t idx, UnicodeString& tzID) const {
   2711     if (!matches->getTimeZoneIDAt(idx, tzID)) {
   2712         UChar mzIDBuf[32];
   2713         UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf));
   2714         if (matches->getMetaZoneIDAt(idx, mzID)) {
   2715             fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, tzID);
   2716         }
   2717     }
   2718     return tzID;
   2719 }
   2720 
   2721 
   2722 class ZoneIdMatchHandler : public TextTrieMapSearchResultHandler {
   2723 public:
   2724     ZoneIdMatchHandler();
   2725     virtual ~ZoneIdMatchHandler();
   2726 
   2727     UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status);
   2728     const UChar* getID();
   2729     int32_t getMatchLen();
   2730 private:
   2731     int32_t fLen;
   2732     const UChar* fID;
   2733 };
   2734 
   2735 ZoneIdMatchHandler::ZoneIdMatchHandler()
   2736 : fLen(0), fID(NULL) {
   2737 }
   2738 
   2739 ZoneIdMatchHandler::~ZoneIdMatchHandler() {
   2740 }
   2741 
   2742 UBool
   2743 ZoneIdMatchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
   2744     if (U_FAILURE(status)) {
   2745         return FALSE;
   2746     }
   2747     if (node->hasValues()) {
   2748         const UChar* id = (const UChar*)node->getValue(0);
   2749         if (id != NULL) {
   2750             if (fLen < matchLength) {
   2751                 fID = id;
   2752                 fLen = matchLength;
   2753             }
   2754         }
   2755     }
   2756     return TRUE;
   2757 }
   2758 
   2759 const UChar*
   2760 ZoneIdMatchHandler::getID() {
   2761     return fID;
   2762 }
   2763 
   2764 int32_t
   2765 ZoneIdMatchHandler::getMatchLen() {
   2766     return fLen;
   2767 }
   2768 
   2769 
   2770 static void U_CALLCONV initZoneIdTrie(UErrorCode &status) {
   2771     U_ASSERT(gZoneIdTrie == NULL);
   2772     ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEFORMAT, tzfmt_cleanup);
   2773     gZoneIdTrie = new TextTrieMap(TRUE, NULL);    // No deleter, because values are pooled by ZoneMeta
   2774     if (gZoneIdTrie == NULL) {
   2775         status = U_MEMORY_ALLOCATION_ERROR;
   2776         return;
   2777     }
   2778     StringEnumeration *tzenum = TimeZone::createEnumeration();
   2779     const UnicodeString *id;
   2780     while ((id = tzenum->snext(status)) != NULL) {
   2781         const UChar* uid = ZoneMeta::findTimeZoneID(*id);
   2782         if (uid) {
   2783             gZoneIdTrie->put(uid, const_cast<UChar *>(uid), status);
   2784         }
   2785     }
   2786     delete tzenum;
   2787 }
   2788 
   2789 
   2790 UnicodeString&
   2791 TimeZoneFormat::parseZoneID(const UnicodeString& text, ParsePosition& pos, UnicodeString& tzID) const {
   2792     UErrorCode status = U_ZERO_ERROR;
   2793     umtx_initOnce(gZoneIdTrieInitOnce, &initZoneIdTrie, status);
   2794 
   2795     int32_t start = pos.getIndex();
   2796     int32_t len = 0;
   2797     tzID.setToBogus();
   2798 
   2799     if (U_SUCCESS(status)) {
   2800         LocalPointer<ZoneIdMatchHandler> handler(new ZoneIdMatchHandler());
   2801         gZoneIdTrie->search(text, start, handler.getAlias(), status);
   2802         len = handler->getMatchLen();
   2803         if (len > 0) {
   2804             tzID.setTo(handler->getID(), -1);
   2805         }
   2806     }
   2807 
   2808     if (len > 0) {
   2809         pos.setIndex(start + len);
   2810     } else {
   2811         pos.setErrorIndex(start);
   2812     }
   2813 
   2814     return tzID;
   2815 }
   2816 
   2817 static void U_CALLCONV initShortZoneIdTrie(UErrorCode &status) {
   2818     U_ASSERT(gShortZoneIdTrie == NULL);
   2819     ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEFORMAT, tzfmt_cleanup);
   2820     StringEnumeration *tzenum = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
   2821     if (U_SUCCESS(status)) {
   2822         gShortZoneIdTrie = new TextTrieMap(TRUE, NULL);    // No deleter, because values are pooled by ZoneMeta
   2823         if (gShortZoneIdTrie == NULL) {
   2824             status = U_MEMORY_ALLOCATION_ERROR;
   2825         } else {
   2826             const UnicodeString *id;
   2827             while ((id = tzenum->snext(status)) != NULL) {
   2828                 const UChar* uID = ZoneMeta::findTimeZoneID(*id);
   2829                 const UChar* shortID = ZoneMeta::getShortID(*id);
   2830                 if (shortID && uID) {
   2831                     gShortZoneIdTrie->put(shortID, const_cast<UChar *>(uID), status);
   2832                 }
   2833             }
   2834         }
   2835     }
   2836     delete tzenum;
   2837 }
   2838 
   2839 
   2840 UnicodeString&
   2841 TimeZoneFormat::parseShortZoneID(const UnicodeString& text, ParsePosition& pos, UnicodeString& tzID) const {
   2842     UErrorCode status = U_ZERO_ERROR;
   2843     umtx_initOnce(gShortZoneIdTrieInitOnce, &initShortZoneIdTrie, status);
   2844 
   2845     int32_t start = pos.getIndex();
   2846     int32_t len = 0;
   2847     tzID.setToBogus();
   2848 
   2849     if (U_SUCCESS(status)) {
   2850         LocalPointer<ZoneIdMatchHandler> handler(new ZoneIdMatchHandler());
   2851         gShortZoneIdTrie->search(text, start, handler.getAlias(), status);
   2852         len = handler->getMatchLen();
   2853         if (len > 0) {
   2854             tzID.setTo(handler->getID(), -1);
   2855         }
   2856     }
   2857 
   2858     if (len > 0) {
   2859         pos.setIndex(start + len);
   2860     } else {
   2861         pos.setErrorIndex(start);
   2862     }
   2863 
   2864     return tzID;
   2865 }
   2866 
   2867 
   2868 UnicodeString&
   2869 TimeZoneFormat::parseExemplarLocation(const UnicodeString& text, ParsePosition& pos, UnicodeString& tzID) const {
   2870     int32_t startIdx = pos.getIndex();
   2871     int32_t parsedPos = -1;
   2872     tzID.setToBogus();
   2873 
   2874     UErrorCode status = U_ZERO_ERROR;
   2875     LocalPointer<TimeZoneNames::MatchInfoCollection> exemplarMatches(fTimeZoneNames->find(text, startIdx, UTZNM_EXEMPLAR_LOCATION, status));
   2876     if (U_FAILURE(status)) {
   2877         pos.setErrorIndex(startIdx);
   2878         return tzID;
   2879     }
   2880     int32_t matchIdx = -1;
   2881     if (!exemplarMatches.isNull()) {
   2882         for (int32_t i = 0; i < exemplarMatches->size(); i++) {
   2883             if (startIdx + exemplarMatches->getMatchLengthAt(i) > parsedPos) {
   2884                 matchIdx = i;
   2885                 parsedPos = startIdx + exemplarMatches->getMatchLengthAt(i);
   2886             }
   2887         }
   2888         if (parsedPos > 0) {
   2889             pos.setIndex(parsedPos);
   2890             getTimeZoneID(exemplarMatches.getAlias(), matchIdx, tzID);
   2891         }
   2892     }
   2893 
   2894     if (tzID.length() == 0) {
   2895         pos.setErrorIndex(startIdx);
   2896     }
   2897 
   2898     return tzID;
   2899 }
   2900 
   2901 U_NAMESPACE_END
   2902 
   2903 #endif
   2904