LCOV - code coverage report
Current view: top level - intl/icu/source/i18n - tzfmt.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 1532 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 89 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13