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