intl/icu/source/i18n/tzfmt.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/intl/icu/source/i18n/tzfmt.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2777 @@
     1.4 +/*
     1.5 +*******************************************************************************
     1.6 +* Copyright (C) 2011-2013, International Business Machines Corporation and
     1.7 +* others. All Rights Reserved.
     1.8 +*******************************************************************************
     1.9 +*/
    1.10 +
    1.11 +#include "unicode/utypes.h"
    1.12 +
    1.13 +#if !UCONFIG_NO_FORMATTING
    1.14 +
    1.15 +#include "unicode/calendar.h"
    1.16 +#include "unicode/tzfmt.h"
    1.17 +#include "unicode/numsys.h"
    1.18 +#include "unicode/uchar.h"
    1.19 +#include "unicode/udat.h"
    1.20 +#include "tzgnames.h"
    1.21 +#include "cmemory.h"
    1.22 +#include "cstring.h"
    1.23 +#include "putilimp.h"
    1.24 +#include "uassert.h"
    1.25 +#include "ucln_in.h"
    1.26 +#include "umutex.h"
    1.27 +#include "uresimp.h"
    1.28 +#include "ureslocs.h"
    1.29 +#include "uvector.h"
    1.30 +#include "zonemeta.h"
    1.31 +#include "tznames_impl.h"   // TextTrieMap
    1.32 +
    1.33 +U_NAMESPACE_BEGIN
    1.34 +
    1.35 +// Bit flags used by the parse method.
    1.36 +// The order must match UTimeZoneFormatStyle enum.
    1.37 +#define ISO_Z_STYLE_FLAG 0x0080
    1.38 +#define ISO_LOCAL_STYLE_FLAG 0x0100
    1.39 +static const int16_t STYLE_PARSE_FLAGS[] = {
    1.40 +    0x0001, // UTZFMT_STYLE_GENERIC_LOCATION,
    1.41 +    0x0002, // UTZFMT_STYLE_GENERIC_LONG,
    1.42 +    0x0004, // UTZFMT_STYLE_GENERIC_SHORT,
    1.43 +    0x0008, // UTZFMT_STYLE_SPECIFIC_LONG,
    1.44 +    0x0010, // UTZFMT_STYLE_SPECIFIC_SHORT,
    1.45 +    0x0020, // UTZFMT_STYLE_LOCALIZED_GMT,
    1.46 +    0x0040, // UTZFMT_STYLE_LOCALIZED_GMT_SHORT,
    1.47 +    ISO_Z_STYLE_FLAG,       // UTZFMT_STYLE_ISO_BASIC_SHORT,
    1.48 +    ISO_LOCAL_STYLE_FLAG,   // UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT,
    1.49 +    ISO_Z_STYLE_FLAG,       // UTZFMT_STYLE_ISO_BASIC_FIXED,
    1.50 +    ISO_LOCAL_STYLE_FLAG,   // UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED,
    1.51 +    ISO_Z_STYLE_FLAG,       // UTZFMT_STYLE_ISO_BASIC_FULL,
    1.52 +    ISO_LOCAL_STYLE_FLAG,   // UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL,
    1.53 +    ISO_Z_STYLE_FLAG,       // UTZFMT_STYLE_ISO_EXTENDED_FIXED,
    1.54 +    ISO_LOCAL_STYLE_FLAG,   // UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED,
    1.55 +    ISO_Z_STYLE_FLAG,       // UTZFMT_STYLE_ISO_EXTENDED_FULL,
    1.56 +    ISO_LOCAL_STYLE_FLAG,   // UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL,
    1.57 +    0x0200, // UTZFMT_STYLE_ZONE_ID,
    1.58 +    0x0400, // UTZFMT_STYLE_ZONE_ID_SHORT,
    1.59 +    0x0800  // UTZFMT_STYLE_EXEMPLAR_LOCATION
    1.60 +};
    1.61 +
    1.62 +static const char gZoneStringsTag[] = "zoneStrings";
    1.63 +static const char gGmtFormatTag[]= "gmtFormat";
    1.64 +static const char gGmtZeroFormatTag[] = "gmtZeroFormat";
    1.65 +static const char gHourFormatTag[]= "hourFormat";
    1.66 +
    1.67 +static const UChar TZID_GMT[] = {0x0045, 0x0074, 0x0063, 0x002F, 0x0047, 0x004D, 0x0054, 0};    // Etc/GMT
    1.68 +static const UChar UNKNOWN_ZONE_ID[] = {
    1.69 +    0x0045, 0x0074, 0x0063, 0x002F, 0x0055, 0x006E, 0x006B, 0x006E, 0x006F, 0x0077, 0x006E, 0}; // Etc/Unknown
    1.70 +static const UChar UNKNOWN_SHORT_ZONE_ID[] = {0x0075, 0x006E, 0x006B, 0};   // unk
    1.71 +static const UChar UNKNOWN_LOCATION[] = {0x0055, 0x006E, 0x006B, 0x006E, 0x006F, 0x0077, 0x006E, 0};    // Unknown
    1.72 +
    1.73 +static const UChar DEFAULT_GMT_PATTERN[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0}; // GMT{0}
    1.74 +//static const UChar DEFAULT_GMT_ZERO[] = {0x0047, 0x004D, 0x0054, 0}; // GMT
    1.75 +static const UChar DEFAULT_GMT_POSITIVE_HM[] = {0x002B, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // +H:mm
    1.76 +static const UChar DEFAULT_GMT_POSITIVE_HMS[] = {0x002B, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // +H:mm:ss
    1.77 +static const UChar DEFAULT_GMT_NEGATIVE_HM[] = {0x002D, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // -H:mm
    1.78 +static const UChar DEFAULT_GMT_NEGATIVE_HMS[] = {0x002D, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // -H:mm:ss
    1.79 +static const UChar DEFAULT_GMT_POSITIVE_H[] = {0x002B, 0x0048, 0}; // +H
    1.80 +static const UChar DEFAULT_GMT_NEGATIVE_H[] = {0x002D, 0x0048, 0}; // -H
    1.81 +
    1.82 +static const UChar32 DEFAULT_GMT_DIGITS[] = {
    1.83 +    0x0030, 0x0031, 0x0032, 0x0033, 0x0034,
    1.84 +    0x0035, 0x0036, 0x0037, 0x0038, 0x0039
    1.85 +};
    1.86 +
    1.87 +static const UChar DEFAULT_GMT_OFFSET_SEP = 0x003A; // ':'
    1.88 +
    1.89 +static const UChar ARG0[] = {0x007B, 0x0030, 0x007D};   // "{0}"
    1.90 +static const int32_t ARG0_LEN = 3;
    1.91 +
    1.92 +static const UChar DEFAULT_GMT_OFFSET_MINUTE_PATTERN[] = {0x006D, 0x006D, 0};   // "mm"
    1.93 +static const UChar DEFAULT_GMT_OFFSET_SECOND_PATTERN[] = {0x0073, 0x0073, 0};   // "ss"
    1.94 +
    1.95 +static const UChar ALT_GMT_STRINGS[][4] = {
    1.96 +    {0x0047, 0x004D, 0x0054, 0},    // GMT
    1.97 +    {0x0055, 0x0054, 0x0043, 0},    // UTC
    1.98 +    {0x0055, 0x0054, 0, 0},         // UT
    1.99 +    {0, 0, 0, 0}
   1.100 +};
   1.101 +
   1.102 +// Order of GMT offset pattern parsing, *_HMS must be evaluated first
   1.103 +// because *_HM is most likely a substring of *_HMS 
   1.104 +static const int32_t PARSE_GMT_OFFSET_TYPES[] = {
   1.105 +    UTZFMT_PAT_POSITIVE_HMS,
   1.106 +    UTZFMT_PAT_NEGATIVE_HMS,
   1.107 +    UTZFMT_PAT_POSITIVE_HM,
   1.108 +    UTZFMT_PAT_NEGATIVE_HM,
   1.109 +    UTZFMT_PAT_POSITIVE_H,
   1.110 +    UTZFMT_PAT_NEGATIVE_H,
   1.111 +    -1
   1.112 +};
   1.113 +
   1.114 +static const UChar SINGLEQUOTE  = 0x0027;
   1.115 +static const UChar PLUS         = 0x002B;
   1.116 +static const UChar MINUS        = 0x002D;
   1.117 +static const UChar ISO8601_UTC  = 0x005A;   // 'Z'
   1.118 +static const UChar ISO8601_SEP  = 0x003A;   // ':'
   1.119 +
   1.120 +static const int32_t MILLIS_PER_HOUR = 60 * 60 * 1000;
   1.121 +static const int32_t MILLIS_PER_MINUTE = 60 * 1000;
   1.122 +static const int32_t MILLIS_PER_SECOND = 1000;
   1.123 +
   1.124 +// Maximum offset (exclusive) in millisecond supported by offset formats
   1.125 +static int32_t MAX_OFFSET = 24 * MILLIS_PER_HOUR;
   1.126 +
   1.127 +// Maximum values for GMT offset fields
   1.128 +static const int32_t MAX_OFFSET_HOUR = 23;
   1.129 +static const int32_t MAX_OFFSET_MINUTE = 59;
   1.130 +static const int32_t MAX_OFFSET_SECOND = 59;
   1.131 +
   1.132 +static const int32_t UNKNOWN_OFFSET = 0x7FFFFFFF;
   1.133 +
   1.134 +static const int32_t ALL_SIMPLE_NAME_TYPES = UTZNM_LONG_STANDARD | UTZNM_LONG_DAYLIGHT | UTZNM_SHORT_STANDARD | UTZNM_SHORT_DAYLIGHT | UTZNM_EXEMPLAR_LOCATION;
   1.135 +static const int32_t ALL_GENERIC_NAME_TYPES = UTZGNM_LOCATION | UTZGNM_LONG | UTZGNM_SHORT;
   1.136 +
   1.137 +#define DIGIT_VAL(c) (0x0030 <= (c) && (c) <= 0x0039 ? (c) - 0x0030 : -1)
   1.138 +#define MAX_OFFSET_DIGITS 6
   1.139 +
   1.140 +// Time Zone ID/Short ID trie
   1.141 +static TextTrieMap *gZoneIdTrie = NULL;
   1.142 +static icu::UInitOnce gZoneIdTrieInitOnce = U_INITONCE_INITIALIZER;
   1.143 +
   1.144 +static TextTrieMap *gShortZoneIdTrie = NULL;
   1.145 +static icu::UInitOnce gShortZoneIdTrieInitOnce = U_INITONCE_INITIALIZER;
   1.146 +
   1.147 +static UMutex gLock = U_MUTEX_INITIALIZER;
   1.148 +
   1.149 +U_CDECL_BEGIN
   1.150 +/**
   1.151 + * Cleanup callback func
   1.152 + */
   1.153 +static UBool U_CALLCONV tzfmt_cleanup(void)
   1.154 +{
   1.155 +    if (gZoneIdTrie != NULL) {
   1.156 +        delete gZoneIdTrie;
   1.157 +    }
   1.158 +    gZoneIdTrie = NULL;
   1.159 +    gZoneIdTrieInitOnce.reset();
   1.160 +
   1.161 +    if (gShortZoneIdTrie != NULL) {
   1.162 +        delete gShortZoneIdTrie;
   1.163 +    }
   1.164 +    gShortZoneIdTrie = NULL;
   1.165 +    gShortZoneIdTrieInitOnce.reset();
   1.166 +
   1.167 +    return TRUE;
   1.168 +}
   1.169 +U_CDECL_END
   1.170 +
   1.171 +// ------------------------------------------------------------------
   1.172 +// GMTOffsetField
   1.173 +//
   1.174 +// This class represents a localized GMT offset pattern
   1.175 +// item and used by TimeZoneFormat
   1.176 +// ------------------------------------------------------------------
   1.177 +class GMTOffsetField : public UMemory {
   1.178 +public:
   1.179 +    enum FieldType {
   1.180 +        TEXT = 0,
   1.181 +        HOUR = 1,
   1.182 +        MINUTE = 2,
   1.183 +        SECOND = 4
   1.184 +    };
   1.185 +
   1.186 +    virtual ~GMTOffsetField();
   1.187 +
   1.188 +    static GMTOffsetField* createText(const UnicodeString& text, UErrorCode& status);
   1.189 +    static GMTOffsetField* createTimeField(FieldType type, uint8_t width, UErrorCode& status);
   1.190 +    static UBool isValid(FieldType type, int32_t width);
   1.191 +    static FieldType getTypeByLetter(UChar ch);
   1.192 +
   1.193 +    FieldType getType() const;
   1.194 +    uint8_t getWidth() const;
   1.195 +    const UChar* getPatternText(void) const;
   1.196 +
   1.197 +private:
   1.198 +    UChar* fText;
   1.199 +    FieldType fType;
   1.200 +    uint8_t fWidth;
   1.201 +
   1.202 +    GMTOffsetField();
   1.203 +};
   1.204 +
   1.205 +GMTOffsetField::GMTOffsetField()
   1.206 +: fText(NULL), fType(TEXT), fWidth(0) {
   1.207 +}
   1.208 +
   1.209 +GMTOffsetField::~GMTOffsetField() {
   1.210 +    if (fText) {
   1.211 +        uprv_free(fText);
   1.212 +    }
   1.213 +}
   1.214 +
   1.215 +GMTOffsetField*
   1.216 +GMTOffsetField::createText(const UnicodeString& text, UErrorCode& status) {
   1.217 +    if (U_FAILURE(status)) {
   1.218 +        return NULL;
   1.219 +    }
   1.220 +    GMTOffsetField* result = new GMTOffsetField();
   1.221 +    if (result == NULL) {
   1.222 +        status = U_MEMORY_ALLOCATION_ERROR;
   1.223 +        return NULL;
   1.224 +    }
   1.225 +
   1.226 +    int32_t len = text.length();
   1.227 +    result->fText = (UChar*)uprv_malloc((len + 1) * sizeof(UChar));
   1.228 +    if (result->fText == NULL) {
   1.229 +        status = U_MEMORY_ALLOCATION_ERROR;
   1.230 +        delete result;
   1.231 +        return NULL;
   1.232 +    }
   1.233 +    u_strncpy(result->fText, text.getBuffer(), len);
   1.234 +    result->fText[len] = 0;
   1.235 +    result->fType = TEXT;
   1.236 +
   1.237 +    return result;
   1.238 +}
   1.239 +
   1.240 +GMTOffsetField*
   1.241 +GMTOffsetField::createTimeField(FieldType type, uint8_t width, UErrorCode& status) {
   1.242 +    U_ASSERT(type != TEXT);
   1.243 +    if (U_FAILURE(status)) {
   1.244 +        return NULL;
   1.245 +    }
   1.246 +    GMTOffsetField* result = new GMTOffsetField();
   1.247 +    if (result == NULL) {
   1.248 +        status = U_MEMORY_ALLOCATION_ERROR;
   1.249 +        return NULL;
   1.250 +    }
   1.251 +
   1.252 +    result->fType = type;
   1.253 +    result->fWidth = width;
   1.254 +
   1.255 +    return result;
   1.256 +}
   1.257 +
   1.258 +UBool
   1.259 +GMTOffsetField::isValid(FieldType type, int32_t width) {
   1.260 +    switch (type) {
   1.261 +    case HOUR:
   1.262 +        return (width == 1 || width == 2);
   1.263 +    case MINUTE:
   1.264 +    case SECOND:
   1.265 +        return (width == 2);
   1.266 +    default:
   1.267 +        U_ASSERT(FALSE);
   1.268 +    }
   1.269 +    return (width > 0);
   1.270 +}
   1.271 +
   1.272 +GMTOffsetField::FieldType
   1.273 +GMTOffsetField::getTypeByLetter(UChar ch) {
   1.274 +    if (ch == 0x0048 /* H */) {
   1.275 +        return HOUR;
   1.276 +    } else if (ch == 0x006D /* m */) {
   1.277 +        return MINUTE;
   1.278 +    } else if (ch == 0x0073 /* s */) {
   1.279 +        return SECOND;
   1.280 +    }
   1.281 +    return TEXT;
   1.282 +}
   1.283 +
   1.284 +inline GMTOffsetField::FieldType
   1.285 +GMTOffsetField::getType() const {
   1.286 +     return fType;
   1.287 + }
   1.288 +
   1.289 +inline uint8_t
   1.290 +GMTOffsetField::getWidth() const {
   1.291 +    return fWidth;
   1.292 +}
   1.293 + 
   1.294 +inline const UChar*
   1.295 +GMTOffsetField::getPatternText(void) const {
   1.296 +    return fText;
   1.297 +}
   1.298 +
   1.299 +
   1.300 +U_CDECL_BEGIN
   1.301 +static void U_CALLCONV
   1.302 +deleteGMTOffsetField(void *obj) {
   1.303 +    delete static_cast<GMTOffsetField *>(obj);
   1.304 +}
   1.305 +U_CDECL_END
   1.306 +
   1.307 +
   1.308 +// ------------------------------------------------------------------
   1.309 +// TimeZoneFormat
   1.310 +// ------------------------------------------------------------------
   1.311 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeZoneFormat)
   1.312 +
   1.313 +TimeZoneFormat::TimeZoneFormat(const Locale& locale, UErrorCode& status) 
   1.314 +: fLocale(locale), fTimeZoneNames(NULL), fTimeZoneGenericNames(NULL), fDefParseOptionFlags(0) {
   1.315 +
   1.316 +    for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
   1.317 +        fGMTOffsetPatternItems[i] = NULL;
   1.318 +    }
   1.319 +
   1.320 +    const char* region = fLocale.getCountry();
   1.321 +    int32_t regionLen = uprv_strlen(region);
   1.322 +    if (regionLen == 0) {
   1.323 +        char loc[ULOC_FULLNAME_CAPACITY];
   1.324 +        uloc_addLikelySubtags(fLocale.getName(), loc, sizeof(loc), &status);
   1.325 +
   1.326 +        regionLen = uloc_getCountry(loc, fTargetRegion, sizeof(fTargetRegion), &status);
   1.327 +        if (U_SUCCESS(status)) {
   1.328 +            fTargetRegion[regionLen] = 0;
   1.329 +        } else {
   1.330 +            return;
   1.331 +        }
   1.332 +    } else if (regionLen < (int32_t)sizeof(fTargetRegion)) {
   1.333 +        uprv_strcpy(fTargetRegion, region);
   1.334 +    } else {
   1.335 +        fTargetRegion[0] = 0;
   1.336 +    }
   1.337 +
   1.338 +    fTimeZoneNames = TimeZoneNames::createInstance(locale, status);
   1.339 +    // fTimeZoneGenericNames is lazily instantiated
   1.340 +    if (U_FAILURE(status)) {
   1.341 +        return;
   1.342 +    }
   1.343 +
   1.344 +    const UChar* gmtPattern = NULL;
   1.345 +    const UChar* hourFormats = NULL;
   1.346 +
   1.347 +    UResourceBundle *zoneBundle = ures_open(U_ICUDATA_ZONE, locale.getName(), &status);
   1.348 +    UResourceBundle *zoneStringsArray = ures_getByKeyWithFallback(zoneBundle, gZoneStringsTag, NULL, &status);
   1.349 +    if (U_SUCCESS(status)) {
   1.350 +        const UChar* resStr;
   1.351 +        int32_t len;
   1.352 +        resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gGmtFormatTag, &len, &status);
   1.353 +        if (len > 0) {
   1.354 +            gmtPattern = resStr;
   1.355 +        }
   1.356 +        resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gGmtZeroFormatTag, &len, &status);
   1.357 +        if (len > 0) {
   1.358 +            fGMTZeroFormat.setTo(TRUE, resStr, len);
   1.359 +        }
   1.360 +        resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gHourFormatTag, &len, &status);
   1.361 +        if (len > 0) {
   1.362 +            hourFormats = resStr;
   1.363 +        }
   1.364 +        ures_close(zoneStringsArray);
   1.365 +        ures_close(zoneBundle);
   1.366 +    }
   1.367 +
   1.368 +    if (gmtPattern == NULL) {
   1.369 +        gmtPattern = DEFAULT_GMT_PATTERN;
   1.370 +    }
   1.371 +    initGMTPattern(UnicodeString(gmtPattern, -1), status);
   1.372 +
   1.373 +    UBool useDefaultOffsetPatterns = TRUE;
   1.374 +    if (hourFormats) {
   1.375 +        UChar *sep = u_strchr(hourFormats, (UChar)0x003B /* ';' */);
   1.376 +        if (sep != NULL) {
   1.377 +            UErrorCode tmpStatus = U_ZERO_ERROR;
   1.378 +            fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM].setTo(FALSE, hourFormats, (int32_t)(sep - hourFormats));
   1.379 +            fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM].setTo(TRUE, sep + 1, -1);
   1.380 +            expandOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HMS], tmpStatus);
   1.381 +            expandOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HMS], tmpStatus);
   1.382 +            truncateOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_H], tmpStatus);
   1.383 +            truncateOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_H], tmpStatus);
   1.384 +            if (U_SUCCESS(tmpStatus)) {
   1.385 +                useDefaultOffsetPatterns = FALSE;
   1.386 +            }
   1.387 +        }
   1.388 +    }
   1.389 +    if (useDefaultOffsetPatterns) {
   1.390 +        fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_H].setTo(TRUE, DEFAULT_GMT_POSITIVE_H, -1);
   1.391 +        fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM].setTo(TRUE, DEFAULT_GMT_POSITIVE_HM, -1);
   1.392 +        fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HMS].setTo(TRUE, DEFAULT_GMT_POSITIVE_HMS, -1);
   1.393 +        fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_H].setTo(TRUE, DEFAULT_GMT_NEGATIVE_H, -1);
   1.394 +        fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM].setTo(TRUE, DEFAULT_GMT_NEGATIVE_HM, -1);
   1.395 +        fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HMS].setTo(TRUE, DEFAULT_GMT_NEGATIVE_HMS, -1);
   1.396 +    }
   1.397 +    initGMTOffsetPatterns(status);
   1.398 +
   1.399 +    NumberingSystem* ns = NumberingSystem::createInstance(locale, status);
   1.400 +    UBool useDefDigits = TRUE;
   1.401 +    if (ns && !ns->isAlgorithmic()) {
   1.402 +        UnicodeString digits = ns->getDescription();
   1.403 +        useDefDigits = !toCodePoints(digits, fGMTOffsetDigits, 10);
   1.404 +    }
   1.405 +    if (useDefDigits) {
   1.406 +        uprv_memcpy(fGMTOffsetDigits, DEFAULT_GMT_DIGITS, sizeof(UChar32) * 10);
   1.407 +    }
   1.408 +    delete ns;
   1.409 +}
   1.410 +
   1.411 +TimeZoneFormat::TimeZoneFormat(const TimeZoneFormat& other)
   1.412 +: Format(other), fTimeZoneNames(NULL), fTimeZoneGenericNames(NULL) {
   1.413 +
   1.414 +    for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
   1.415 +        fGMTOffsetPatternItems[i] = NULL;
   1.416 +    }
   1.417 +    *this = other;
   1.418 +}
   1.419 +
   1.420 +
   1.421 +TimeZoneFormat::~TimeZoneFormat() {
   1.422 +    delete fTimeZoneNames;
   1.423 +    delete fTimeZoneGenericNames;
   1.424 +    for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
   1.425 +        delete fGMTOffsetPatternItems[i];
   1.426 +    }
   1.427 +}
   1.428 +
   1.429 +TimeZoneFormat&
   1.430 +TimeZoneFormat::operator=(const TimeZoneFormat& other) {
   1.431 +    if (this == &other) {
   1.432 +        return *this;
   1.433 +    }
   1.434 +
   1.435 +    delete fTimeZoneNames;
   1.436 +    delete fTimeZoneGenericNames;
   1.437 +    fTimeZoneGenericNames = NULL;
   1.438 +
   1.439 +    fLocale = other.fLocale;
   1.440 +    uprv_memcpy(fTargetRegion, other.fTargetRegion, sizeof(fTargetRegion));
   1.441 +
   1.442 +    fTimeZoneNames = other.fTimeZoneNames->clone();
   1.443 +    if (other.fTimeZoneGenericNames) {
   1.444 +        // TODO: this test has dubious thread safety.
   1.445 +        fTimeZoneGenericNames = other.fTimeZoneGenericNames->clone();
   1.446 +    }
   1.447 +
   1.448 +    fGMTPattern = other.fGMTPattern;
   1.449 +    fGMTPatternPrefix = other.fGMTPatternPrefix;
   1.450 +    fGMTPatternSuffix = other.fGMTPatternSuffix;
   1.451 +
   1.452 +    UErrorCode status = U_ZERO_ERROR;
   1.453 +    for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
   1.454 +        fGMTOffsetPatterns[i] = other.fGMTOffsetPatterns[i];
   1.455 +        delete fGMTOffsetPatternItems[i];
   1.456 +    }
   1.457 +    initGMTOffsetPatterns(status);
   1.458 +    U_ASSERT(U_SUCCESS(status));
   1.459 +
   1.460 +    fGMTZeroFormat = other.fGMTZeroFormat;
   1.461 +
   1.462 +    uprv_memcpy(fGMTOffsetDigits, other.fGMTOffsetDigits, sizeof(fGMTOffsetDigits));
   1.463 +
   1.464 +    fDefParseOptionFlags = other.fDefParseOptionFlags;
   1.465 +
   1.466 +    return *this;
   1.467 +}
   1.468 +
   1.469 +
   1.470 +UBool
   1.471 +TimeZoneFormat::operator==(const Format& other) const {
   1.472 +    TimeZoneFormat* tzfmt = (TimeZoneFormat*)&other;
   1.473 +
   1.474 +    UBool isEqual =
   1.475 +            fLocale == tzfmt->fLocale
   1.476 +            && fGMTPattern == tzfmt->fGMTPattern
   1.477 +            && fGMTZeroFormat == tzfmt->fGMTZeroFormat
   1.478 +            && *fTimeZoneNames == *tzfmt->fTimeZoneNames;
   1.479 +
   1.480 +    for (int32_t i = 0; i < UTZFMT_PAT_COUNT && isEqual; i++) {
   1.481 +        isEqual = fGMTOffsetPatterns[i] == tzfmt->fGMTOffsetPatterns[i];
   1.482 +    }
   1.483 +    for (int32_t i = 0; i < 10 && isEqual; i++) {
   1.484 +        isEqual = fGMTOffsetDigits[i] == tzfmt->fGMTOffsetDigits[i];
   1.485 +    }
   1.486 +    // TODO
   1.487 +    // Check fTimeZoneGenericNames. For now,
   1.488 +    // if fTimeZoneNames is same, fTimeZoneGenericNames should
   1.489 +    // be also equivalent.
   1.490 +    return isEqual;
   1.491 +}
   1.492 +
   1.493 +Format*
   1.494 +TimeZoneFormat::clone() const {
   1.495 +    return new TimeZoneFormat(*this);
   1.496 +}
   1.497 +
   1.498 +TimeZoneFormat* U_EXPORT2
   1.499 +TimeZoneFormat::createInstance(const Locale& locale, UErrorCode& status) {
   1.500 +    TimeZoneFormat* tzfmt = new TimeZoneFormat(locale, status);
   1.501 +    if (U_SUCCESS(status)) {
   1.502 +        return tzfmt;
   1.503 +    }
   1.504 +    delete tzfmt;
   1.505 +    return NULL;
   1.506 +}
   1.507 +
   1.508 +// ------------------------------------------------------------------
   1.509 +// Setter and Getter
   1.510 +
   1.511 +const TimeZoneNames*
   1.512 +TimeZoneFormat::getTimeZoneNames() const {
   1.513 +    return (const TimeZoneNames*)fTimeZoneNames;
   1.514 +}
   1.515 +
   1.516 +void
   1.517 +TimeZoneFormat::adoptTimeZoneNames(TimeZoneNames *tznames) {
   1.518 +    delete fTimeZoneNames;
   1.519 +    fTimeZoneNames = tznames;
   1.520 +
   1.521 +    // TODO - We should also update fTimeZoneGenericNames
   1.522 +}
   1.523 +
   1.524 +void
   1.525 +TimeZoneFormat::setTimeZoneNames(const TimeZoneNames &tznames) {
   1.526 +    delete fTimeZoneNames;
   1.527 +    fTimeZoneNames = tznames.clone();
   1.528 +
   1.529 +    // TODO - We should also update fTimeZoneGenericNames
   1.530 +}
   1.531 +
   1.532 +void
   1.533 +TimeZoneFormat::setDefaultParseOptions(uint32_t flags) {
   1.534 +    fDefParseOptionFlags = flags;
   1.535 +}
   1.536 +
   1.537 +uint32_t
   1.538 +TimeZoneFormat::getDefaultParseOptions(void) const {
   1.539 +    return fDefParseOptionFlags;
   1.540 +}
   1.541 +
   1.542 +
   1.543 +UnicodeString& 
   1.544 +TimeZoneFormat::getGMTPattern(UnicodeString& pattern) const {
   1.545 +    return pattern.setTo(fGMTPattern);
   1.546 +}
   1.547 +
   1.548 +void
   1.549 +TimeZoneFormat::setGMTPattern(const UnicodeString& pattern, UErrorCode& status) {
   1.550 +    initGMTPattern(pattern, status);
   1.551 +}
   1.552 +
   1.553 +UnicodeString&
   1.554 +TimeZoneFormat::getGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type, UnicodeString& pattern) const {
   1.555 +    return pattern.setTo(fGMTOffsetPatterns[type]);
   1.556 +}
   1.557 +
   1.558 +void
   1.559 +TimeZoneFormat::setGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type, const UnicodeString& pattern, UErrorCode& status) {
   1.560 +    if (U_FAILURE(status)) {
   1.561 +        return;
   1.562 +    }
   1.563 +    if (pattern == fGMTOffsetPatterns[type]) {
   1.564 +        // No need to reset
   1.565 +        return;
   1.566 +    }
   1.567 +
   1.568 +    OffsetFields required = FIELDS_HM;
   1.569 +    switch (type) {
   1.570 +    case UTZFMT_PAT_POSITIVE_H:
   1.571 +    case UTZFMT_PAT_NEGATIVE_H:
   1.572 +        required = FIELDS_H;
   1.573 +        break;
   1.574 +    case UTZFMT_PAT_POSITIVE_HM:
   1.575 +    case UTZFMT_PAT_NEGATIVE_HM:
   1.576 +        required = FIELDS_HM;
   1.577 +        break;
   1.578 +    case UTZFMT_PAT_POSITIVE_HMS:
   1.579 +    case UTZFMT_PAT_NEGATIVE_HMS:
   1.580 +        required = FIELDS_HMS;
   1.581 +        break;
   1.582 +    default:
   1.583 +        U_ASSERT(FALSE);
   1.584 +        break;
   1.585 +    }
   1.586 +
   1.587 +    UVector* patternItems = parseOffsetPattern(pattern, required, status);
   1.588 +    if (patternItems == NULL) {
   1.589 +        return;
   1.590 +    }
   1.591 +
   1.592 +    fGMTOffsetPatterns[type].setTo(pattern);
   1.593 +    delete fGMTOffsetPatternItems[type];
   1.594 +    fGMTOffsetPatternItems[type] = patternItems;
   1.595 +    checkAbuttingHoursAndMinutes();
   1.596 +}
   1.597 +
   1.598 +UnicodeString&
   1.599 +TimeZoneFormat::getGMTOffsetDigits(UnicodeString& digits) const {
   1.600 +    digits.remove();
   1.601 +    for (int32_t i = 0; i < 10; i++) {
   1.602 +        digits.append(fGMTOffsetDigits[i]);
   1.603 +    }
   1.604 +    return digits;
   1.605 +}
   1.606 +
   1.607 +void
   1.608 +TimeZoneFormat::setGMTOffsetDigits(const UnicodeString& digits, UErrorCode& status) {
   1.609 +    if (U_FAILURE(status)) {
   1.610 +        return;
   1.611 +    }
   1.612 +    UChar32 digitArray[10];
   1.613 +    if (!toCodePoints(digits, digitArray, 10)) {
   1.614 +        status = U_ILLEGAL_ARGUMENT_ERROR;
   1.615 +        return;
   1.616 +    }
   1.617 +    uprv_memcpy(fGMTOffsetDigits, digitArray, sizeof(UChar32)*10);
   1.618 +}
   1.619 +
   1.620 +UnicodeString&
   1.621 +TimeZoneFormat::getGMTZeroFormat(UnicodeString& gmtZeroFormat) const {
   1.622 +    return gmtZeroFormat.setTo(fGMTZeroFormat);
   1.623 +}
   1.624 +
   1.625 +void
   1.626 +TimeZoneFormat::setGMTZeroFormat(const UnicodeString& gmtZeroFormat, UErrorCode& status) {
   1.627 +    if (U_SUCCESS(status)) {
   1.628 +        if (gmtZeroFormat.isEmpty()) {
   1.629 +            status = U_ILLEGAL_ARGUMENT_ERROR;
   1.630 +        } else if (gmtZeroFormat != fGMTZeroFormat) {
   1.631 +            fGMTZeroFormat.setTo(gmtZeroFormat);
   1.632 +        }
   1.633 +    }
   1.634 +}
   1.635 +
   1.636 +// ------------------------------------------------------------------
   1.637 +// Format and Parse
   1.638 +
   1.639 +UnicodeString&
   1.640 +TimeZoneFormat::format(UTimeZoneFormatStyle style, const TimeZone& tz, UDate date,
   1.641 +        UnicodeString& name, UTimeZoneFormatTimeType* timeType /* = NULL */) const {
   1.642 +    if (timeType) {
   1.643 +        *timeType = UTZFMT_TIME_TYPE_UNKNOWN;
   1.644 +    }
   1.645 +
   1.646 +    UBool noOffsetFormatFallback = FALSE;
   1.647 +
   1.648 +    switch (style) {
   1.649 +    case UTZFMT_STYLE_GENERIC_LOCATION:
   1.650 +        formatGeneric(tz, UTZGNM_LOCATION, date, name);
   1.651 +        break;
   1.652 +    case UTZFMT_STYLE_GENERIC_LONG:
   1.653 +        formatGeneric(tz, UTZGNM_LONG, date, name);
   1.654 +        break;
   1.655 +    case UTZFMT_STYLE_GENERIC_SHORT:
   1.656 +        formatGeneric(tz, UTZGNM_SHORT, date, name);
   1.657 +        break;
   1.658 +    case UTZFMT_STYLE_SPECIFIC_LONG:
   1.659 +        formatSpecific(tz, UTZNM_LONG_STANDARD, UTZNM_LONG_DAYLIGHT, date, name, timeType);
   1.660 +        break;
   1.661 +    case UTZFMT_STYLE_SPECIFIC_SHORT:
   1.662 +        formatSpecific(tz, UTZNM_SHORT_STANDARD, UTZNM_SHORT_DAYLIGHT, date, name, timeType);
   1.663 +        break;
   1.664 +
   1.665 +    case UTZFMT_STYLE_ZONE_ID:
   1.666 +        tz.getID(name);
   1.667 +        noOffsetFormatFallback = TRUE;
   1.668 +        break;
   1.669 +    case UTZFMT_STYLE_ZONE_ID_SHORT:
   1.670 +        {
   1.671 +            const UChar* shortID = ZoneMeta::getShortID(tz);
   1.672 +            if (shortID == NULL) {
   1.673 +                shortID = UNKNOWN_SHORT_ZONE_ID;
   1.674 +            }
   1.675 +            name.setTo(shortID, -1);
   1.676 +        }
   1.677 +        noOffsetFormatFallback = TRUE;
   1.678 +        break;
   1.679 +
   1.680 +    case UTZFMT_STYLE_EXEMPLAR_LOCATION:
   1.681 +        formatExemplarLocation(tz, name);
   1.682 +        noOffsetFormatFallback = TRUE;
   1.683 +        break;
   1.684 +
   1.685 +    default:
   1.686 +        // will be handled below
   1.687 +        break;
   1.688 +    }
   1.689 +
   1.690 +    if (name.isEmpty() && !noOffsetFormatFallback) {
   1.691 +        UErrorCode status = U_ZERO_ERROR;
   1.692 +        int32_t rawOffset, dstOffset;
   1.693 +        tz.getOffset(date, FALSE, rawOffset, dstOffset, status);
   1.694 +        int32_t offset = rawOffset + dstOffset;
   1.695 +        if (U_SUCCESS(status)) {
   1.696 +            switch (style) {
   1.697 +            case UTZFMT_STYLE_GENERIC_LOCATION:
   1.698 +            case UTZFMT_STYLE_GENERIC_LONG:
   1.699 +            case UTZFMT_STYLE_SPECIFIC_LONG:
   1.700 +            case UTZFMT_STYLE_LOCALIZED_GMT:
   1.701 +                formatOffsetLocalizedGMT(offset, name, status);
   1.702 +                break;
   1.703 +
   1.704 +            case UTZFMT_STYLE_GENERIC_SHORT:
   1.705 +            case UTZFMT_STYLE_SPECIFIC_SHORT:
   1.706 +            case UTZFMT_STYLE_LOCALIZED_GMT_SHORT:
   1.707 +                formatOffsetShortLocalizedGMT(offset, name, status);
   1.708 +                break;
   1.709 +
   1.710 +            case UTZFMT_STYLE_ISO_BASIC_SHORT:
   1.711 +                formatOffsetISO8601Basic(offset, TRUE, TRUE, TRUE, name, status);
   1.712 +                break;
   1.713 +
   1.714 +            case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT:
   1.715 +                formatOffsetISO8601Basic(offset, FALSE, TRUE, TRUE, name, status);
   1.716 +                break;
   1.717 +
   1.718 +            case UTZFMT_STYLE_ISO_BASIC_FIXED:
   1.719 +                formatOffsetISO8601Basic(offset, TRUE, FALSE, TRUE, name, status);
   1.720 +                break;
   1.721 +
   1.722 +            case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED:
   1.723 +                formatOffsetISO8601Basic(offset, FALSE, FALSE, TRUE, name, status);
   1.724 +                break;
   1.725 +
   1.726 +            case UTZFMT_STYLE_ISO_EXTENDED_FIXED:
   1.727 +                formatOffsetISO8601Extended(offset, TRUE, FALSE, TRUE, name, status);
   1.728 +                break;
   1.729 +
   1.730 +            case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED:
   1.731 +                formatOffsetISO8601Extended(offset, FALSE, FALSE, TRUE, name, status);
   1.732 +                break;
   1.733 +
   1.734 +            case UTZFMT_STYLE_ISO_BASIC_FULL:
   1.735 +                formatOffsetISO8601Basic(offset, TRUE, FALSE, FALSE, name, status);
   1.736 +                break;
   1.737 +
   1.738 +            case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL:
   1.739 +                formatOffsetISO8601Basic(offset, FALSE, FALSE, FALSE, name, status);
   1.740 +                break;
   1.741 +
   1.742 +            case UTZFMT_STYLE_ISO_EXTENDED_FULL:
   1.743 +                formatOffsetISO8601Extended(offset, TRUE, FALSE, FALSE, name, status);
   1.744 +                break;
   1.745 +
   1.746 +            case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL:
   1.747 +                formatOffsetISO8601Extended(offset, FALSE, FALSE, FALSE, name, status);
   1.748 +                break;
   1.749 +
   1.750 +            default:
   1.751 +              // UTZFMT_STYLE_ZONE_ID, UTZFMT_STYLE_ZONE_ID_SHORT, UTZFMT_STYLE_EXEMPLAR_LOCATION
   1.752 +              break;
   1.753 +            }
   1.754 +
   1.755 +            if (timeType) {
   1.756 +                *timeType = (dstOffset != 0) ? UTZFMT_TIME_TYPE_DAYLIGHT : UTZFMT_TIME_TYPE_STANDARD;
   1.757 +            }
   1.758 +        }
   1.759 +    }
   1.760 +
   1.761 +    return name;
   1.762 +}
   1.763 +
   1.764 +UnicodeString&
   1.765 +TimeZoneFormat::format(const Formattable& obj, UnicodeString& appendTo,
   1.766 +        FieldPosition& pos, UErrorCode& status) const {
   1.767 +    if (U_FAILURE(status)) {
   1.768 +        return appendTo;
   1.769 +    }
   1.770 +    UDate date = Calendar::getNow();
   1.771 +    if (obj.getType() == Formattable::kObject) {
   1.772 +        const UObject* formatObj = obj.getObject();
   1.773 +        const TimeZone* tz = dynamic_cast<const TimeZone*>(formatObj);
   1.774 +        if (tz == NULL) {
   1.775 +            const Calendar* cal = dynamic_cast<const Calendar*>(formatObj);
   1.776 +            if (cal != NULL) {
   1.777 +                tz = &cal->getTimeZone();
   1.778 +                date = cal->getTime(status);
   1.779 +            }
   1.780 +        }
   1.781 +        if (tz != NULL) {
   1.782 +            int32_t rawOffset, dstOffset;
   1.783 +            tz->getOffset(date, FALSE, rawOffset, dstOffset, status);
   1.784 +            UnicodeString result;
   1.785 +            formatOffsetLocalizedGMT(rawOffset + dstOffset, result, status);
   1.786 +            if (U_SUCCESS(status)) {
   1.787 +                appendTo.append(result);
   1.788 +                if (pos.getField() == UDAT_TIMEZONE_FIELD) {
   1.789 +                    pos.setBeginIndex(0);
   1.790 +                    pos.setEndIndex(result.length());
   1.791 +                }
   1.792 +            }
   1.793 +        }
   1.794 +    }
   1.795 +    return appendTo;
   1.796 +}
   1.797 +
   1.798 +TimeZone*
   1.799 +TimeZoneFormat::parse(UTimeZoneFormatStyle style, const UnicodeString& text, ParsePosition& pos,
   1.800 +        UTimeZoneFormatTimeType* timeType /*= NULL*/) const {
   1.801 +    return parse(style, text, pos, getDefaultParseOptions(), timeType);
   1.802 +}
   1.803 +
   1.804 +TimeZone*
   1.805 +TimeZoneFormat::parse(UTimeZoneFormatStyle style, const UnicodeString& text, ParsePosition& pos,
   1.806 +        int32_t parseOptions, UTimeZoneFormatTimeType* timeType /* = NULL */) const {
   1.807 +    if (timeType) {
   1.808 +        *timeType = UTZFMT_TIME_TYPE_UNKNOWN;
   1.809 +    }
   1.810 +
   1.811 +    int32_t startIdx = pos.getIndex();
   1.812 +    int32_t maxPos = text.length();
   1.813 +    int32_t offset;
   1.814 +
   1.815 +    // Styles using localized GMT format as fallback
   1.816 +    UBool fallbackLocalizedGMT = 
   1.817 +        (style == UTZFMT_STYLE_SPECIFIC_LONG || style == UTZFMT_STYLE_GENERIC_LONG || style == UTZFMT_STYLE_GENERIC_LOCATION);
   1.818 +    UBool fallbackShortLocalizedGMT =
   1.819 +        (style == UTZFMT_STYLE_SPECIFIC_SHORT || style == UTZFMT_STYLE_GENERIC_SHORT);
   1.820 +
   1.821 +    int32_t evaluated = 0;  // bit flags representing already evaluated styles
   1.822 +    ParsePosition tmpPos(startIdx);
   1.823 +
   1.824 +    int32_t parsedOffset = UNKNOWN_OFFSET;  // stores successfully parsed offset for later use
   1.825 +    int32_t parsedPos = -1;                 // stores successfully parsed offset position for later use
   1.826 +
   1.827 +    // Try localized GMT format first if necessary
   1.828 +    if (fallbackLocalizedGMT || fallbackShortLocalizedGMT) {
   1.829 +        UBool hasDigitOffset = FALSE;
   1.830 +        offset = parseOffsetLocalizedGMT(text, tmpPos, fallbackShortLocalizedGMT, &hasDigitOffset);
   1.831 +        if (tmpPos.getErrorIndex() == -1) {
   1.832 +            // Even when the input text was successfully parsed as a localized GMT format text,
   1.833 +            // we may still need to evaluate the specified style if -
   1.834 +            //   1) GMT zero format was used, and
   1.835 +            //   2) The input text was not completely processed
   1.836 +            if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
   1.837 +                pos.setIndex(tmpPos.getIndex());
   1.838 +                return createTimeZoneForOffset(offset);
   1.839 +            }
   1.840 +            parsedOffset = offset;
   1.841 +            parsedPos = tmpPos.getIndex();
   1.842 +        }
   1.843 +        // Note: For now, no distinction between long/short localized GMT format in the parser.
   1.844 +        // This might be changed in future.
   1.845 +        // evaluated |= (fallbackLocalizedGMT ? STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT] : STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT]);
   1.846 +        evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT] | STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT];
   1.847 +    }
   1.848 +
   1.849 +    UErrorCode status = U_ZERO_ERROR;
   1.850 +    UnicodeString tzID;
   1.851 +
   1.852 +    // Try the specified style
   1.853 +    switch (style) {
   1.854 +    case UTZFMT_STYLE_LOCALIZED_GMT:
   1.855 +        {
   1.856 +            tmpPos.setIndex(startIdx);
   1.857 +            tmpPos.setErrorIndex(-1);
   1.858 +
   1.859 +            offset = parseOffsetLocalizedGMT(text, tmpPos);
   1.860 +            if (tmpPos.getErrorIndex() == -1) {
   1.861 +                pos.setIndex(tmpPos.getIndex());
   1.862 +                return createTimeZoneForOffset(offset);
   1.863 +            }
   1.864 +
   1.865 +            // Note: For now, no distinction between long/short localized GMT format in the parser.
   1.866 +            // This might be changed in future.
   1.867 +            evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT];
   1.868 +
   1.869 +            break;
   1.870 +        }
   1.871 +    case UTZFMT_STYLE_LOCALIZED_GMT_SHORT:
   1.872 +        {
   1.873 +            tmpPos.setIndex(startIdx);
   1.874 +            tmpPos.setErrorIndex(-1);
   1.875 +
   1.876 +            offset = parseOffsetShortLocalizedGMT(text, tmpPos);
   1.877 +            if (tmpPos.getErrorIndex() == -1) {
   1.878 +                pos.setIndex(tmpPos.getIndex());
   1.879 +                return createTimeZoneForOffset(offset);
   1.880 +            }
   1.881 +
   1.882 +            // Note: For now, no distinction between long/short localized GMT format in the parser.
   1.883 +            // This might be changed in future.
   1.884 +            evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT];
   1.885 +
   1.886 +            break;
   1.887 +        }
   1.888 +    case UTZFMT_STYLE_ISO_BASIC_SHORT:
   1.889 +    case UTZFMT_STYLE_ISO_BASIC_FIXED:
   1.890 +    case UTZFMT_STYLE_ISO_BASIC_FULL:
   1.891 +    case UTZFMT_STYLE_ISO_EXTENDED_FIXED:
   1.892 +    case UTZFMT_STYLE_ISO_EXTENDED_FULL:
   1.893 +        {
   1.894 +            tmpPos.setIndex(startIdx);
   1.895 +            tmpPos.setErrorIndex(-1);
   1.896 +
   1.897 +            offset = parseOffsetISO8601(text, tmpPos);
   1.898 +            if (tmpPos.getErrorIndex() == -1) {
   1.899 +                pos.setIndex(tmpPos.getIndex());
   1.900 +                return createTimeZoneForOffset(offset);
   1.901 +            }
   1.902 +
   1.903 +            break;
   1.904 +        }
   1.905 +
   1.906 +    case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT:
   1.907 +    case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED:
   1.908 +    case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL:
   1.909 +    case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED:
   1.910 +    case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL:
   1.911 +        {
   1.912 +            tmpPos.setIndex(startIdx);
   1.913 +            tmpPos.setErrorIndex(-1);
   1.914 +
   1.915 +            // Exclude the case of UTC Indicator "Z" here
   1.916 +            UBool hasDigitOffset = FALSE;
   1.917 +            offset = parseOffsetISO8601(text, tmpPos, FALSE, &hasDigitOffset);
   1.918 +            if (tmpPos.getErrorIndex() == -1 && hasDigitOffset) {
   1.919 +                pos.setIndex(tmpPos.getIndex());
   1.920 +                return createTimeZoneForOffset(offset);
   1.921 +            }
   1.922 +
   1.923 +            break;
   1.924 +        }
   1.925 +
   1.926 +    case UTZFMT_STYLE_SPECIFIC_LONG:
   1.927 +    case UTZFMT_STYLE_SPECIFIC_SHORT:
   1.928 +        {
   1.929 +            // Specific styles
   1.930 +            int32_t nameTypes = 0;
   1.931 +            if (style == UTZFMT_STYLE_SPECIFIC_LONG) {
   1.932 +                nameTypes = (UTZNM_LONG_STANDARD | UTZNM_LONG_DAYLIGHT);
   1.933 +            } else {
   1.934 +                U_ASSERT(style == UTZFMT_STYLE_SPECIFIC_SHORT);
   1.935 +                nameTypes = (UTZNM_SHORT_STANDARD | UTZNM_SHORT_DAYLIGHT);
   1.936 +            }
   1.937 +            LocalPointer<TimeZoneNames::MatchInfoCollection> specificMatches(fTimeZoneNames->find(text, startIdx, nameTypes, status));
   1.938 +            if (U_FAILURE(status)) {
   1.939 +                pos.setErrorIndex(startIdx);
   1.940 +                return NULL;
   1.941 +            }
   1.942 +            if (!specificMatches.isNull()) {
   1.943 +                int32_t matchIdx = -1;
   1.944 +                int32_t matchPos = -1;
   1.945 +                for (int32_t i = 0; i < specificMatches->size(); i++) {
   1.946 +                    matchPos  = startIdx + specificMatches->getMatchLengthAt(i);
   1.947 +                    if (matchPos > parsedPos) {
   1.948 +                        matchIdx = i;
   1.949 +                        parsedPos = matchPos;
   1.950 +                    }
   1.951 +                }
   1.952 +                if (matchIdx >= 0) {
   1.953 +                    if (timeType) {
   1.954 +                        *timeType = getTimeType(specificMatches->getNameTypeAt(matchIdx));
   1.955 +                    }
   1.956 +                    pos.setIndex(matchPos);
   1.957 +                    getTimeZoneID(specificMatches.getAlias(), matchIdx, tzID);
   1.958 +                    U_ASSERT(!tzID.isEmpty());
   1.959 +                    return TimeZone::createTimeZone(tzID);
   1.960 +                }
   1.961 +            }
   1.962 +            break;
   1.963 +        }
   1.964 +    case UTZFMT_STYLE_GENERIC_LONG:
   1.965 +    case UTZFMT_STYLE_GENERIC_SHORT:
   1.966 +    case UTZFMT_STYLE_GENERIC_LOCATION:
   1.967 +        {
   1.968 +            int32_t genericNameTypes = 0;
   1.969 +            switch (style) {
   1.970 +            case UTZFMT_STYLE_GENERIC_LOCATION:
   1.971 +                genericNameTypes = UTZGNM_LOCATION;
   1.972 +                break;
   1.973 +
   1.974 +            case UTZFMT_STYLE_GENERIC_LONG:
   1.975 +                genericNameTypes = UTZGNM_LONG | UTZGNM_LOCATION;
   1.976 +                break;
   1.977 +
   1.978 +            case UTZFMT_STYLE_GENERIC_SHORT:
   1.979 +                genericNameTypes = UTZGNM_SHORT | UTZGNM_LOCATION;
   1.980 +                break;
   1.981 +
   1.982 +            default:
   1.983 +                U_ASSERT(FALSE);
   1.984 +            }
   1.985 +
   1.986 +            int32_t len = 0;
   1.987 +            UTimeZoneFormatTimeType tt = UTZFMT_TIME_TYPE_UNKNOWN;
   1.988 +            const TimeZoneGenericNames *gnames = getTimeZoneGenericNames(status);
   1.989 +            if (U_SUCCESS(status)) {
   1.990 +                len = gnames->findBestMatch(text, startIdx, genericNameTypes, tzID, tt, status);
   1.991 +            }
   1.992 +            if (U_FAILURE(status)) {
   1.993 +                pos.setErrorIndex(startIdx);
   1.994 +                return NULL;
   1.995 +            }
   1.996 +            if (len > 0) {
   1.997 +                // Found a match
   1.998 +                if (timeType) {
   1.999 +                    *timeType = tt;
  1.1000 +                }
  1.1001 +                pos.setIndex(startIdx + len);
  1.1002 +                U_ASSERT(!tzID.isEmpty());
  1.1003 +                return TimeZone::createTimeZone(tzID);
  1.1004 +            }
  1.1005 +
  1.1006 +            break;
  1.1007 +        }
  1.1008 +    case UTZFMT_STYLE_ZONE_ID:
  1.1009 +        {
  1.1010 +            tmpPos.setIndex(startIdx);
  1.1011 +            tmpPos.setErrorIndex(-1);
  1.1012 +
  1.1013 +            parseZoneID(text, tmpPos, tzID);
  1.1014 +            if (tmpPos.getErrorIndex() == -1) {
  1.1015 +                pos.setIndex(tmpPos.getIndex());
  1.1016 +                return TimeZone::createTimeZone(tzID);
  1.1017 +            }
  1.1018 +            break;
  1.1019 +        }
  1.1020 +    case UTZFMT_STYLE_ZONE_ID_SHORT:
  1.1021 +        {
  1.1022 +            tmpPos.setIndex(startIdx);
  1.1023 +            tmpPos.setErrorIndex(-1);
  1.1024 +
  1.1025 +            parseShortZoneID(text, tmpPos, tzID);
  1.1026 +            if (tmpPos.getErrorIndex() == -1) {
  1.1027 +                pos.setIndex(tmpPos.getIndex());
  1.1028 +                return TimeZone::createTimeZone(tzID);
  1.1029 +            }
  1.1030 +            break;
  1.1031 +        }
  1.1032 +    case UTZFMT_STYLE_EXEMPLAR_LOCATION:
  1.1033 +        {
  1.1034 +            tmpPos.setIndex(startIdx);
  1.1035 +            tmpPos.setErrorIndex(-1);
  1.1036 +
  1.1037 +            parseExemplarLocation(text, tmpPos, tzID);
  1.1038 +            if (tmpPos.getErrorIndex() == -1) {
  1.1039 +                pos.setIndex(tmpPos.getIndex());
  1.1040 +                return TimeZone::createTimeZone(tzID);
  1.1041 +            }
  1.1042 +            break;
  1.1043 +        }
  1.1044 +    }
  1.1045 +    evaluated |= STYLE_PARSE_FLAGS[style];
  1.1046 +
  1.1047 +
  1.1048 +    if (parsedPos > startIdx) {
  1.1049 +        // When the specified style is one of SPECIFIC_XXX or GENERIC_XXX, we tried to parse the input
  1.1050 +        // as localized GMT format earlier. If parsedOffset is positive, it means it was successfully
  1.1051 +        // parsed as localized GMT format, but offset digits were not detected (more specifically, GMT
  1.1052 +        // zero format). Then, it tried to find a match within the set of display names, but could not
  1.1053 +        // find a match. At this point, we can safely assume the input text contains the localized
  1.1054 +        // GMT format.
  1.1055 +        U_ASSERT(parsedOffset != UNKNOWN_OFFSET);
  1.1056 +        pos.setIndex(parsedPos);
  1.1057 +        return createTimeZoneForOffset(parsedOffset);
  1.1058 +    }
  1.1059 +
  1.1060 +    // Failed to parse the input text as the time zone format in the specified style.
  1.1061 +    // Check the longest match among other styles below.
  1.1062 +    UnicodeString parsedID;
  1.1063 +    UTimeZoneFormatTimeType parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  1.1064 +
  1.1065 +    U_ASSERT(parsedPos < 0);
  1.1066 +    U_ASSERT(parsedOffset == UNKNOWN_OFFSET);
  1.1067 +
  1.1068 +    // ISO 8601
  1.1069 +    if (parsedPos < maxPos &&
  1.1070 +        ((evaluated & ISO_Z_STYLE_FLAG) == 0 || (evaluated & ISO_LOCAL_STYLE_FLAG) == 0)) {
  1.1071 +        tmpPos.setIndex(startIdx);
  1.1072 +        tmpPos.setErrorIndex(-1);
  1.1073 +
  1.1074 +        UBool hasDigitOffset = FALSE;
  1.1075 +        offset = parseOffsetISO8601(text, tmpPos, FALSE, &hasDigitOffset);
  1.1076 +        if (tmpPos.getErrorIndex() == -1) {
  1.1077 +            if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
  1.1078 +                pos.setIndex(tmpPos.getIndex());
  1.1079 +                return createTimeZoneForOffset(offset);
  1.1080 +            }
  1.1081 +            // Note: When ISO 8601 format contains offset digits, it should not
  1.1082 +            // collide with other formats. However, ISO 8601 UTC format "Z" (single letter)
  1.1083 +            // may collide with other names. In this case, we need to evaluate other names.
  1.1084 +            if (parsedPos < tmpPos.getIndex()) {
  1.1085 +                parsedOffset = offset;
  1.1086 +                parsedID.setToBogus();
  1.1087 +                parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  1.1088 +                parsedPos = tmpPos.getIndex();
  1.1089 +                U_ASSERT(parsedPos == startIdx + 1);    // only when "Z" is used
  1.1090 +            }
  1.1091 +        }
  1.1092 +    }
  1.1093 +
  1.1094 +    // Localized GMT format
  1.1095 +    if (parsedPos < maxPos &&
  1.1096 +        (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT]) == 0) {
  1.1097 +        tmpPos.setIndex(startIdx);
  1.1098 +        tmpPos.setErrorIndex(-1);
  1.1099 +
  1.1100 +        UBool hasDigitOffset = FALSE;
  1.1101 +        offset = parseOffsetLocalizedGMT(text, tmpPos, FALSE, &hasDigitOffset);
  1.1102 +        if (tmpPos.getErrorIndex() == -1) {
  1.1103 +            if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
  1.1104 +                pos.setIndex(tmpPos.getIndex());
  1.1105 +                return createTimeZoneForOffset(offset);
  1.1106 +            }
  1.1107 +            // Evaluate other names - see the comment earlier in this method.
  1.1108 +            if (parsedPos < tmpPos.getIndex()) {
  1.1109 +                parsedOffset = offset;
  1.1110 +                parsedID.setToBogus();
  1.1111 +                parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  1.1112 +                parsedPos = tmpPos.getIndex();
  1.1113 +            }
  1.1114 +        }
  1.1115 +    }
  1.1116 +
  1.1117 +    if (parsedPos < maxPos &&
  1.1118 +        (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT]) == 0) {
  1.1119 +        tmpPos.setIndex(startIdx);
  1.1120 +        tmpPos.setErrorIndex(-1);
  1.1121 +
  1.1122 +        UBool hasDigitOffset = FALSE;
  1.1123 +        offset = parseOffsetLocalizedGMT(text, tmpPos, TRUE, &hasDigitOffset);
  1.1124 +        if (tmpPos.getErrorIndex() == -1) {
  1.1125 +            if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
  1.1126 +                pos.setIndex(tmpPos.getIndex());
  1.1127 +                return createTimeZoneForOffset(offset);
  1.1128 +            }
  1.1129 +            // Evaluate other names - see the comment earlier in this method.
  1.1130 +            if (parsedPos < tmpPos.getIndex()) {
  1.1131 +                parsedOffset = offset;
  1.1132 +                parsedID.setToBogus();
  1.1133 +                parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  1.1134 +                parsedPos = tmpPos.getIndex();
  1.1135 +            }
  1.1136 +        }
  1.1137 +    }
  1.1138 +
  1.1139 +    // When ParseOption.ALL_STYLES is available, we also try to look all possible display names and IDs.
  1.1140 +    // For example, when style is GENERIC_LONG, "EST" (SPECIFIC_SHORT) is never
  1.1141 +    // used for America/New_York. With parseAllStyles true, this code parses "EST"
  1.1142 +    // as America/New_York.
  1.1143 +
  1.1144 +    // Note: Adding all possible names into the trie used by the implementation is quite heavy operation,
  1.1145 +    // which we want to avoid normally (note that we cache the trie, so this is applicable to the
  1.1146 +    // first time only as long as the cache does not expire).
  1.1147 +
  1.1148 +    if (parseOptions & UTZFMT_PARSE_OPTION_ALL_STYLES) {
  1.1149 +        // Try all specific names and exemplar location names
  1.1150 +        if (parsedPos < maxPos) {
  1.1151 +            LocalPointer<TimeZoneNames::MatchInfoCollection> specificMatches(fTimeZoneNames->find(text, startIdx, ALL_SIMPLE_NAME_TYPES, status));
  1.1152 +            if (U_FAILURE(status)) {
  1.1153 +                pos.setErrorIndex(startIdx);
  1.1154 +                return NULL;
  1.1155 +            }
  1.1156 +            int32_t specificMatchIdx = -1;
  1.1157 +            int32_t matchPos = -1;
  1.1158 +            if (!specificMatches.isNull()) {
  1.1159 +                for (int32_t i = 0; i < specificMatches->size(); i++) {
  1.1160 +                    if (startIdx + specificMatches->getMatchLengthAt(i) > matchPos) {
  1.1161 +                        specificMatchIdx = i;
  1.1162 +                        matchPos = startIdx + specificMatches->getMatchLengthAt(i);
  1.1163 +                    }
  1.1164 +                }
  1.1165 +            }
  1.1166 +            if (parsedPos < matchPos) {
  1.1167 +                U_ASSERT(specificMatchIdx >= 0);
  1.1168 +                parsedPos = matchPos;
  1.1169 +                getTimeZoneID(specificMatches.getAlias(), specificMatchIdx, parsedID);
  1.1170 +                parsedTimeType = getTimeType(specificMatches->getNameTypeAt(specificMatchIdx));
  1.1171 +                parsedOffset = UNKNOWN_OFFSET;
  1.1172 +            }
  1.1173 +        }
  1.1174 +        // Try generic names
  1.1175 +        if (parsedPos < maxPos) {
  1.1176 +            int32_t genMatchLen = -1;
  1.1177 +            UTimeZoneFormatTimeType tt = UTZFMT_TIME_TYPE_UNKNOWN;
  1.1178 +
  1.1179 +            const TimeZoneGenericNames *gnames = getTimeZoneGenericNames(status);
  1.1180 +            if (U_SUCCESS(status)) {
  1.1181 +                genMatchLen = gnames->findBestMatch(text, startIdx, ALL_GENERIC_NAME_TYPES, tzID, tt, status);
  1.1182 +            }
  1.1183 +            if (U_FAILURE(status)) {
  1.1184 +                pos.setErrorIndex(startIdx);
  1.1185 +                return NULL;
  1.1186 +            }
  1.1187 +
  1.1188 +            if (parsedPos < startIdx + genMatchLen) {
  1.1189 +                parsedPos = startIdx + genMatchLen;
  1.1190 +                parsedID.setTo(tzID);
  1.1191 +                parsedTimeType = tt;
  1.1192 +                parsedOffset = UNKNOWN_OFFSET;
  1.1193 +            }
  1.1194 +        }
  1.1195 +
  1.1196 +        // Try time zone ID
  1.1197 +        if (parsedPos < maxPos && (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_ZONE_ID]) == 0) {
  1.1198 +            tmpPos.setIndex(startIdx);
  1.1199 +            tmpPos.setErrorIndex(-1);
  1.1200 +
  1.1201 +            parseZoneID(text, tmpPos, tzID);
  1.1202 +            if (tmpPos.getErrorIndex() == -1 && parsedPos < tmpPos.getIndex()) {
  1.1203 +                parsedPos = tmpPos.getIndex();
  1.1204 +                parsedID.setTo(tzID);
  1.1205 +                parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  1.1206 +                parsedOffset = UNKNOWN_OFFSET;
  1.1207 +            }
  1.1208 +        }
  1.1209 +        // Try short time zone ID
  1.1210 +        if (parsedPos < maxPos && (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_ZONE_ID]) == 0) {
  1.1211 +            tmpPos.setIndex(startIdx);
  1.1212 +            tmpPos.setErrorIndex(-1);
  1.1213 +
  1.1214 +            parseShortZoneID(text, tmpPos, tzID);
  1.1215 +            if (tmpPos.getErrorIndex() == -1 && parsedPos < tmpPos.getIndex()) {
  1.1216 +                parsedPos = tmpPos.getIndex();
  1.1217 +                parsedID.setTo(tzID);
  1.1218 +                parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  1.1219 +                parsedOffset = UNKNOWN_OFFSET;
  1.1220 +            }
  1.1221 +        }
  1.1222 +    }
  1.1223 +
  1.1224 +    if (parsedPos > startIdx) {
  1.1225 +        // Parsed successfully
  1.1226 +        TimeZone* parsedTZ;
  1.1227 +        if (parsedID.length() > 0) {
  1.1228 +            parsedTZ = TimeZone::createTimeZone(parsedID);
  1.1229 +        } else {
  1.1230 +            U_ASSERT(parsedOffset != UNKNOWN_OFFSET);
  1.1231 +            parsedTZ = createTimeZoneForOffset(parsedOffset);
  1.1232 +        }
  1.1233 +        if (timeType) {
  1.1234 +            *timeType = parsedTimeType;
  1.1235 +        }
  1.1236 +        pos.setIndex(parsedPos);
  1.1237 +        return parsedTZ;
  1.1238 +    }
  1.1239 +
  1.1240 +    pos.setErrorIndex(startIdx);
  1.1241 +    return NULL;
  1.1242 +}
  1.1243 +
  1.1244 +void
  1.1245 +TimeZoneFormat::parseObject(const UnicodeString& source, Formattable& result,
  1.1246 +        ParsePosition& parse_pos) const {
  1.1247 +    result.adoptObject(parse(UTZFMT_STYLE_GENERIC_LOCATION, source, parse_pos, UTZFMT_PARSE_OPTION_ALL_STYLES));
  1.1248 +}
  1.1249 +
  1.1250 +
  1.1251 +// ------------------------------------------------------------------
  1.1252 +// Private zone name format/parse implementation
  1.1253 +
  1.1254 +UnicodeString&
  1.1255 +TimeZoneFormat::formatGeneric(const TimeZone& tz, int32_t genType, UDate date, UnicodeString& name) const {
  1.1256 +    UErrorCode status = U_ZERO_ERROR;
  1.1257 +    const TimeZoneGenericNames* gnames = getTimeZoneGenericNames(status);
  1.1258 +    if (U_FAILURE(status)) {
  1.1259 +        name.setToBogus();
  1.1260 +        return name;
  1.1261 +    }
  1.1262 +
  1.1263 +    if (genType == UTZGNM_LOCATION) {
  1.1264 +        const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(tz);
  1.1265 +        if (canonicalID == NULL) {
  1.1266 +            name.setToBogus();
  1.1267 +            return name;
  1.1268 +        }
  1.1269 +        return gnames->getGenericLocationName(UnicodeString(canonicalID), name);
  1.1270 +    }
  1.1271 +    return gnames->getDisplayName(tz, (UTimeZoneGenericNameType)genType, date, name);
  1.1272 +}
  1.1273 +
  1.1274 +UnicodeString&
  1.1275 +TimeZoneFormat::formatSpecific(const TimeZone& tz, UTimeZoneNameType stdType, UTimeZoneNameType dstType,
  1.1276 +        UDate date, UnicodeString& name, UTimeZoneFormatTimeType *timeType) const {
  1.1277 +    if (fTimeZoneNames == NULL) {
  1.1278 +        name.setToBogus();
  1.1279 +        return name;
  1.1280 +    }
  1.1281 +
  1.1282 +    UErrorCode status = U_ZERO_ERROR;
  1.1283 +    UBool isDaylight = tz.inDaylightTime(date, status);
  1.1284 +    const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(tz);
  1.1285 +
  1.1286 +    if (U_FAILURE(status) || canonicalID == NULL) {
  1.1287 +        name.setToBogus();
  1.1288 +        return name;
  1.1289 +    }
  1.1290 +
  1.1291 +    if (isDaylight) {
  1.1292 +        fTimeZoneNames->getDisplayName(UnicodeString(canonicalID), dstType, date, name);
  1.1293 +    } else {
  1.1294 +        fTimeZoneNames->getDisplayName(UnicodeString(canonicalID), stdType, date, name);
  1.1295 +    }
  1.1296 +
  1.1297 +    if (timeType && !name.isEmpty()) {
  1.1298 +        *timeType = isDaylight ? UTZFMT_TIME_TYPE_DAYLIGHT : UTZFMT_TIME_TYPE_STANDARD;
  1.1299 +    }
  1.1300 +    return name;
  1.1301 +}
  1.1302 +
  1.1303 +const TimeZoneGenericNames*
  1.1304 +TimeZoneFormat::getTimeZoneGenericNames(UErrorCode& status) const {
  1.1305 +    if (U_FAILURE(status)) {
  1.1306 +        return NULL;
  1.1307 +    }
  1.1308 +
  1.1309 +    umtx_lock(&gLock);
  1.1310 +    if (fTimeZoneGenericNames == NULL) {
  1.1311 +        TimeZoneFormat *nonConstThis = const_cast<TimeZoneFormat *>(this);
  1.1312 +        nonConstThis->fTimeZoneGenericNames = TimeZoneGenericNames::createInstance(fLocale, status);
  1.1313 +    }
  1.1314 +    umtx_unlock(&gLock);
  1.1315 +
  1.1316 +    return fTimeZoneGenericNames;
  1.1317 +}
  1.1318 +
  1.1319 +UnicodeString&
  1.1320 +TimeZoneFormat::formatExemplarLocation(const TimeZone& tz, UnicodeString& name) const {
  1.1321 +    UnicodeString location;
  1.1322 +    const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(tz);
  1.1323 +
  1.1324 +    if (canonicalID) {
  1.1325 +        fTimeZoneNames->getExemplarLocationName(UnicodeString(canonicalID), location);
  1.1326 +    }
  1.1327 +    if (location.length() > 0) {
  1.1328 +        name.setTo(location);
  1.1329 +    } else {
  1.1330 +        // Use "unknown" location
  1.1331 +        fTimeZoneNames->getExemplarLocationName(UnicodeString(UNKNOWN_ZONE_ID), location);
  1.1332 +        if (location.length() > 0) {
  1.1333 +            name.setTo(location);
  1.1334 +        } else {
  1.1335 +            // last resort
  1.1336 +            name.setTo(UNKNOWN_LOCATION, -1);
  1.1337 +        }
  1.1338 +    }
  1.1339 +    return name;
  1.1340 +}
  1.1341 +
  1.1342 +
  1.1343 +// ------------------------------------------------------------------
  1.1344 +// Zone offset format and parse
  1.1345 +
  1.1346 +UnicodeString&
  1.1347 +TimeZoneFormat::formatOffsetISO8601Basic(int32_t offset, UBool useUtcIndicator, UBool isShort, UBool ignoreSeconds,
  1.1348 +        UnicodeString& result, UErrorCode& status) const {
  1.1349 +    return formatOffsetISO8601(offset, TRUE, useUtcIndicator, isShort, ignoreSeconds, result, status);
  1.1350 +}
  1.1351 +
  1.1352 +UnicodeString&
  1.1353 +TimeZoneFormat::formatOffsetISO8601Extended(int32_t offset, UBool useUtcIndicator, UBool isShort, UBool ignoreSeconds,
  1.1354 +        UnicodeString& result, UErrorCode& status) const {
  1.1355 +    return formatOffsetISO8601(offset, FALSE, useUtcIndicator, isShort, ignoreSeconds, result, status);
  1.1356 +}
  1.1357 +
  1.1358 +UnicodeString&
  1.1359 +TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset, UnicodeString& result, UErrorCode& status) const {
  1.1360 +    return formatOffsetLocalizedGMT(offset, FALSE, result, status);
  1.1361 +}
  1.1362 +
  1.1363 +UnicodeString&
  1.1364 +TimeZoneFormat::formatOffsetShortLocalizedGMT(int32_t offset, UnicodeString& result, UErrorCode& status) const {
  1.1365 +    return formatOffsetLocalizedGMT(offset, TRUE, result, status);
  1.1366 +}
  1.1367 +
  1.1368 +int32_t
  1.1369 +TimeZoneFormat::parseOffsetISO8601(const UnicodeString& text, ParsePosition& pos) const {
  1.1370 +    return parseOffsetISO8601(text, pos, FALSE);
  1.1371 +}
  1.1372 +
  1.1373 +int32_t
  1.1374 +TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString& text, ParsePosition& pos) const {
  1.1375 +    return parseOffsetLocalizedGMT(text, pos, FALSE, NULL);
  1.1376 +}
  1.1377 +
  1.1378 +int32_t
  1.1379 +TimeZoneFormat::parseOffsetShortLocalizedGMT(const UnicodeString& text, ParsePosition& pos) const {
  1.1380 +    return parseOffsetLocalizedGMT(text, pos, TRUE, NULL);
  1.1381 +}
  1.1382 +
  1.1383 +// ------------------------------------------------------------------
  1.1384 +// Private zone offset format/parse implementation
  1.1385 +
  1.1386 +UnicodeString&
  1.1387 +TimeZoneFormat::formatOffsetISO8601(int32_t offset, UBool isBasic, UBool useUtcIndicator,
  1.1388 +        UBool isShort, UBool ignoreSeconds, UnicodeString& result, UErrorCode& status) const {
  1.1389 +    if (U_FAILURE(status)) {
  1.1390 +        result.setToBogus();
  1.1391 +        return result;
  1.1392 +    }
  1.1393 +    int32_t absOffset = offset < 0 ? -offset : offset;
  1.1394 +    if (useUtcIndicator && (absOffset < MILLIS_PER_SECOND || (ignoreSeconds && absOffset < MILLIS_PER_MINUTE))) {
  1.1395 +        result.setTo(ISO8601_UTC);
  1.1396 +        return result;
  1.1397 +    }
  1.1398 +
  1.1399 +    OffsetFields minFields = isShort ? FIELDS_H : FIELDS_HM;
  1.1400 +    OffsetFields maxFields = ignoreSeconds ? FIELDS_HM : FIELDS_HMS;
  1.1401 +    UChar sep = isBasic ? 0 : ISO8601_SEP;
  1.1402 +
  1.1403 +    // Note: FIELDS_HMS as maxFields is a CLDR/ICU extension. ISO 8601 specification does
  1.1404 +    // not support seconds field.
  1.1405 +
  1.1406 +    if (absOffset >= MAX_OFFSET) {
  1.1407 +        result.setToBogus();
  1.1408 +        status = U_ILLEGAL_ARGUMENT_ERROR;
  1.1409 +        return result;
  1.1410 +    }
  1.1411 +
  1.1412 +    int fields[3];
  1.1413 +    fields[0] = absOffset / MILLIS_PER_HOUR;
  1.1414 +    absOffset = absOffset % MILLIS_PER_HOUR;
  1.1415 +    fields[1] = absOffset / MILLIS_PER_MINUTE;
  1.1416 +    absOffset = absOffset % MILLIS_PER_MINUTE;
  1.1417 +    fields[2] = absOffset / MILLIS_PER_SECOND;
  1.1418 +
  1.1419 +    U_ASSERT(fields[0] >= 0 && fields[0] <= MAX_OFFSET_HOUR);
  1.1420 +    U_ASSERT(fields[1] >= 0 && fields[1] <= MAX_OFFSET_MINUTE);
  1.1421 +    U_ASSERT(fields[2] >= 0 && fields[2] <= MAX_OFFSET_SECOND);
  1.1422 +
  1.1423 +    int32_t lastIdx = maxFields;
  1.1424 +    while (lastIdx > minFields) {
  1.1425 +        if (fields[lastIdx] != 0) {
  1.1426 +            break;
  1.1427 +        }
  1.1428 +        lastIdx--;
  1.1429 +    }
  1.1430 +
  1.1431 +    UChar sign = PLUS;
  1.1432 +    if (offset < 0) {
  1.1433 +        // if all output fields are 0s, do not use negative sign
  1.1434 +        for (int32_t idx = 0; idx <= lastIdx; idx++) {
  1.1435 +            if (fields[idx] != 0) {
  1.1436 +                sign = MINUS;
  1.1437 +                break;
  1.1438 +            }
  1.1439 +        }
  1.1440 +    }
  1.1441 +    result.setTo(sign);
  1.1442 +
  1.1443 +    for (int32_t idx = 0; idx <= lastIdx; idx++) {
  1.1444 +        if (sep && idx != 0) {
  1.1445 +            result.append(sep);
  1.1446 +        }
  1.1447 +        result.append((UChar)(0x0030 + fields[idx]/10));
  1.1448 +        result.append((UChar)(0x0030 + fields[idx]%10));
  1.1449 +    }
  1.1450 +
  1.1451 +    return result;
  1.1452 +}
  1.1453 +
  1.1454 +UnicodeString&
  1.1455 +TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset, UBool isShort, UnicodeString& result, UErrorCode& status) const {
  1.1456 +    if (U_FAILURE(status)) {
  1.1457 +        result.setToBogus();
  1.1458 +        return result;
  1.1459 +    }
  1.1460 +    if (offset <= -MAX_OFFSET || offset >= MAX_OFFSET) {
  1.1461 +        result.setToBogus();
  1.1462 +        status = U_ILLEGAL_ARGUMENT_ERROR;
  1.1463 +        return result;
  1.1464 +    }
  1.1465 +
  1.1466 +    if (offset == 0) {
  1.1467 +        result.setTo(fGMTZeroFormat);
  1.1468 +        return result;
  1.1469 +    }
  1.1470 +
  1.1471 +    UBool positive = TRUE;
  1.1472 +    if (offset < 0) {
  1.1473 +        offset = -offset;
  1.1474 +        positive = FALSE;
  1.1475 +    }
  1.1476 +
  1.1477 +    int32_t offsetH = offset / MILLIS_PER_HOUR;
  1.1478 +    offset = offset % MILLIS_PER_HOUR;
  1.1479 +    int32_t offsetM = offset / MILLIS_PER_MINUTE;
  1.1480 +    offset = offset % MILLIS_PER_MINUTE;
  1.1481 +    int32_t offsetS = offset / MILLIS_PER_SECOND;
  1.1482 +
  1.1483 +    U_ASSERT(offsetH <= MAX_OFFSET_HOUR && offsetM <= MAX_OFFSET_MINUTE && offsetS <= MAX_OFFSET_SECOND);
  1.1484 +
  1.1485 +    const UVector* offsetPatternItems = NULL;
  1.1486 +    if (positive) {
  1.1487 +        if (offsetS != 0) {
  1.1488 +            offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_HMS];
  1.1489 +        } else if (offsetM != 0 || !isShort) {
  1.1490 +            offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_HM];
  1.1491 +        } else {
  1.1492 +            offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_H];
  1.1493 +        }
  1.1494 +    } else {
  1.1495 +        if (offsetS != 0) {
  1.1496 +            offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_HMS];
  1.1497 +        } else if (offsetM != 0 || !isShort) {
  1.1498 +            offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_HM];
  1.1499 +        } else {
  1.1500 +            offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_H];
  1.1501 +        }
  1.1502 +    }
  1.1503 +
  1.1504 +    U_ASSERT(offsetPatternItems != NULL);
  1.1505 +
  1.1506 +    // Building the GMT format string
  1.1507 +    result.setTo(fGMTPatternPrefix);
  1.1508 +
  1.1509 +    for (int32_t i = 0; i < offsetPatternItems->size(); i++) {
  1.1510 +        const GMTOffsetField* item = (GMTOffsetField*)offsetPatternItems->elementAt(i);
  1.1511 +        GMTOffsetField::FieldType type = item->getType();
  1.1512 +
  1.1513 +        switch (type) {
  1.1514 +        case GMTOffsetField::TEXT:
  1.1515 +            result.append(item->getPatternText(), -1);
  1.1516 +            break;
  1.1517 +
  1.1518 +        case GMTOffsetField::HOUR:
  1.1519 +            appendOffsetDigits(result, offsetH, (isShort ? 1 : 2));
  1.1520 +            break;
  1.1521 +
  1.1522 +        case GMTOffsetField::MINUTE:
  1.1523 +            appendOffsetDigits(result, offsetM, 2);
  1.1524 +            break;
  1.1525 +
  1.1526 +        case GMTOffsetField::SECOND:
  1.1527 +            appendOffsetDigits(result, offsetS, 2);
  1.1528 +            break;
  1.1529 +        }
  1.1530 +    }
  1.1531 +
  1.1532 +    result.append(fGMTPatternSuffix);
  1.1533 +    return result;
  1.1534 +}
  1.1535 +
  1.1536 +int32_t
  1.1537 +TimeZoneFormat::parseOffsetISO8601(const UnicodeString& text, ParsePosition& pos, UBool extendedOnly, UBool* hasDigitOffset /* = NULL */) const {
  1.1538 +    if (hasDigitOffset) {
  1.1539 +        *hasDigitOffset = FALSE;
  1.1540 +    }
  1.1541 +    int32_t start = pos.getIndex();
  1.1542 +    if (start >= text.length()) {
  1.1543 +        pos.setErrorIndex(start);
  1.1544 +        return 0;
  1.1545 +    }
  1.1546 +
  1.1547 +    UChar firstChar = text.charAt(start);
  1.1548 +    if (firstChar == ISO8601_UTC || firstChar == (UChar)(ISO8601_UTC + 0x20)) {
  1.1549 +        // "Z" (or "z") - indicates UTC
  1.1550 +        pos.setIndex(start + 1);
  1.1551 +        return 0;
  1.1552 +    }
  1.1553 +
  1.1554 +    int32_t sign = 1;
  1.1555 +    if (firstChar == PLUS) {
  1.1556 +        sign = 1;
  1.1557 +    } else if (firstChar == MINUS) {
  1.1558 +        sign = -1;
  1.1559 +    } else {
  1.1560 +        // Not an ISO 8601 offset string
  1.1561 +        pos.setErrorIndex(start);
  1.1562 +        return 0;
  1.1563 +    }
  1.1564 +    ParsePosition posOffset(start + 1);
  1.1565 +    int32_t offset = parseAsciiOffsetFields(text, posOffset, ISO8601_SEP, FIELDS_H, FIELDS_HMS);
  1.1566 +    if (posOffset.getErrorIndex() == -1 && !extendedOnly && (posOffset.getIndex() - start <= 3)) {
  1.1567 +        // If the text is successfully parsed as extended format with the options above, it can be also parsed
  1.1568 +        // as basic format. For example, "0230" can be parsed as offset 2:00 (only first digits are valid for
  1.1569 +        // extended format), but it can be parsed as offset 2:30 with basic format. We use longer result.
  1.1570 +        ParsePosition posBasic(start + 1);
  1.1571 +        int32_t tmpOffset = parseAbuttingAsciiOffsetFields(text, posBasic, FIELDS_H, FIELDS_HMS, FALSE);
  1.1572 +        if (posBasic.getErrorIndex() == -1 && posBasic.getIndex() > posOffset.getIndex()) {
  1.1573 +            offset = tmpOffset;
  1.1574 +            posOffset.setIndex(posBasic.getIndex());
  1.1575 +        }
  1.1576 +    }
  1.1577 +
  1.1578 +    if (posOffset.getErrorIndex() != -1) {
  1.1579 +        pos.setErrorIndex(start);
  1.1580 +        return 0;
  1.1581 +    }
  1.1582 +
  1.1583 +    pos.setIndex(posOffset.getIndex());
  1.1584 +    if (hasDigitOffset) {
  1.1585 +        *hasDigitOffset = TRUE;
  1.1586 +    }
  1.1587 +    return sign * offset;
  1.1588 +}
  1.1589 +
  1.1590 +int32_t
  1.1591 +TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString& text, ParsePosition& pos, UBool isShort, UBool* hasDigitOffset) const {
  1.1592 +    int32_t start = pos.getIndex();
  1.1593 +    int32_t offset = 0;
  1.1594 +    int32_t parsedLength = 0;
  1.1595 +
  1.1596 +    if (hasDigitOffset) {
  1.1597 +        *hasDigitOffset = FALSE;
  1.1598 +    }
  1.1599 +
  1.1600 +    offset = parseOffsetLocalizedGMTPattern(text, start, isShort, parsedLength);
  1.1601 +
  1.1602 +    // For now, parseOffsetLocalizedGMTPattern handles both long and short
  1.1603 +    // formats, no matter isShort is true or false. This might be changed in future
  1.1604 +    // when strict parsing is necessary, or different set of patterns are used for
  1.1605 +    // short/long formats.
  1.1606 +#if 0
  1.1607 +    if (parsedLength == 0) {
  1.1608 +        offset = parseOffsetLocalizedGMTPattern(text, start, !isShort, parsedLength);
  1.1609 +    }
  1.1610 +#endif
  1.1611 +
  1.1612 +    if (parsedLength > 0) {
  1.1613 +        if (hasDigitOffset) {
  1.1614 +            *hasDigitOffset = TRUE;
  1.1615 +        }
  1.1616 +        pos.setIndex(start + parsedLength);
  1.1617 +        return offset;
  1.1618 +    }
  1.1619 +
  1.1620 +    // Try the default patterns
  1.1621 +    offset = parseOffsetDefaultLocalizedGMT(text, start, parsedLength);
  1.1622 +    if (parsedLength > 0) {
  1.1623 +        if (hasDigitOffset) {
  1.1624 +            *hasDigitOffset = TRUE;
  1.1625 +        }
  1.1626 +        pos.setIndex(start + parsedLength);
  1.1627 +        return offset;
  1.1628 +    }
  1.1629 +
  1.1630 +    // Check if this is a GMT zero format
  1.1631 +    if (text.caseCompare(start, fGMTZeroFormat.length(), fGMTZeroFormat, 0) == 0) {
  1.1632 +        pos.setIndex(start + fGMTZeroFormat.length());
  1.1633 +        return 0;
  1.1634 +    }
  1.1635 +
  1.1636 +    // Check if this is a default GMT zero format
  1.1637 +    for (int32_t i = 0; ALT_GMT_STRINGS[i][0] != 0; i++) {
  1.1638 +        const UChar* defGMTZero = ALT_GMT_STRINGS[i];
  1.1639 +        int32_t defGMTZeroLen = u_strlen(defGMTZero);
  1.1640 +        if (text.caseCompare(start, defGMTZeroLen, defGMTZero, 0) == 0) {
  1.1641 +            pos.setIndex(start + defGMTZeroLen);
  1.1642 +            return 0;
  1.1643 +        }
  1.1644 +    }
  1.1645 +
  1.1646 +    // Nothing matched
  1.1647 +    pos.setErrorIndex(start);
  1.1648 +    return 0;
  1.1649 +}
  1.1650 +
  1.1651 +int32_t
  1.1652 +TimeZoneFormat::parseOffsetLocalizedGMTPattern(const UnicodeString& text, int32_t start, UBool /*isShort*/, int32_t& parsedLen) const {
  1.1653 +    int32_t idx = start;
  1.1654 +    int32_t offset = 0;
  1.1655 +    UBool parsed = FALSE;
  1.1656 +
  1.1657 +    do {
  1.1658 +        // Prefix part
  1.1659 +        int32_t len = fGMTPatternPrefix.length();
  1.1660 +        if (len > 0 && text.caseCompare(idx, len, fGMTPatternPrefix, 0) != 0) {
  1.1661 +            // prefix match failed
  1.1662 +            break;
  1.1663 +        }
  1.1664 +        idx += len;
  1.1665 +
  1.1666 +        // Offset part
  1.1667 +        offset = parseOffsetFields(text, idx, FALSE, len);
  1.1668 +        if (len == 0) {
  1.1669 +            // offset field match failed
  1.1670 +            break;
  1.1671 +        }
  1.1672 +        idx += len;
  1.1673 +
  1.1674 +        len = fGMTPatternSuffix.length();
  1.1675 +        if (len > 0 && text.caseCompare(idx, len, fGMTPatternSuffix, 0) != 0) {
  1.1676 +            // no suffix match
  1.1677 +            break;
  1.1678 +        }
  1.1679 +        idx += len;
  1.1680 +        parsed = TRUE;
  1.1681 +    } while (FALSE);
  1.1682 +
  1.1683 +    parsedLen = parsed ? idx - start : 0;
  1.1684 +    return offset;
  1.1685 +}
  1.1686 +
  1.1687 +int32_t
  1.1688 +TimeZoneFormat::parseOffsetFields(const UnicodeString& text, int32_t start, UBool /*isShort*/, int32_t& parsedLen) const {
  1.1689 +    int32_t outLen = 0;
  1.1690 +    int32_t offset = 0;
  1.1691 +    int32_t sign = 1;
  1.1692 +
  1.1693 +    parsedLen = 0;
  1.1694 +
  1.1695 +    int32_t offsetH, offsetM, offsetS;
  1.1696 +    offsetH = offsetM = offsetS = 0;
  1.1697 +
  1.1698 +    for (int32_t patidx = 0; PARSE_GMT_OFFSET_TYPES[patidx] >= 0; patidx++) {
  1.1699 +        int32_t gmtPatType = PARSE_GMT_OFFSET_TYPES[patidx];
  1.1700 +        UVector* items = fGMTOffsetPatternItems[gmtPatType];
  1.1701 +        U_ASSERT(items != NULL);
  1.1702 +
  1.1703 +        outLen = parseOffsetFieldsWithPattern(text, start, items, FALSE, offsetH, offsetM, offsetS);
  1.1704 +        if (outLen > 0) {
  1.1705 +            sign = (gmtPatType == UTZFMT_PAT_POSITIVE_H || gmtPatType == UTZFMT_PAT_POSITIVE_HM || gmtPatType == UTZFMT_PAT_POSITIVE_HMS) ?
  1.1706 +                1 : -1;
  1.1707 +            break;
  1.1708 +        }
  1.1709 +    }
  1.1710 +
  1.1711 +    if (outLen > 0 && fAbuttingOffsetHoursAndMinutes) {
  1.1712 +        // When hours field is sabutting minutes field,
  1.1713 +        // the parse result above may not be appropriate.
  1.1714 +        // For example, "01020" is parsed as 01:02: above,
  1.1715 +        // but it should be parsed as 00:10:20.
  1.1716 +        int32_t tmpLen = 0;
  1.1717 +        int32_t tmpSign = 1;
  1.1718 +        int32_t tmpH, tmpM, tmpS;
  1.1719 +
  1.1720 +        for (int32_t patidx = 0; PARSE_GMT_OFFSET_TYPES[patidx] >= 0; patidx++) {
  1.1721 +            int32_t gmtPatType = PARSE_GMT_OFFSET_TYPES[patidx];
  1.1722 +            UVector* items = fGMTOffsetPatternItems[gmtPatType];
  1.1723 +            U_ASSERT(items != NULL);
  1.1724 +
  1.1725 +            // forcing parse to use single hour digit
  1.1726 +            tmpLen = parseOffsetFieldsWithPattern(text, start, items, TRUE, tmpH, tmpM, tmpS);
  1.1727 +            if (tmpLen > 0) {
  1.1728 +                tmpSign = (gmtPatType == UTZFMT_PAT_POSITIVE_H || gmtPatType == UTZFMT_PAT_POSITIVE_HM || gmtPatType == UTZFMT_PAT_POSITIVE_HMS) ?
  1.1729 +                    1 : -1;
  1.1730 +                break;
  1.1731 +            }
  1.1732 +        }
  1.1733 +        if (tmpLen > outLen) {
  1.1734 +            // Better parse result with single hour digit
  1.1735 +            outLen = tmpLen;
  1.1736 +            sign = tmpSign;
  1.1737 +            offsetH = tmpH;
  1.1738 +            offsetM = tmpM;
  1.1739 +            offsetS = tmpS;
  1.1740 +        }
  1.1741 +    }
  1.1742 +
  1.1743 +    if (outLen > 0) {
  1.1744 +        offset = ((((offsetH * 60) + offsetM) * 60) + offsetS) * 1000 * sign;
  1.1745 +        parsedLen = outLen;
  1.1746 +    }
  1.1747 +
  1.1748 +    return offset;
  1.1749 +}
  1.1750 +
  1.1751 +int32_t
  1.1752 +TimeZoneFormat::parseOffsetFieldsWithPattern(const UnicodeString& text, int32_t start,
  1.1753 +        UVector* patternItems, UBool forceSingleHourDigit, int32_t& hour, int32_t& min, int32_t& sec) const {
  1.1754 +    UBool failed = FALSE;
  1.1755 +    int32_t offsetH, offsetM, offsetS;
  1.1756 +    offsetH = offsetM = offsetS = 0;
  1.1757 +    int32_t idx = start;
  1.1758 +
  1.1759 +    for (int32_t i = 0; i < patternItems->size(); i++) {
  1.1760 +        int32_t len = 0;
  1.1761 +        const GMTOffsetField* field = (const GMTOffsetField*)patternItems->elementAt(i);
  1.1762 +        GMTOffsetField::FieldType fieldType = field->getType();
  1.1763 +        if (fieldType == GMTOffsetField::TEXT) {
  1.1764 +            const UChar* patStr = field->getPatternText();
  1.1765 +            len = u_strlen(patStr);
  1.1766 +            if (text.caseCompare(idx, len, patStr, 0) != 0) {
  1.1767 +                failed = TRUE;
  1.1768 +                break;
  1.1769 +            }
  1.1770 +            idx += len;
  1.1771 +        } else {
  1.1772 +            if (fieldType == GMTOffsetField::HOUR) {
  1.1773 +                uint8_t maxDigits = forceSingleHourDigit ? 1 : 2;
  1.1774 +                offsetH = parseOffsetFieldWithLocalizedDigits(text, idx, 1, maxDigits, 0, MAX_OFFSET_HOUR, len);
  1.1775 +            } else if (fieldType == GMTOffsetField::MINUTE) {
  1.1776 +                offsetM = parseOffsetFieldWithLocalizedDigits(text, idx, 2, 2, 0, MAX_OFFSET_MINUTE, len);
  1.1777 +            } else if (fieldType == GMTOffsetField::SECOND) {
  1.1778 +                offsetS = parseOffsetFieldWithLocalizedDigits(text, idx, 2, 2, 0, MAX_OFFSET_SECOND, len);
  1.1779 +            }
  1.1780 +
  1.1781 +            if (len == 0) {
  1.1782 +                failed = TRUE;
  1.1783 +                break;
  1.1784 +            }
  1.1785 +            idx += len;
  1.1786 +        }
  1.1787 +    }
  1.1788 +
  1.1789 +    if (failed) {
  1.1790 +        hour = min = sec = 0;
  1.1791 +        return 0;
  1.1792 +    }
  1.1793 +
  1.1794 +    hour = offsetH;
  1.1795 +    min = offsetM;
  1.1796 +    sec = offsetS;
  1.1797 +
  1.1798 +    return idx - start;
  1.1799 +}
  1.1800 +
  1.1801 +int32_t
  1.1802 +TimeZoneFormat::parseAbuttingOffsetFields(const UnicodeString& text, int32_t start, int32_t& parsedLen) const {
  1.1803 +    int32_t digits[MAX_OFFSET_DIGITS];
  1.1804 +    int32_t parsed[MAX_OFFSET_DIGITS];  // accumulative offsets
  1.1805 +
  1.1806 +    // Parse digits into int[]
  1.1807 +    int32_t idx = start;
  1.1808 +    int32_t len = 0;
  1.1809 +    int32_t numDigits = 0;
  1.1810 +    for (int32_t i = 0; i < MAX_OFFSET_DIGITS; i++) {
  1.1811 +        digits[i] = parseSingleLocalizedDigit(text, idx, len);
  1.1812 +        if (digits[i] < 0) {
  1.1813 +            break;
  1.1814 +        }
  1.1815 +        idx += len;
  1.1816 +        parsed[i] = idx - start;
  1.1817 +        numDigits++;
  1.1818 +    }
  1.1819 +
  1.1820 +    if (numDigits == 0) {
  1.1821 +        parsedLen = 0;
  1.1822 +        return 0;
  1.1823 +    }
  1.1824 +
  1.1825 +    int32_t offset = 0;
  1.1826 +    while (numDigits > 0) {
  1.1827 +        int32_t hour = 0;
  1.1828 +        int32_t min = 0;
  1.1829 +        int32_t sec = 0;
  1.1830 +
  1.1831 +        U_ASSERT(numDigits > 0 && numDigits <= MAX_OFFSET_DIGITS);
  1.1832 +        switch (numDigits) {
  1.1833 +        case 1: // H
  1.1834 +            hour = digits[0];
  1.1835 +            break;
  1.1836 +        case 2: // HH
  1.1837 +            hour = digits[0] * 10 + digits[1];
  1.1838 +            break;
  1.1839 +        case 3: // Hmm
  1.1840 +            hour = digits[0];
  1.1841 +            min = digits[1] * 10 + digits[2];
  1.1842 +            break;
  1.1843 +        case 4: // HHmm
  1.1844 +            hour = digits[0] * 10 + digits[1];
  1.1845 +            min = digits[2] * 10 + digits[3];
  1.1846 +            break;
  1.1847 +        case 5: // Hmmss
  1.1848 +            hour = digits[0];
  1.1849 +            min = digits[1] * 10 + digits[2];
  1.1850 +            sec = digits[3] * 10 + digits[4];
  1.1851 +            break;
  1.1852 +        case 6: // HHmmss
  1.1853 +            hour = digits[0] * 10 + digits[1];
  1.1854 +            min = digits[2] * 10 + digits[3];
  1.1855 +            sec = digits[4] * 10 + digits[5];
  1.1856 +            break;
  1.1857 +        }
  1.1858 +        if (hour <= MAX_OFFSET_HOUR && min <= MAX_OFFSET_MINUTE && sec <= MAX_OFFSET_SECOND) {
  1.1859 +            // found a valid combination
  1.1860 +            offset = hour * MILLIS_PER_HOUR + min * MILLIS_PER_MINUTE + sec * MILLIS_PER_SECOND;
  1.1861 +            parsedLen = parsed[numDigits - 1];
  1.1862 +            break;
  1.1863 +        }
  1.1864 +        numDigits--;
  1.1865 +    }
  1.1866 +    return offset;
  1.1867 +}
  1.1868 +
  1.1869 +int32_t
  1.1870 +TimeZoneFormat::parseOffsetDefaultLocalizedGMT(const UnicodeString& text, int start, int32_t& parsedLen) const {
  1.1871 +    int32_t idx = start;
  1.1872 +    int32_t offset = 0;
  1.1873 +    int32_t parsed = 0;
  1.1874 +
  1.1875 +    do {
  1.1876 +        // check global default GMT alternatives
  1.1877 +        int32_t gmtLen = 0;
  1.1878 +
  1.1879 +        for (int32_t i = 0; ALT_GMT_STRINGS[i][0] != 0; i++) {
  1.1880 +            const UChar* gmt = ALT_GMT_STRINGS[i];
  1.1881 +            int32_t len = u_strlen(gmt);
  1.1882 +            if (text.caseCompare(start, len, gmt, 0) == 0) {
  1.1883 +                gmtLen = len;
  1.1884 +                break;
  1.1885 +            }
  1.1886 +        }
  1.1887 +        if (gmtLen == 0) {
  1.1888 +            break;
  1.1889 +        }
  1.1890 +        idx += gmtLen;
  1.1891 +
  1.1892 +        // offset needs a sign char and a digit at minimum
  1.1893 +        if (idx + 1 >= text.length()) {
  1.1894 +            break;
  1.1895 +        }
  1.1896 +
  1.1897 +        // parse sign
  1.1898 +        int32_t sign = 1;
  1.1899 +        UChar c = text.charAt(idx);
  1.1900 +        if (c == PLUS) {
  1.1901 +            sign = 1;
  1.1902 +        } else if (c == MINUS) {
  1.1903 +            sign = -1;
  1.1904 +        } else {
  1.1905 +            break;
  1.1906 +        }
  1.1907 +        idx++;
  1.1908 +
  1.1909 +        // offset part
  1.1910 +        // try the default pattern with the separator first
  1.1911 +        int32_t lenWithSep = 0;
  1.1912 +        int32_t offsetWithSep = parseDefaultOffsetFields(text, idx, DEFAULT_GMT_OFFSET_SEP, lenWithSep);
  1.1913 +        if (lenWithSep == text.length() - idx) {
  1.1914 +            // maximum match
  1.1915 +            offset = offsetWithSep * sign;
  1.1916 +            idx += lenWithSep;
  1.1917 +        } else {
  1.1918 +            // try abutting field pattern
  1.1919 +            int32_t lenAbut = 0;
  1.1920 +            int32_t offsetAbut = parseAbuttingOffsetFields(text, idx, lenAbut);
  1.1921 +
  1.1922 +            if (lenWithSep > lenAbut) {
  1.1923 +                offset = offsetWithSep * sign;
  1.1924 +                idx += lenWithSep;
  1.1925 +            } else {
  1.1926 +                offset = offsetAbut * sign;
  1.1927 +                idx += lenAbut;
  1.1928 +            }
  1.1929 +        }
  1.1930 +        parsed = idx - start;
  1.1931 +    } while (false);
  1.1932 +
  1.1933 +    parsedLen = parsed;
  1.1934 +    return offset;
  1.1935 +}
  1.1936 +
  1.1937 +int32_t
  1.1938 +TimeZoneFormat::parseDefaultOffsetFields(const UnicodeString& text, int32_t start, UChar separator, int32_t& parsedLen) const {
  1.1939 +    int32_t max = text.length();
  1.1940 +    int32_t idx = start;
  1.1941 +    int32_t len = 0;
  1.1942 +    int32_t hour = 0, min = 0, sec = 0;
  1.1943 +
  1.1944 +    parsedLen = 0;
  1.1945 +
  1.1946 +    do {
  1.1947 +        hour = parseOffsetFieldWithLocalizedDigits(text, idx, 1, 2, 0, MAX_OFFSET_HOUR, len);
  1.1948 +        if (len == 0) {
  1.1949 +            break;
  1.1950 +        }
  1.1951 +        idx += len;
  1.1952 +
  1.1953 +        if (idx + 1 < max && text.charAt(idx) == separator) {
  1.1954 +            min = parseOffsetFieldWithLocalizedDigits(text, idx + 1, 2, 2, 0, MAX_OFFSET_MINUTE, len);
  1.1955 +            if (len == 0) {
  1.1956 +                break;
  1.1957 +            }
  1.1958 +            idx += (1 + len);
  1.1959 +
  1.1960 +            if (idx + 1 < max && text.charAt(idx) == separator) {
  1.1961 +                sec = parseOffsetFieldWithLocalizedDigits(text, idx + 1, 2, 2, 0, MAX_OFFSET_SECOND, len);
  1.1962 +                if (len == 0) {
  1.1963 +                    break;
  1.1964 +                }
  1.1965 +                idx += (1 + len);
  1.1966 +            }
  1.1967 +        }
  1.1968 +    } while (FALSE);
  1.1969 +
  1.1970 +    if (idx == start) {
  1.1971 +        return 0;
  1.1972 +    }
  1.1973 +
  1.1974 +    parsedLen = idx - start;
  1.1975 +    return hour * MILLIS_PER_HOUR + min * MILLIS_PER_MINUTE + sec * MILLIS_PER_SECOND;
  1.1976 +}
  1.1977 +
  1.1978 +int32_t
  1.1979 +TimeZoneFormat::parseOffsetFieldWithLocalizedDigits(const UnicodeString& text, int32_t start, uint8_t minDigits, uint8_t maxDigits, uint16_t minVal, uint16_t maxVal, int32_t& parsedLen) const {
  1.1980 +    parsedLen = 0;
  1.1981 +
  1.1982 +    int32_t decVal = 0;
  1.1983 +    int32_t numDigits = 0;
  1.1984 +    int32_t idx = start;
  1.1985 +    int32_t digitLen = 0;
  1.1986 +
  1.1987 +    while (idx < text.length() && numDigits < maxDigits) {
  1.1988 +        int32_t digit = parseSingleLocalizedDigit(text, idx, digitLen);
  1.1989 +        if (digit < 0) {
  1.1990 +            break;
  1.1991 +        }
  1.1992 +        int32_t tmpVal = decVal * 10 + digit;
  1.1993 +        if (tmpVal > maxVal) {
  1.1994 +            break;
  1.1995 +        }
  1.1996 +        decVal = tmpVal;
  1.1997 +        numDigits++;
  1.1998 +        idx += digitLen;
  1.1999 +    }
  1.2000 +
  1.2001 +    // Note: maxVal is checked in the while loop
  1.2002 +    if (numDigits < minDigits || decVal < minVal) {
  1.2003 +        decVal = -1;
  1.2004 +        numDigits = 0;
  1.2005 +    } else {
  1.2006 +        parsedLen = idx - start;
  1.2007 +    }
  1.2008 +
  1.2009 +    return decVal;
  1.2010 +}
  1.2011 +
  1.2012 +int32_t
  1.2013 +TimeZoneFormat::parseSingleLocalizedDigit(const UnicodeString& text, int32_t start, int32_t& len) const {
  1.2014 +    int32_t digit = -1;
  1.2015 +    len = 0;
  1.2016 +    if (start < text.length()) {
  1.2017 +        UChar32 cp = text.char32At(start);
  1.2018 +
  1.2019 +        // First, try digits configured for this instance
  1.2020 +        for (int32_t i = 0; i < 10; i++) {
  1.2021 +            if (cp == fGMTOffsetDigits[i]) {
  1.2022 +                digit = i;
  1.2023 +                break;
  1.2024 +            }
  1.2025 +        }
  1.2026 +        // If failed, check if this is a Unicode digit
  1.2027 +        if (digit < 0) {
  1.2028 +            int32_t tmp = u_charDigitValue(cp);
  1.2029 +            digit = (tmp >= 0 && tmp <= 9) ? tmp : -1;
  1.2030 +        }
  1.2031 +
  1.2032 +        if (digit >= 0) {
  1.2033 +            int32_t next = text.moveIndex32(start, 1);
  1.2034 +            len = next - start;
  1.2035 +        }
  1.2036 +    }
  1.2037 +    return digit;
  1.2038 +}
  1.2039 +
  1.2040 +UnicodeString&
  1.2041 +TimeZoneFormat::formatOffsetWithAsciiDigits(int32_t offset, UChar sep, OffsetFields minFields, OffsetFields maxFields, UnicodeString& result) {
  1.2042 +    U_ASSERT(maxFields >= minFields);
  1.2043 +    U_ASSERT(offset > -MAX_OFFSET && offset < MAX_OFFSET);
  1.2044 +
  1.2045 +    UChar sign = PLUS;
  1.2046 +    if (offset < 0) {
  1.2047 +        sign = MINUS;
  1.2048 +        offset = -offset;
  1.2049 +    }
  1.2050 +    result.setTo(sign);
  1.2051 +
  1.2052 +    int fields[3];
  1.2053 +    fields[0] = offset / MILLIS_PER_HOUR;
  1.2054 +    offset = offset % MILLIS_PER_HOUR;
  1.2055 +    fields[1] = offset / MILLIS_PER_MINUTE;
  1.2056 +    offset = offset % MILLIS_PER_MINUTE;
  1.2057 +    fields[2] = offset / MILLIS_PER_SECOND;
  1.2058 +
  1.2059 +    U_ASSERT(fields[0] >= 0 && fields[0] <= MAX_OFFSET_HOUR);
  1.2060 +    U_ASSERT(fields[1] >= 0 && fields[1] <= MAX_OFFSET_MINUTE);
  1.2061 +    U_ASSERT(fields[2] >= 0 && fields[2] <= MAX_OFFSET_SECOND);
  1.2062 +
  1.2063 +    int32_t lastIdx = maxFields;
  1.2064 +    while (lastIdx > minFields) {
  1.2065 +        if (fields[lastIdx] != 0) {
  1.2066 +            break;
  1.2067 +        }
  1.2068 +        lastIdx--;
  1.2069 +    }
  1.2070 +
  1.2071 +    for (int32_t idx = 0; idx <= lastIdx; idx++) {
  1.2072 +        if (sep && idx != 0) {
  1.2073 +            result.append(sep);
  1.2074 +        }
  1.2075 +        result.append((UChar)(0x0030 + fields[idx]/10));
  1.2076 +        result.append((UChar)(0x0030 + fields[idx]%10));
  1.2077 +    }
  1.2078 +
  1.2079 +    return result;
  1.2080 +}
  1.2081 +
  1.2082 +int32_t
  1.2083 +TimeZoneFormat::parseAbuttingAsciiOffsetFields(const UnicodeString& text, ParsePosition& pos, OffsetFields minFields, OffsetFields maxFields, UBool fixedHourWidth) {
  1.2084 +    int32_t start = pos.getIndex();
  1.2085 +
  1.2086 +    int32_t minDigits = 2 * (minFields + 1) - (fixedHourWidth ? 0 : 1);
  1.2087 +    int32_t maxDigits = 2 * (maxFields + 1);
  1.2088 +
  1.2089 +    U_ASSERT(maxDigits <= MAX_OFFSET_DIGITS);
  1.2090 +
  1.2091 +    int32_t digits[MAX_OFFSET_DIGITS] = {};
  1.2092 +    int32_t numDigits = 0;
  1.2093 +    int32_t idx = start;
  1.2094 +    while (numDigits < maxDigits && idx < text.length()) {
  1.2095 +        UChar uch = text.charAt(idx);
  1.2096 +        int32_t digit = DIGIT_VAL(uch);
  1.2097 +        if (digit < 0) {
  1.2098 +            break;
  1.2099 +        }
  1.2100 +        digits[numDigits] = digit;
  1.2101 +        numDigits++;
  1.2102 +        idx++;
  1.2103 +    }
  1.2104 +
  1.2105 +    if (fixedHourWidth && (numDigits & 1)) {
  1.2106 +        // Fixed digits, so the number of digits must be even number. Truncating.
  1.2107 +        numDigits--;
  1.2108 +    }
  1.2109 +
  1.2110 +    if (numDigits < minDigits) {
  1.2111 +        pos.setErrorIndex(start);
  1.2112 +        return 0;
  1.2113 +    }
  1.2114 +
  1.2115 +    int32_t hour = 0, min = 0, sec = 0;
  1.2116 +    UBool bParsed = FALSE;
  1.2117 +    while (numDigits >= minDigits) {
  1.2118 +        switch (numDigits) {
  1.2119 +        case 1: //H
  1.2120 +            hour = digits[0];
  1.2121 +            break;
  1.2122 +        case 2: //HH
  1.2123 +            hour = digits[0] * 10 + digits[1];
  1.2124 +            break;
  1.2125 +        case 3: //Hmm
  1.2126 +            hour = digits[0];
  1.2127 +            min = digits[1] * 10 + digits[2];
  1.2128 +            break;
  1.2129 +        case 4: //HHmm
  1.2130 +            hour = digits[0] * 10 + digits[1];
  1.2131 +            min = digits[2] * 10 + digits[3];
  1.2132 +            break;
  1.2133 +        case 5: //Hmmss
  1.2134 +            hour = digits[0];
  1.2135 +            min = digits[1] * 10 + digits[2];
  1.2136 +            sec = digits[3] * 10 + digits[4];
  1.2137 +            break;
  1.2138 +        case 6: //HHmmss
  1.2139 +            hour = digits[0] * 10 + digits[1];
  1.2140 +            min = digits[2] * 10 + digits[3];
  1.2141 +            sec = digits[4] * 10 + digits[5];
  1.2142 +            break;
  1.2143 +        }
  1.2144 +
  1.2145 +        if (hour <= MAX_OFFSET_HOUR && min <= MAX_OFFSET_MINUTE && sec <= MAX_OFFSET_SECOND) {
  1.2146 +            // Successfully parsed
  1.2147 +            bParsed = true;
  1.2148 +            break;
  1.2149 +        }
  1.2150 +
  1.2151 +        // Truncating
  1.2152 +        numDigits -= (fixedHourWidth ? 2 : 1);
  1.2153 +        hour = min = sec = 0;
  1.2154 +    }
  1.2155 +
  1.2156 +    if (!bParsed) {
  1.2157 +        pos.setErrorIndex(start);
  1.2158 +        return 0;
  1.2159 +    }
  1.2160 +    pos.setIndex(start + numDigits);
  1.2161 +    return ((((hour * 60) + min) * 60) + sec) * 1000;
  1.2162 +}
  1.2163 +
  1.2164 +int32_t
  1.2165 +TimeZoneFormat::parseAsciiOffsetFields(const UnicodeString& text, ParsePosition& pos, UChar sep, OffsetFields minFields, OffsetFields maxFields) {
  1.2166 +    int32_t start = pos.getIndex();
  1.2167 +    int32_t fieldVal[] = {0, 0, 0};
  1.2168 +    int32_t fieldLen[] = {0, -1, -1};
  1.2169 +    for (int32_t idx = start, fieldIdx = 0; idx < text.length() && fieldIdx <= maxFields; idx++) {
  1.2170 +        UChar c = text.charAt(idx);
  1.2171 +        if (c == sep) {
  1.2172 +            if (fieldIdx == 0) {
  1.2173 +                if (fieldLen[0] == 0) {
  1.2174 +                    // no hours field
  1.2175 +                    break;
  1.2176 +                }
  1.2177 +                // 1 digit hour, move to next field
  1.2178 +            } else {
  1.2179 +                if (fieldLen[fieldIdx] != -1) {
  1.2180 +                    // premature minute or seconds field
  1.2181 +                    break;
  1.2182 +                }
  1.2183 +                fieldLen[fieldIdx] = 0;
  1.2184 +            }
  1.2185 +            continue;
  1.2186 +        } else if (fieldLen[fieldIdx] == -1) {
  1.2187 +            // no separator after 2 digit field
  1.2188 +            break;
  1.2189 +        }
  1.2190 +        int32_t digit = DIGIT_VAL(c);
  1.2191 +        if (digit < 0) {
  1.2192 +            // not a digit
  1.2193 +            break;
  1.2194 +        }
  1.2195 +        fieldVal[fieldIdx] = fieldVal[fieldIdx] * 10 + digit;
  1.2196 +        fieldLen[fieldIdx]++;
  1.2197 +        if (fieldLen[fieldIdx] >= 2) {
  1.2198 +            // parsed 2 digits, move to next field
  1.2199 +            fieldIdx++;
  1.2200 +        }
  1.2201 +    }
  1.2202 +
  1.2203 +    int32_t offset = 0;
  1.2204 +    int32_t parsedLen = 0;
  1.2205 +    int32_t parsedFields = -1;
  1.2206 +    do {
  1.2207 +        // hour
  1.2208 +        if (fieldLen[0] == 0) {
  1.2209 +            break;
  1.2210 +        }
  1.2211 +        if (fieldVal[0] > MAX_OFFSET_HOUR) {
  1.2212 +            offset = (fieldVal[0] / 10) * MILLIS_PER_HOUR;
  1.2213 +            parsedFields = FIELDS_H;
  1.2214 +            parsedLen = 1;
  1.2215 +            break;
  1.2216 +        }
  1.2217 +        offset = fieldVal[0] * MILLIS_PER_HOUR;
  1.2218 +        parsedLen = fieldLen[0];
  1.2219 +        parsedFields = FIELDS_H;
  1.2220 +
  1.2221 +        // minute
  1.2222 +        if (fieldLen[1] != 2 || fieldVal[1] > MAX_OFFSET_MINUTE) {
  1.2223 +            break;
  1.2224 +        }
  1.2225 +        offset += fieldVal[1] * MILLIS_PER_MINUTE;
  1.2226 +        parsedLen += (1 + fieldLen[1]);
  1.2227 +        parsedFields = FIELDS_HM;
  1.2228 +
  1.2229 +        // second
  1.2230 +        if (fieldLen[2] != 2 || fieldVal[2] > MAX_OFFSET_SECOND) {
  1.2231 +            break;
  1.2232 +        }
  1.2233 +        offset += fieldVal[2] * MILLIS_PER_SECOND;
  1.2234 +        parsedLen += (1 + fieldLen[2]);
  1.2235 +        parsedFields = FIELDS_HMS;
  1.2236 +    } while (false);
  1.2237 +
  1.2238 +    if (parsedFields < minFields) {
  1.2239 +        pos.setErrorIndex(start);
  1.2240 +        return 0;
  1.2241 +    }
  1.2242 +
  1.2243 +    pos.setIndex(start + parsedLen);
  1.2244 +    return offset;
  1.2245 +}
  1.2246 +
  1.2247 +void
  1.2248 +TimeZoneFormat::appendOffsetDigits(UnicodeString& buf, int32_t n, uint8_t minDigits) const {
  1.2249 +    U_ASSERT(n >= 0 && n < 60);
  1.2250 +    int32_t numDigits = n >= 10 ? 2 : 1;
  1.2251 +    for (int32_t i = 0; i < minDigits - numDigits; i++) {
  1.2252 +        buf.append(fGMTOffsetDigits[0]);
  1.2253 +    }
  1.2254 +    if (numDigits == 2) {
  1.2255 +        buf.append(fGMTOffsetDigits[n / 10]);
  1.2256 +    }
  1.2257 +    buf.append(fGMTOffsetDigits[n % 10]);
  1.2258 +}
  1.2259 +
  1.2260 +// ------------------------------------------------------------------
  1.2261 +// Private misc
  1.2262 +void
  1.2263 +TimeZoneFormat::initGMTPattern(const UnicodeString& gmtPattern, UErrorCode& status) {
  1.2264 +    if (U_FAILURE(status)) {
  1.2265 +        return;
  1.2266 +    }
  1.2267 +    // This implementation not perfect, but sufficient practically.
  1.2268 +    int32_t idx = gmtPattern.indexOf(ARG0, ARG0_LEN, 0);
  1.2269 +    if (idx < 0) {
  1.2270 +        status = U_ILLEGAL_ARGUMENT_ERROR;
  1.2271 +        return;
  1.2272 +    }
  1.2273 +    fGMTPattern.setTo(gmtPattern);
  1.2274 +    unquote(gmtPattern.tempSubString(0, idx), fGMTPatternPrefix);
  1.2275 +    unquote(gmtPattern.tempSubString(idx + ARG0_LEN), fGMTPatternSuffix);
  1.2276 +}
  1.2277 +
  1.2278 +UnicodeString&
  1.2279 +TimeZoneFormat::unquote(const UnicodeString& pattern, UnicodeString& result) {
  1.2280 +    if (pattern.indexOf(SINGLEQUOTE) < 0) {
  1.2281 +        result.setTo(pattern);
  1.2282 +        return result;
  1.2283 +    }
  1.2284 +    result.remove();
  1.2285 +    UBool isPrevQuote = FALSE;
  1.2286 +    UBool inQuote = FALSE;
  1.2287 +    for (int32_t i = 0; i < pattern.length(); i++) {
  1.2288 +        UChar c = pattern.charAt(i);
  1.2289 +        if (c == SINGLEQUOTE) {
  1.2290 +            if (isPrevQuote) {
  1.2291 +                result.append(c);
  1.2292 +                isPrevQuote = FALSE;
  1.2293 +            } else {
  1.2294 +                isPrevQuote = TRUE;
  1.2295 +            }
  1.2296 +            inQuote = !inQuote;
  1.2297 +        } else {
  1.2298 +            isPrevQuote = FALSE;
  1.2299 +            result.append(c);
  1.2300 +        }
  1.2301 +    }
  1.2302 +    return result;
  1.2303 +}
  1.2304 +
  1.2305 +UVector*
  1.2306 +TimeZoneFormat::parseOffsetPattern(const UnicodeString& pattern, OffsetFields required, UErrorCode& status) {
  1.2307 +    if (U_FAILURE(status)) {
  1.2308 +        return NULL;
  1.2309 +    }
  1.2310 +    UVector* result = new UVector(deleteGMTOffsetField, NULL, status);
  1.2311 +    if (result == NULL) {
  1.2312 +        status = U_MEMORY_ALLOCATION_ERROR;
  1.2313 +        return NULL;
  1.2314 +    }
  1.2315 +
  1.2316 +    int32_t checkBits = 0;
  1.2317 +    UBool isPrevQuote = FALSE;
  1.2318 +    UBool inQuote = FALSE;
  1.2319 +    UnicodeString text;
  1.2320 +    GMTOffsetField::FieldType itemType = GMTOffsetField::TEXT;
  1.2321 +    int32_t itemLength = 1;
  1.2322 +
  1.2323 +    for (int32_t i = 0; i < pattern.length(); i++) {
  1.2324 +        UChar ch = pattern.charAt(i);
  1.2325 +        if (ch == SINGLEQUOTE) {
  1.2326 +            if (isPrevQuote) {
  1.2327 +                text.append(SINGLEQUOTE);
  1.2328 +                isPrevQuote = FALSE;
  1.2329 +            } else {
  1.2330 +                isPrevQuote = TRUE;
  1.2331 +                if (itemType != GMTOffsetField::TEXT) {
  1.2332 +                    if (GMTOffsetField::isValid(itemType, itemLength)) {
  1.2333 +                        GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, (uint8_t)itemLength, status);
  1.2334 +                        result->addElement(fld, status);
  1.2335 +                        if (U_FAILURE(status)) {
  1.2336 +                            break;
  1.2337 +                        }
  1.2338 +                    } else {
  1.2339 +                        status = U_ILLEGAL_ARGUMENT_ERROR;
  1.2340 +                        break;
  1.2341 +                    }
  1.2342 +                    itemType = GMTOffsetField::TEXT;
  1.2343 +                }
  1.2344 +            }
  1.2345 +            inQuote = !inQuote;
  1.2346 +        } else {
  1.2347 +            isPrevQuote = FALSE;
  1.2348 +            if (inQuote) {
  1.2349 +                text.append(ch);
  1.2350 +            } else {
  1.2351 +                GMTOffsetField::FieldType tmpType = GMTOffsetField::getTypeByLetter(ch);
  1.2352 +                if (tmpType != GMTOffsetField::TEXT) {
  1.2353 +                    // an offset time pattern character
  1.2354 +                    if (tmpType == itemType) {
  1.2355 +                        itemLength++;
  1.2356 +                    } else {
  1.2357 +                        if (itemType == GMTOffsetField::TEXT) {
  1.2358 +                            if (text.length() > 0) {
  1.2359 +                                GMTOffsetField* textfld = GMTOffsetField::createText(text, status);
  1.2360 +                                result->addElement(textfld, status);
  1.2361 +                                if (U_FAILURE(status)) {
  1.2362 +                                    break;
  1.2363 +                                }
  1.2364 +                                text.remove();
  1.2365 +                            }
  1.2366 +                        } else {
  1.2367 +                            if (GMTOffsetField::isValid(itemType, itemLength)) {
  1.2368 +                                GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status);
  1.2369 +                                result->addElement(fld, status);
  1.2370 +                                if (U_FAILURE(status)) {
  1.2371 +                                    break;
  1.2372 +                                }
  1.2373 +                            } else {
  1.2374 +                                status = U_ILLEGAL_ARGUMENT_ERROR;
  1.2375 +                                break;
  1.2376 +                            }
  1.2377 +                        }
  1.2378 +                        itemType = tmpType;
  1.2379 +                        itemLength = 1;
  1.2380 +                        checkBits |= tmpType;
  1.2381 +                    }
  1.2382 +                } else {
  1.2383 +                    // a string literal
  1.2384 +                    if (itemType != GMTOffsetField::TEXT) {
  1.2385 +                        if (GMTOffsetField::isValid(itemType, itemLength)) {
  1.2386 +                            GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status);
  1.2387 +                            result->addElement(fld, status);
  1.2388 +                            if (U_FAILURE(status)) {
  1.2389 +                                break;
  1.2390 +                            }
  1.2391 +                        } else {
  1.2392 +                            status = U_ILLEGAL_ARGUMENT_ERROR;
  1.2393 +                            break;
  1.2394 +                        }
  1.2395 +                        itemType = GMTOffsetField::TEXT;
  1.2396 +                    }
  1.2397 +                    text.append(ch);
  1.2398 +                }
  1.2399 +            }
  1.2400 +        }
  1.2401 +    }
  1.2402 +    // handle last item
  1.2403 +    if (U_SUCCESS(status)) {
  1.2404 +        if (itemType == GMTOffsetField::TEXT) {
  1.2405 +            if (text.length() > 0) {
  1.2406 +                GMTOffsetField* tfld = GMTOffsetField::createText(text, status);
  1.2407 +                result->addElement(tfld, status);
  1.2408 +            }
  1.2409 +        } else {
  1.2410 +            if (GMTOffsetField::isValid(itemType, itemLength)) {
  1.2411 +                GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status);
  1.2412 +                result->addElement(fld, status);
  1.2413 +            } else {
  1.2414 +                status = U_ILLEGAL_ARGUMENT_ERROR;
  1.2415 +            }
  1.2416 +        }
  1.2417 +
  1.2418 +        // Check all required fields are set
  1.2419 +        if (U_SUCCESS(status)) {
  1.2420 +            int32_t reqBits = 0;
  1.2421 +            switch (required) {
  1.2422 +            case FIELDS_H:
  1.2423 +                reqBits = GMTOffsetField::HOUR;
  1.2424 +                break;
  1.2425 +            case FIELDS_HM:
  1.2426 +                reqBits = GMTOffsetField::HOUR | GMTOffsetField::MINUTE;
  1.2427 +                break;
  1.2428 +            case FIELDS_HMS:
  1.2429 +                reqBits = GMTOffsetField::HOUR | GMTOffsetField::MINUTE | GMTOffsetField::SECOND;
  1.2430 +                break;
  1.2431 +            }
  1.2432 +            if (checkBits == reqBits) {
  1.2433 +                // all required fields are set, no extra fields
  1.2434 +                return result;
  1.2435 +            }
  1.2436 +        }
  1.2437 +    }
  1.2438 +
  1.2439 +    // error
  1.2440 +    delete result;
  1.2441 +    return NULL;
  1.2442 +}
  1.2443 +
  1.2444 +UnicodeString&
  1.2445 +TimeZoneFormat::expandOffsetPattern(const UnicodeString& offsetHM, UnicodeString& result, UErrorCode& status) {
  1.2446 +    result.setToBogus();
  1.2447 +    if (U_FAILURE(status)) {
  1.2448 +        return result;
  1.2449 +    }
  1.2450 +    U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN) == 2);
  1.2451 +
  1.2452 +    int32_t idx_mm = offsetHM.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN, 2, 0);
  1.2453 +    if (idx_mm < 0) {
  1.2454 +        // Bad time zone hour pattern data
  1.2455 +        status = U_ILLEGAL_ARGUMENT_ERROR;
  1.2456 +        return result;
  1.2457 +    }
  1.2458 +
  1.2459 +    UnicodeString sep;
  1.2460 +    int32_t idx_H = offsetHM.tempSubString(0, idx_mm).lastIndexOf((UChar)0x0048 /* H */);
  1.2461 +    if (idx_H >= 0) {
  1.2462 +        sep = offsetHM.tempSubString(idx_H + 1, idx_mm - (idx_H + 1));
  1.2463 +    }
  1.2464 +    result.setTo(offsetHM.tempSubString(0, idx_mm + 2));
  1.2465 +    result.append(sep);
  1.2466 +    result.append(DEFAULT_GMT_OFFSET_SECOND_PATTERN, -1);
  1.2467 +    result.append(offsetHM.tempSubString(idx_mm + 2));
  1.2468 +    return result;
  1.2469 +}
  1.2470 +
  1.2471 +UnicodeString&
  1.2472 +TimeZoneFormat::truncateOffsetPattern(const UnicodeString& offsetHM, UnicodeString& result, UErrorCode& status) {
  1.2473 +    result.setToBogus();
  1.2474 +    if (U_FAILURE(status)) {
  1.2475 +        return result;
  1.2476 +    }
  1.2477 +    U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN) == 2);
  1.2478 +
  1.2479 +    int32_t idx_mm = offsetHM.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN, 2, 0);
  1.2480 +    if (idx_mm < 0) {
  1.2481 +        // Bad time zone hour pattern data
  1.2482 +        status = U_ILLEGAL_ARGUMENT_ERROR;
  1.2483 +        return result;
  1.2484 +    }
  1.2485 +    UChar HH[] = {0x0048, 0x0048};
  1.2486 +    int32_t idx_HH = offsetHM.tempSubString(0, idx_mm).lastIndexOf(HH, 2, 0);
  1.2487 +    if (idx_HH >= 0) {
  1.2488 +        return result.setTo(offsetHM.tempSubString(0, idx_HH + 2));
  1.2489 +    }
  1.2490 +    int32_t idx_H = offsetHM.tempSubString(0, idx_mm).lastIndexOf((UChar)0x0048, 0);
  1.2491 +    if (idx_H >= 0) {
  1.2492 +        return result.setTo(offsetHM.tempSubString(0, idx_H + 1));
  1.2493 +    }
  1.2494 +    // Bad time zone hour pattern data
  1.2495 +    status = U_ILLEGAL_ARGUMENT_ERROR;
  1.2496 +    return result;
  1.2497 +}
  1.2498 +
  1.2499 +void
  1.2500 +TimeZoneFormat::initGMTOffsetPatterns(UErrorCode& status) {
  1.2501 +    for (int32_t type = 0; type < UTZFMT_PAT_COUNT; type++) {
  1.2502 +        switch (type) {
  1.2503 +        case UTZFMT_PAT_POSITIVE_H:
  1.2504 +        case UTZFMT_PAT_NEGATIVE_H:
  1.2505 +            fGMTOffsetPatternItems[type] = parseOffsetPattern(fGMTOffsetPatterns[type], FIELDS_H, status);
  1.2506 +            break;
  1.2507 +        case UTZFMT_PAT_POSITIVE_HM:
  1.2508 +        case UTZFMT_PAT_NEGATIVE_HM:
  1.2509 +            fGMTOffsetPatternItems[type] = parseOffsetPattern(fGMTOffsetPatterns[type], FIELDS_HM, status);
  1.2510 +            break;
  1.2511 +        case UTZFMT_PAT_POSITIVE_HMS:
  1.2512 +        case UTZFMT_PAT_NEGATIVE_HMS:
  1.2513 +            fGMTOffsetPatternItems[type] = parseOffsetPattern(fGMTOffsetPatterns[type], FIELDS_HMS, status);
  1.2514 +            break;
  1.2515 +        }
  1.2516 +    }
  1.2517 +    checkAbuttingHoursAndMinutes();
  1.2518 +}
  1.2519 +
  1.2520 +void
  1.2521 +TimeZoneFormat::checkAbuttingHoursAndMinutes() {
  1.2522 +    fAbuttingOffsetHoursAndMinutes= FALSE;
  1.2523 +    for (int32_t type = 0; type < UTZFMT_PAT_COUNT; type++) {
  1.2524 +        UBool afterH = FALSE;
  1.2525 +        UVector *items = fGMTOffsetPatternItems[type];
  1.2526 +        for (int32_t i = 0; i < items->size(); i++) {
  1.2527 +            const GMTOffsetField* item = (GMTOffsetField*)items->elementAt(i);
  1.2528 +            GMTOffsetField::FieldType type = item->getType();
  1.2529 +            if (type != GMTOffsetField::TEXT) {
  1.2530 +                if (afterH) {
  1.2531 +                    fAbuttingOffsetHoursAndMinutes = TRUE;
  1.2532 +                    break;
  1.2533 +                } else if (type == GMTOffsetField::HOUR) {
  1.2534 +                    afterH = TRUE;
  1.2535 +                }
  1.2536 +            } else if (afterH) {
  1.2537 +                break;
  1.2538 +            }
  1.2539 +        }
  1.2540 +        if (fAbuttingOffsetHoursAndMinutes) {
  1.2541 +            break;
  1.2542 +        }
  1.2543 +    }
  1.2544 +}
  1.2545 +
  1.2546 +UBool
  1.2547 +TimeZoneFormat::toCodePoints(const UnicodeString& str, UChar32* codeArray, int32_t size) {
  1.2548 +    int32_t count = str.countChar32();
  1.2549 +    if (count != size) {
  1.2550 +        return FALSE;
  1.2551 +    }
  1.2552 +
  1.2553 +    for (int32_t idx = 0, start = 0; idx < size; idx++) {
  1.2554 +        codeArray[idx] = str.char32At(start);
  1.2555 +        start = str.moveIndex32(start, 1);
  1.2556 +    }
  1.2557 +
  1.2558 +    return TRUE;
  1.2559 +}
  1.2560 +
  1.2561 +TimeZone*
  1.2562 +TimeZoneFormat::createTimeZoneForOffset(int32_t offset) const {
  1.2563 +    if (offset == 0) {
  1.2564 +        // when offset is 0, we should use "Etc/GMT"
  1.2565 +        return TimeZone::createTimeZone(UnicodeString(TZID_GMT));
  1.2566 +    }
  1.2567 +    return ZoneMeta::createCustomTimeZone(offset);
  1.2568 +}
  1.2569 +
  1.2570 +UTimeZoneFormatTimeType
  1.2571 +TimeZoneFormat::getTimeType(UTimeZoneNameType nameType) {
  1.2572 +    switch (nameType) {
  1.2573 +    case UTZNM_LONG_STANDARD:
  1.2574 +    case UTZNM_SHORT_STANDARD:
  1.2575 +        return UTZFMT_TIME_TYPE_STANDARD;
  1.2576 +
  1.2577 +    case UTZNM_LONG_DAYLIGHT:
  1.2578 +    case UTZNM_SHORT_DAYLIGHT:
  1.2579 +        return UTZFMT_TIME_TYPE_DAYLIGHT;
  1.2580 +
  1.2581 +    default:
  1.2582 +        U_ASSERT(FALSE);
  1.2583 +    }
  1.2584 +    return UTZFMT_TIME_TYPE_UNKNOWN;
  1.2585 +}
  1.2586 +
  1.2587 +UnicodeString&
  1.2588 +TimeZoneFormat::getTimeZoneID(const TimeZoneNames::MatchInfoCollection* matches, int32_t idx, UnicodeString& tzID) const {
  1.2589 +    if (!matches->getTimeZoneIDAt(idx, tzID)) {
  1.2590 +        UnicodeString mzID;
  1.2591 +        if (matches->getMetaZoneIDAt(idx, mzID)) {
  1.2592 +            fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, tzID);
  1.2593 +        }
  1.2594 +    }
  1.2595 +    return tzID;
  1.2596 +}
  1.2597 +
  1.2598 +
  1.2599 +class ZoneIdMatchHandler : public TextTrieMapSearchResultHandler {
  1.2600 +public:
  1.2601 +    ZoneIdMatchHandler();
  1.2602 +    virtual ~ZoneIdMatchHandler();
  1.2603 +
  1.2604 +    UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status);
  1.2605 +    const UChar* getID();
  1.2606 +    int32_t getMatchLen();
  1.2607 +private:
  1.2608 +    int32_t fLen;
  1.2609 +    const UChar* fID;
  1.2610 +};
  1.2611 +
  1.2612 +ZoneIdMatchHandler::ZoneIdMatchHandler() 
  1.2613 +: fLen(0), fID(NULL) {
  1.2614 +}
  1.2615 +
  1.2616 +ZoneIdMatchHandler::~ZoneIdMatchHandler() {
  1.2617 +}
  1.2618 +
  1.2619 +UBool
  1.2620 +ZoneIdMatchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
  1.2621 +    if (U_FAILURE(status)) {
  1.2622 +        return FALSE;
  1.2623 +    }
  1.2624 +    if (node->hasValues()) {
  1.2625 +        const UChar* id = (const UChar*)node->getValue(0);
  1.2626 +        if (id != NULL) {
  1.2627 +            if (fLen < matchLength) {
  1.2628 +                fID = id;
  1.2629 +                fLen = matchLength;
  1.2630 +            }
  1.2631 +        }
  1.2632 +    }
  1.2633 +    return TRUE;
  1.2634 +}
  1.2635 +
  1.2636 +const UChar*
  1.2637 +ZoneIdMatchHandler::getID() {
  1.2638 +    return fID;
  1.2639 +}
  1.2640 +
  1.2641 +int32_t
  1.2642 +ZoneIdMatchHandler::getMatchLen() {
  1.2643 +    return fLen;
  1.2644 +}
  1.2645 +
  1.2646 +
  1.2647 +static void U_CALLCONV initZoneIdTrie(UErrorCode &status) {
  1.2648 +    U_ASSERT(gZoneIdTrie == NULL);
  1.2649 +    ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEFORMAT, tzfmt_cleanup);
  1.2650 +    gZoneIdTrie = new TextTrieMap(TRUE, NULL);    // No deleter, because values are pooled by ZoneMeta
  1.2651 +    if (gZoneIdTrie == NULL) {
  1.2652 +        status = U_MEMORY_ALLOCATION_ERROR;
  1.2653 +        return;
  1.2654 +    }
  1.2655 +    StringEnumeration *tzenum = TimeZone::createEnumeration();
  1.2656 +    const UnicodeString *id;
  1.2657 +    while ((id = tzenum->snext(status))) {
  1.2658 +        const UChar* uid = ZoneMeta::findTimeZoneID(*id);
  1.2659 +        if (uid) {
  1.2660 +            gZoneIdTrie->put(uid, const_cast<UChar *>(uid), status);
  1.2661 +        }
  1.2662 +    }
  1.2663 +    delete tzenum;
  1.2664 +}
  1.2665 +
  1.2666 +
  1.2667 +UnicodeString&
  1.2668 +TimeZoneFormat::parseZoneID(const UnicodeString& text, ParsePosition& pos, UnicodeString& tzID) const {
  1.2669 +    UErrorCode status = U_ZERO_ERROR;
  1.2670 +    umtx_initOnce(gZoneIdTrieInitOnce, &initZoneIdTrie, status);
  1.2671 +
  1.2672 +    int32_t start = pos.getIndex();
  1.2673 +    int32_t len = 0;
  1.2674 +    tzID.setToBogus();
  1.2675 +
  1.2676 +    if (U_SUCCESS(status)) {
  1.2677 +        LocalPointer<ZoneIdMatchHandler> handler(new ZoneIdMatchHandler());
  1.2678 +        gZoneIdTrie->search(text, start, handler.getAlias(), status); 
  1.2679 +        len = handler->getMatchLen();
  1.2680 +        if (len > 0) {
  1.2681 +            tzID.setTo(handler->getID(), -1);
  1.2682 +        }
  1.2683 +    }
  1.2684 +
  1.2685 +    if (len > 0) {
  1.2686 +        pos.setIndex(start + len);
  1.2687 +    } else {
  1.2688 +        pos.setErrorIndex(start);
  1.2689 +    }
  1.2690 +
  1.2691 +    return tzID;
  1.2692 +}
  1.2693 +
  1.2694 +static void U_CALLCONV initShortZoneIdTrie(UErrorCode &status) {
  1.2695 +    U_ASSERT(gShortZoneIdTrie == NULL);
  1.2696 +    ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEFORMAT, tzfmt_cleanup);
  1.2697 +    StringEnumeration *tzenum = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
  1.2698 +    if (U_SUCCESS(status)) {
  1.2699 +        gShortZoneIdTrie = new TextTrieMap(TRUE, NULL);    // No deleter, because values are pooled by ZoneMeta
  1.2700 +        if (gShortZoneIdTrie == NULL) {
  1.2701 +            status = U_MEMORY_ALLOCATION_ERROR;
  1.2702 +        } else {
  1.2703 +            const UnicodeString *id;
  1.2704 +            while ((id = tzenum->snext(status))) {
  1.2705 +                const UChar* uID = ZoneMeta::findTimeZoneID(*id);
  1.2706 +                const UChar* shortID = ZoneMeta::getShortID(*id);
  1.2707 +                if (shortID && uID) {
  1.2708 +                    gShortZoneIdTrie->put(shortID, const_cast<UChar *>(uID), status);
  1.2709 +                }
  1.2710 +            }
  1.2711 +        }
  1.2712 +    }
  1.2713 +    delete tzenum;
  1.2714 +}
  1.2715 +
  1.2716 +
  1.2717 +UnicodeString&
  1.2718 +TimeZoneFormat::parseShortZoneID(const UnicodeString& text, ParsePosition& pos, UnicodeString& tzID) const {
  1.2719 +    UErrorCode status = U_ZERO_ERROR;
  1.2720 +    umtx_initOnce(gShortZoneIdTrieInitOnce, &initShortZoneIdTrie, status);
  1.2721 +
  1.2722 +    int32_t start = pos.getIndex();
  1.2723 +    int32_t len = 0;
  1.2724 +    tzID.setToBogus();
  1.2725 +
  1.2726 +    if (U_SUCCESS(status)) {
  1.2727 +        LocalPointer<ZoneIdMatchHandler> handler(new ZoneIdMatchHandler());
  1.2728 +        gShortZoneIdTrie->search(text, start, handler.getAlias(), status); 
  1.2729 +        len = handler->getMatchLen();
  1.2730 +        if (len > 0) {
  1.2731 +            tzID.setTo(handler->getID(), -1);
  1.2732 +        }
  1.2733 +    }
  1.2734 +
  1.2735 +    if (len > 0) {
  1.2736 +        pos.setIndex(start + len);
  1.2737 +    } else {
  1.2738 +        pos.setErrorIndex(start);
  1.2739 +    }
  1.2740 +
  1.2741 +    return tzID;
  1.2742 +}
  1.2743 +
  1.2744 +
  1.2745 +UnicodeString&
  1.2746 +TimeZoneFormat::parseExemplarLocation(const UnicodeString& text, ParsePosition& pos, UnicodeString& tzID) const {
  1.2747 +    int32_t startIdx = pos.getIndex();
  1.2748 +    int32_t parsedPos = -1;
  1.2749 +    tzID.setToBogus();
  1.2750 +
  1.2751 +    UErrorCode status = U_ZERO_ERROR;
  1.2752 +    LocalPointer<TimeZoneNames::MatchInfoCollection> exemplarMatches(fTimeZoneNames->find(text, startIdx, UTZNM_EXEMPLAR_LOCATION, status));
  1.2753 +    if (U_FAILURE(status)) {
  1.2754 +        pos.setErrorIndex(startIdx);
  1.2755 +        return tzID;
  1.2756 +    }
  1.2757 +    int32_t matchIdx = -1;
  1.2758 +    if (!exemplarMatches.isNull()) {
  1.2759 +        for (int32_t i = 0; i < exemplarMatches->size(); i++) {
  1.2760 +            if (startIdx + exemplarMatches->getMatchLengthAt(i) > parsedPos) {
  1.2761 +                matchIdx = i;
  1.2762 +                parsedPos = startIdx + exemplarMatches->getMatchLengthAt(i);
  1.2763 +            }
  1.2764 +        }
  1.2765 +        if (parsedPos > 0) {
  1.2766 +            pos.setIndex(parsedPos);
  1.2767 +            getTimeZoneID(exemplarMatches.getAlias(), matchIdx, tzID);
  1.2768 +        }
  1.2769 +    }
  1.2770 +
  1.2771 +    if (tzID.length() == 0) {
  1.2772 +        pos.setErrorIndex(startIdx);
  1.2773 +    }
  1.2774 +
  1.2775 +    return tzID;
  1.2776 +}
  1.2777 +
  1.2778 +U_NAMESPACE_END
  1.2779 +
  1.2780 +#endif

mercurial