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