1/*
2*******************************************************************************
3* Copyright (C) 2011-2012, 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
29U_NAMESPACE_BEGIN
30
31static const char gZoneStringsTag[] = "zoneStrings";
32static const char gGmtFormatTag[]= "gmtFormat";
33static const char gGmtZeroFormatTag[] = "gmtZeroFormat";
34static const char gHourFormatTag[]= "hourFormat";
35
36static const UChar TZID_GMT[] = {0x0045, 0x0074, 0x0063, 0x002F, 0x0047, 0x004D, 0x0054, 0};    // Etc/GMT
37
38static const UChar DEFAULT_GMT_PATTERN[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0}; // GMT{0}
39//static const UChar DEFAULT_GMT_ZERO[] = {0x0047, 0x004D, 0x0054, 0}; // GMT
40static const UChar DEFAULT_GMT_POSITIVE_HM[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // +HH:mm
41static const UChar DEFAULT_GMT_POSITIVE_HMS[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // +HH:mm:ss
42static const UChar DEFAULT_GMT_NEGATIVE_HM[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // -HH:mm
43static const UChar DEFAULT_GMT_NEGATIVE_HMS[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // -HH:mm:ss
44
45static const UChar32 DEFAULT_GMT_DIGITS[] = {
46    0x0030, 0x0031, 0x0032, 0x0033, 0x0034,
47    0x0035, 0x0036, 0x0037, 0x0038, 0x0039
48};
49
50static const UChar DEFAULT_GMT_OFFSET_SEP = 0x003A; // ':'
51
52static const UChar ARG0[] = {0x007B, 0x0030, 0x007D};   // "{0}"
53static const int ARG0_LEN = 3;
54
55static const UChar DEFAULT_GMT_OFFSET_MINUTE_PATTERN[] = {0x006D, 0x006D, 0};   // "mm"
56static const UChar DEFAULT_GMT_OFFSET_SECOND_PATTERN[] = {0x0073, 0x0073, 0};   // "ss"
57
58static const UChar ALT_GMT_STRINGS[][4] = {
59    {0x0047, 0x004D, 0x0054, 0},    // GMT
60    {0x0055, 0x0054, 0x0043, 0},    // UTC
61    {0x0055, 0x0054, 0, 0},         // UT
62    {0, 0, 0, 0}
63};
64
65// Order of GMT offset pattern parsing, *_HMS must be evaluated first
66// because *_HM is most likely a substring of *_HMS
67static const int32_t PARSE_GMT_OFFSET_TYPES[] = {
68    UTZFMT_PAT_POSITIVE_HMS,
69    UTZFMT_PAT_NEGATIVE_HMS,
70    UTZFMT_PAT_POSITIVE_HM,
71    UTZFMT_PAT_NEGATIVE_HM,
72    -1
73};
74
75static const UChar SINGLEQUOTE  = 0x0027;
76static const UChar PLUS         = 0x002B;
77static const UChar MINUS        = 0x002D;
78static const UChar ISO8601_UTC  = 0x005A;   // 'Z'
79static const UChar ISO8601_SEP  = 0x003A;   // ':'
80
81static const int32_t MILLIS_PER_HOUR = 60 * 60 * 1000;
82static const int32_t MILLIS_PER_MINUTE = 60 * 1000;
83static const int32_t MILLIS_PER_SECOND = 1000;
84
85// Maximum offset (exclusive) in millisecond supported by offset formats
86static int32_t MAX_OFFSET = 24 * MILLIS_PER_HOUR;
87
88// Maximum values for GMT offset fields
89static const int32_t MAX_OFFSET_HOUR = 23;
90static const int32_t MAX_OFFSET_MINUTE = 59;
91static const int32_t MAX_OFFSET_SECOND = 59;
92
93static const int32_t UNKNOWN_OFFSET = 0x7FFFFFFF;
94
95static const int32_t ALL_SPECIFIC_NAME_TYPES = UTZNM_LONG_STANDARD | UTZNM_LONG_DAYLIGHT | UTZNM_SHORT_STANDARD | UTZNM_SHORT_DAYLIGHT;
96static const int32_t ALL_GENERIC_NAME_TYPES = UTZGNM_LOCATION | UTZGNM_LONG | UTZGNM_SHORT;
97
98#define STYLE_FLAG(c) (1 << (c))
99#define DIGIT_VAL(c) (0x0030 <= (c) && (c) <= 0x0039 ? (c) - 0x0030 : -1)
100#define MAX_OFFSET_DIGITS 6
101
102
103// ------------------------------------------------------------------
104// GMTOffsetField
105//
106// This class represents a localized GMT offset pattern
107// item and used by TimeZoneFormat
108// ------------------------------------------------------------------
109class GMTOffsetField : public UMemory {
110public:
111    enum FieldType {
112        TEXT = 0,
113        HOUR = 1,
114        MINUTE = 2,
115        SECOND = 4
116    };
117
118    virtual ~GMTOffsetField();
119
120    static GMTOffsetField* createText(const UnicodeString& text, UErrorCode& status);
121    static GMTOffsetField* createTimeField(FieldType type, uint8_t width, UErrorCode& status);
122    static UBool isValid(FieldType type, int32_t width);
123    static FieldType getTypeByLetter(UChar ch);
124
125    FieldType getType() const;
126    uint8_t getWidth() const;
127    const UChar* getPatternText(void) const;
128
129private:
130    UChar* fText;
131    FieldType fType;
132    uint8_t fWidth;
133
134    GMTOffsetField();
135};
136
137GMTOffsetField::GMTOffsetField()
138: fText(NULL), fType(TEXT), fWidth(0) {
139}
140
141GMTOffsetField::~GMTOffsetField() {
142    if (fText) {
143        uprv_free(fText);
144    }
145}
146
147GMTOffsetField*
148GMTOffsetField::createText(const UnicodeString& text, UErrorCode& status) {
149    if (U_FAILURE(status)) {
150        return NULL;
151    }
152    GMTOffsetField* result = new GMTOffsetField();
153    if (result == NULL) {
154        status = U_MEMORY_ALLOCATION_ERROR;
155        return NULL;
156    }
157
158    int32_t len = text.length();
159    result->fText = (UChar*)uprv_malloc((len + 1) * sizeof(UChar));
160    if (result->fText == NULL) {
161        status = U_MEMORY_ALLOCATION_ERROR;
162        delete result;
163        return NULL;
164    }
165    u_strncpy(result->fText, text.getBuffer(), len);
166    result->fText[len] = 0;
167    result->fType = TEXT;
168
169    return result;
170}
171
172GMTOffsetField*
173GMTOffsetField::createTimeField(FieldType type, uint8_t width, UErrorCode& status) {
174    U_ASSERT(type != TEXT);
175    if (U_FAILURE(status)) {
176        return NULL;
177    }
178    GMTOffsetField* result = new GMTOffsetField();
179    if (result == NULL) {
180        status = U_MEMORY_ALLOCATION_ERROR;
181        return NULL;
182    }
183
184    result->fType = type;
185    result->fWidth = width;
186
187    return result;
188}
189
190UBool
191GMTOffsetField::isValid(FieldType type, int32_t width) {
192    switch (type) {
193    case HOUR:
194        return (width == 1 || width == 2);
195    case MINUTE:
196    case SECOND:
197        return (width == 2);
198    default:
199        U_ASSERT(FALSE);
200    }
201    return (width > 0);
202}
203
204GMTOffsetField::FieldType
205GMTOffsetField::getTypeByLetter(UChar ch) {
206    if (ch == 0x0048 /* H */) {
207        return HOUR;
208    } else if (ch == 0x006D /* m */) {
209        return MINUTE;
210    } else if (ch == 0x0073 /* s */) {
211        return SECOND;
212    }
213    return TEXT;
214}
215
216inline GMTOffsetField::FieldType
217GMTOffsetField::getType() const {
218     return fType;
219 }
220
221inline uint8_t
222GMTOffsetField::getWidth() const {
223    return fWidth;
224}
225
226inline const UChar*
227GMTOffsetField::getPatternText(void) const {
228    return fText;
229}
230
231
232U_CDECL_BEGIN
233static void U_CALLCONV
234deleteGMTOffsetField(void *obj) {
235    delete static_cast<GMTOffsetField *>(obj);
236}
237U_CDECL_END
238
239
240// ------------------------------------------------------------------
241// TimeZoneFormat
242// ------------------------------------------------------------------
243UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeZoneFormat)
244
245TimeZoneFormat::TimeZoneFormat(const Locale& locale, UErrorCode& status)
246: fLocale(locale), fTimeZoneNames(NULL), fTimeZoneGenericNames(NULL), fDefParseOptionFlags(0) {
247
248    for (int32_t i = 0; i <= UTZFMT_PAT_NEGATIVE_HMS; i++) {
249        fGMTOffsetPatternItems[i] = NULL;
250    }
251
252    const char* region = fLocale.getCountry();
253    int32_t regionLen = uprv_strlen(region);
254    if (regionLen == 0) {
255        char loc[ULOC_FULLNAME_CAPACITY];
256        uloc_addLikelySubtags(fLocale.getName(), loc, sizeof(loc), &status);
257
258        regionLen = uloc_getCountry(loc, fTargetRegion, sizeof(fTargetRegion), &status);
259        if (U_SUCCESS(status)) {
260            fTargetRegion[regionLen] = 0;
261        } else {
262            return;
263        }
264    } else if (regionLen < (int32_t)sizeof(fTargetRegion)) {
265        uprv_strcpy(fTargetRegion, region);
266    } else {
267        fTargetRegion[0] = 0;
268    }
269
270    fTimeZoneNames = TimeZoneNames::createInstance(locale, status);
271    // fTimeZoneGenericNames is lazily instantiated
272
273    const UChar* gmtPattern = NULL;
274    const UChar* hourFormats = NULL;
275
276    UResourceBundle *zoneBundle = ures_open(U_ICUDATA_ZONE, locale.getName(), &status);
277    UResourceBundle *zoneStringsArray = ures_getByKeyWithFallback(zoneBundle, gZoneStringsTag, NULL, &status);
278    if (U_SUCCESS(status)) {
279        const UChar* resStr;
280        int32_t len;
281        resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gGmtFormatTag, &len, &status);
282        if (len > 0) {
283            gmtPattern = resStr;
284        }
285        resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gGmtZeroFormatTag, &len, &status);
286        if (len > 0) {
287            fGMTZeroFormat.setTo(TRUE, resStr, len);
288        }
289        resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gHourFormatTag, &len, &status);
290        if (len > 0) {
291            hourFormats = resStr;
292        }
293        ures_close(zoneStringsArray);
294        ures_close(zoneBundle);
295    }
296
297    if (gmtPattern == NULL) {
298        gmtPattern = DEFAULT_GMT_PATTERN;
299    }
300    initGMTPattern(UnicodeString(gmtPattern, -1), status);
301
302    UBool useDefHourFmt = TRUE;
303    if (hourFormats) {
304        UChar *sep = u_strchr(hourFormats, (UChar)0x003B /* ';' */);
305        if (sep != NULL) {
306            fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM].setTo(FALSE, hourFormats, (int32_t)(sep - hourFormats));
307            fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM].setTo(TRUE, sep + 1, -1);
308            expandOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HMS]);
309            expandOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HMS]);
310            useDefHourFmt = FALSE;
311        }
312    }
313    if (useDefHourFmt) {
314        fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM].setTo(TRUE, DEFAULT_GMT_POSITIVE_HM, -1);
315        fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HMS].setTo(TRUE, DEFAULT_GMT_POSITIVE_HMS, -1);
316        fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM].setTo(TRUE, DEFAULT_GMT_NEGATIVE_HM, -1);
317        fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HMS].setTo(TRUE, DEFAULT_GMT_NEGATIVE_HMS, -1);
318    }
319    initGMTOffsetPatterns(status);
320
321    NumberingSystem* ns = NumberingSystem::createInstance(locale, status);
322    UBool useDefDigits = TRUE;
323    if (ns && !ns->isAlgorithmic()) {
324        UnicodeString digits = ns->getDescription();
325        useDefDigits = !toCodePoints(digits, fGMTOffsetDigits, 10);
326    }
327    if (useDefDigits) {
328        uprv_memcpy(fGMTOffsetDigits, DEFAULT_GMT_DIGITS, sizeof(UChar32) * 10);
329    }
330    delete ns;
331}
332
333TimeZoneFormat::TimeZoneFormat(const TimeZoneFormat& other)
334: Format(other), fTimeZoneNames(NULL), fTimeZoneGenericNames(NULL) {
335
336    for (int32_t i = 0; i <= UTZFMT_PAT_NEGATIVE_HMS; i++) {
337        fGMTOffsetPatternItems[i] = NULL;
338    }
339    *this = other;
340}
341
342
343TimeZoneFormat::~TimeZoneFormat() {
344    delete fTimeZoneNames;
345    delete fTimeZoneGenericNames;
346    for (int32_t i = 0; i <= UTZFMT_PAT_NEGATIVE_HMS; i++) {
347        delete fGMTOffsetPatternItems[i];
348    }
349}
350
351TimeZoneFormat&
352TimeZoneFormat::operator=(const TimeZoneFormat& other) {
353    if (this == &other) {
354        return *this;
355    }
356
357    delete fTimeZoneNames;
358    delete fTimeZoneGenericNames;
359    fTimeZoneGenericNames = NULL;
360
361    fLocale = other.fLocale;
362    uprv_memcpy(fTargetRegion, other.fTargetRegion, sizeof(fTargetRegion));
363
364    fTimeZoneNames = other.fTimeZoneNames->clone();
365    if (other.fTimeZoneGenericNames) {
366        fTimeZoneGenericNames = other.fTimeZoneGenericNames->clone();
367    }
368
369    fGMTPattern = other.fGMTPattern;
370    fGMTPatternPrefix = other.fGMTPatternPrefix;
371    fGMTPatternSuffix = other.fGMTPatternSuffix;
372
373    UErrorCode status = U_ZERO_ERROR;
374    for (int32_t i = 0; i <= UTZFMT_PAT_NEGATIVE_HMS; i++) {
375        fGMTOffsetPatterns[i] = other.fGMTOffsetPatterns[i];
376        delete fGMTOffsetPatternItems[i];
377    }
378    initGMTOffsetPatterns(status);
379    U_ASSERT(U_SUCCESS(status));
380
381    fGMTZeroFormat = other.fGMTZeroFormat;
382
383    uprv_memcpy(fGMTOffsetDigits, other.fGMTOffsetDigits, sizeof(fGMTOffsetDigits));
384
385    fDefParseOptionFlags = other.fDefParseOptionFlags;
386
387    return *this;
388}
389
390
391UBool
392TimeZoneFormat::operator==(const Format& other) const {
393    TimeZoneFormat* tzfmt = (TimeZoneFormat*)&other;
394
395    UBool isEqual =
396            fLocale == tzfmt->fLocale
397            && fGMTPattern == tzfmt->fGMTPattern
398            && fGMTZeroFormat == tzfmt->fGMTZeroFormat
399            && *fTimeZoneNames == *tzfmt->fTimeZoneNames;
400
401    for (int32_t i = 0; i <= UTZFMT_PAT_NEGATIVE_HMS && isEqual; i++) {
402        isEqual = fGMTOffsetPatterns[i] == tzfmt->fGMTOffsetPatterns[i];
403    }
404    for (int32_t i = 0; i < 10 && isEqual; i++) {
405        isEqual = fGMTOffsetDigits[i] == tzfmt->fGMTOffsetDigits[i];
406    }
407    // TODO
408    // Check fTimeZoneGenericNames. For now,
409    // if fTimeZoneNames is same, fTimeZoneGenericNames should
410    // be also equivalent.
411    return isEqual;
412}
413
414Format*
415TimeZoneFormat::clone() const {
416    return new TimeZoneFormat(*this);
417}
418
419TimeZoneFormat* U_EXPORT2
420TimeZoneFormat::createInstance(const Locale& locale, UErrorCode& status) {
421    TimeZoneFormat* tzfmt = new TimeZoneFormat(locale, status);
422    if (U_SUCCESS(status)) {
423        return tzfmt;
424    }
425    delete tzfmt;
426    return NULL;
427}
428
429// ------------------------------------------------------------------
430// Setter and Getter
431
432const TimeZoneNames*
433TimeZoneFormat::getTimeZoneNames() const {
434    return (const TimeZoneNames*)fTimeZoneNames;
435}
436
437void
438TimeZoneFormat::adoptTimeZoneNames(TimeZoneNames *tznames) {
439    delete fTimeZoneNames;
440    fTimeZoneNames = tznames;
441
442    // TODO - We should also update fTimeZoneGenericNames
443}
444
445void
446TimeZoneFormat::setTimeZoneNames(const TimeZoneNames &tznames) {
447    delete fTimeZoneNames;
448    fTimeZoneNames = tznames.clone();
449
450    // TODO - We should also update fTimeZoneGenericNames
451}
452
453void
454TimeZoneFormat::setDefaultParseOptions(uint32_t flags) {
455    fDefParseOptionFlags = flags;
456}
457
458uint32_t
459TimeZoneFormat::getDefaultParseOptions(void) const {
460    return fDefParseOptionFlags;
461}
462
463
464UnicodeString&
465TimeZoneFormat::getGMTPattern(UnicodeString& pattern) const {
466    return pattern.setTo(fGMTPattern);
467}
468
469void
470TimeZoneFormat::setGMTPattern(const UnicodeString& pattern, UErrorCode& status) {
471    initGMTPattern(pattern, status);
472}
473
474UnicodeString&
475TimeZoneFormat::getGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type, UnicodeString& pattern) const {
476    return pattern.setTo(fGMTOffsetPatterns[type]);
477}
478
479void
480TimeZoneFormat::setGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type, const UnicodeString& pattern, UErrorCode& status) {
481    if (U_FAILURE(status)) {
482        return;
483    }
484    if (pattern == fGMTOffsetPatterns[type]) {
485        // No need to reset
486        return;
487    }
488
489    OffsetFields required = (type == UTZFMT_PAT_POSITIVE_HMS || type == UTZFMT_PAT_NEGATIVE_HMS) ? FIELDS_HMS : FIELDS_HM;
490
491    UVector* patternItems = parseOffsetPattern(pattern, required, status);
492    if (patternItems == NULL) {
493        return;
494    }
495
496    fGMTOffsetPatterns[type].setTo(pattern);
497    delete fGMTOffsetPatternItems[type];
498    fGMTOffsetPatternItems[type] = patternItems;
499}
500
501UnicodeString&
502TimeZoneFormat::getGMTOffsetDigits(UnicodeString& digits) const {
503    digits.remove();
504    for (int32_t i = 0; i < 10; i++) {
505        digits.append(fGMTOffsetDigits[i]);
506    }
507    return digits;
508}
509
510void
511TimeZoneFormat::setGMTOffsetDigits(const UnicodeString& digits, UErrorCode& status) {
512    if (U_FAILURE(status)) {
513        return;
514    }
515    UChar32 digitArray[10];
516    if (!toCodePoints(digits, digitArray, 10)) {
517        status = U_ILLEGAL_ARGUMENT_ERROR;
518        return;
519    }
520    uprv_memcpy(fGMTOffsetDigits, digitArray, sizeof(UChar32)*10);
521}
522
523UnicodeString&
524TimeZoneFormat::getGMTZeroFormat(UnicodeString& gmtZeroFormat) const {
525    return gmtZeroFormat.setTo(fGMTZeroFormat);
526}
527
528void
529TimeZoneFormat::setGMTZeroFormat(const UnicodeString& gmtZeroFormat, UErrorCode& status) {
530    if (U_SUCCESS(status)) {
531        if (gmtZeroFormat.isEmpty()) {
532            status = U_ILLEGAL_ARGUMENT_ERROR;
533        } else if (gmtZeroFormat != fGMTZeroFormat) {
534            fGMTZeroFormat.setTo(gmtZeroFormat);
535        }
536    }
537}
538
539// ------------------------------------------------------------------
540// Format and Parse
541
542UnicodeString&
543TimeZoneFormat::format(UTimeZoneFormatStyle style, const TimeZone& tz, UDate date,
544        UnicodeString& name, UTimeZoneFormatTimeType* timeType /* = NULL */) const {
545    if (timeType) {
546        *timeType = UTZFMT_TIME_TYPE_UNKNOWN;
547    }
548    switch (style) {
549    case UTZFMT_STYLE_GENERIC_LOCATION:
550        formatGeneric(tz, UTZGNM_LOCATION, date, name);
551        break;
552    case UTZFMT_STYLE_GENERIC_LONG:
553        formatGeneric(tz, UTZGNM_LONG, date, name);
554        break;
555    case UTZFMT_STYLE_GENERIC_SHORT:
556        formatGeneric(tz, UTZGNM_SHORT, date, name);
557        break;
558    case UTZFMT_STYLE_SPECIFIC_LONG:
559        formatSpecific(tz, UTZNM_LONG_STANDARD, UTZNM_LONG_DAYLIGHT, date, name, timeType);
560        break;
561    case UTZFMT_STYLE_SPECIFIC_SHORT:
562        formatSpecific(tz, UTZNM_SHORT_STANDARD, UTZNM_SHORT_DAYLIGHT, date, name, timeType);
563        break;
564    case UTZFMT_STYLE_RFC822:
565    case UTZFMT_STYLE_ISO8601:
566    case UTZFMT_STYLE_LOCALIZED_GMT:
567        // will be handled below
568        break;
569    }
570
571    if (name.isEmpty()) {
572        UErrorCode status = U_ZERO_ERROR;
573        int32_t rawOffset, dstOffset;
574        tz.getOffset(date, FALSE, rawOffset, dstOffset, status);
575        if (U_SUCCESS(status)) {
576            switch (style) {
577            case UTZFMT_STYLE_RFC822:
578                formatOffsetRFC822(rawOffset + dstOffset, name, status);
579                break;
580            case UTZFMT_STYLE_ISO8601:
581                formatOffsetISO8601(rawOffset + dstOffset, name, status);
582                break;
583            default:
584                formatOffsetLocalizedGMT(rawOffset + dstOffset, name, status);
585                break;
586            }
587            if (timeType) {
588                *timeType = (dstOffset != 0) ? UTZFMT_TIME_TYPE_DAYLIGHT : UTZFMT_TIME_TYPE_STANDARD;
589            }
590        }
591        U_ASSERT(U_SUCCESS(status));
592    }
593
594    return name;
595}
596
597UnicodeString&
598TimeZoneFormat::format(const Formattable& obj, UnicodeString& appendTo,
599        FieldPosition& pos, UErrorCode& status) const {
600    if (U_FAILURE(status)) {
601        return appendTo;
602    }
603    UDate date = Calendar::getNow();
604    if (obj.getType() == Formattable::kObject) {
605        const UObject* formatObj = obj.getObject();
606        const TimeZone* tz = dynamic_cast<const TimeZone*>(formatObj);
607        if (tz == NULL) {
608            const Calendar* cal = dynamic_cast<const Calendar*>(formatObj);
609            if (cal != NULL) {
610                tz = &cal->getTimeZone();
611                date = cal->getTime(status);
612            }
613        }
614        if (tz != NULL) {
615            int32_t rawOffset, dstOffset;
616            tz->getOffset(date, FALSE, rawOffset, dstOffset, status);
617            UnicodeString result;
618            formatOffsetLocalizedGMT(rawOffset + dstOffset, result, status);
619            if (U_SUCCESS(status)) {
620                appendTo.append(result);
621                if (pos.getField() == UDAT_TIMEZONE_FIELD) {
622                    pos.setBeginIndex(0);
623                    pos.setEndIndex(result.length());
624                }
625            }
626        }
627    }
628    return appendTo;
629}
630
631TimeZone*
632TimeZoneFormat::parse(UTimeZoneFormatStyle style, const UnicodeString& text, ParsePosition& pos,
633        UTimeZoneFormatTimeType* timeType /*= NULL*/) const {
634    return parse(style, text, pos, getDefaultParseOptions(), timeType);
635}
636
637TimeZone*
638TimeZoneFormat::parse(UTimeZoneFormatStyle style, const UnicodeString& text, ParsePosition& pos,
639        int32_t parseOptions, UTimeZoneFormatTimeType* timeType /* = NULL */) const {
640    if (timeType) {
641        *timeType = UTZFMT_TIME_TYPE_UNKNOWN;
642    }
643
644    int32_t startIdx = pos.getIndex();
645    int32_t maxPos = text.length();
646    int32_t offset;
647
648    UBool fallbackLocalizedGMT = FALSE;
649    if (style == UTZFMT_STYLE_SPECIFIC_LONG || style == UTZFMT_STYLE_SPECIFIC_SHORT
650        || style == UTZFMT_STYLE_GENERIC_LONG || style == UTZFMT_STYLE_GENERIC_SHORT || style == UTZFMT_STYLE_GENERIC_LOCATION) {
651        // above styles may use localized gmt format as fallback
652        fallbackLocalizedGMT = TRUE;
653    }
654
655    int32_t evaluated = 0;
656    ParsePosition tmpPos(startIdx);
657
658    int32_t parsedOffset = UNKNOWN_OFFSET;  // stores successfully parsed offset for later use
659    int32_t parsedPos = -1;                 // stores successfully parsed offset position for later use
660
661    // Try localized GMT format first if necessary
662    if (fallbackLocalizedGMT) {
663        UBool hasDigitOffset = FALSE;
664        offset = parseOffsetLocalizedGMT(text, tmpPos, &hasDigitOffset);
665        if (tmpPos.getErrorIndex() == -1) {
666            // Even when the input text was successfully parsed as a localized GMT format text,
667            // we may still need to evaluate the specified style if -
668            //   1) GMT zero format was used, and
669            //   2) The input text was not completely processed
670            if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
671                pos.setIndex(tmpPos.getIndex());
672                return createTimeZoneForOffset(offset);
673            }
674            parsedOffset = offset;
675            parsedPos = tmpPos.getIndex();
676        }
677        evaluated |= STYLE_FLAG(UTZFMT_STYLE_LOCALIZED_GMT);
678
679        tmpPos.setIndex(startIdx);
680        tmpPos.setErrorIndex(-1);
681    }
682
683    UErrorCode status = U_ZERO_ERROR;
684    UnicodeString tzID;
685    UTimeZoneFormatTimeType parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
686
687    // Try the specified style
688    switch (style) {
689    case UTZFMT_STYLE_RFC822:
690        {
691            offset = parseOffsetRFC822(text, tmpPos);
692            if (tmpPos.getErrorIndex() == -1) {
693                pos.setIndex(tmpPos.getIndex());
694                return createTimeZoneForOffset(offset);
695            }
696        }
697        break;
698
699    case UTZFMT_STYLE_LOCALIZED_GMT:
700        {
701            offset = parseOffsetLocalizedGMT(text, tmpPos);
702            if (tmpPos.getErrorIndex() == -1) {
703                pos.setIndex(tmpPos.getIndex());
704                return createTimeZoneForOffset(offset);
705            }
706        }
707        break;
708
709    case UTZFMT_STYLE_ISO8601:
710        {
711            offset = parseOffsetISO8601(text, tmpPos);
712            if (tmpPos.getErrorIndex() == -1) {
713                pos.setIndex(tmpPos.getIndex());
714                return createTimeZoneForOffset(offset);
715            }
716            // Note: ISO 8601 parser also support basic format (without ':'),
717            // which is same with RFC 822 format.
718            evaluated |= STYLE_FLAG(UTZFMT_STYLE_RFC822);
719        }
720        break;
721
722    case UTZFMT_STYLE_SPECIFIC_LONG:
723    case UTZFMT_STYLE_SPECIFIC_SHORT:
724        {
725            // Specific styles
726            int32_t nameTypes = 0;
727            if (style == UTZFMT_STYLE_SPECIFIC_LONG) {
728                nameTypes = (UTZNM_LONG_STANDARD | UTZNM_LONG_DAYLIGHT);
729            } else {
730                U_ASSERT(style == UTZFMT_STYLE_SPECIFIC_SHORT);
731                nameTypes = (UTZNM_SHORT_STANDARD | UTZNM_SHORT_DAYLIGHT);
732            }
733            LocalPointer<TimeZoneNames::MatchInfoCollection> specificMatches(fTimeZoneNames->find(text, startIdx, nameTypes, status));
734            if (U_FAILURE(status)) {
735                pos.setErrorIndex(startIdx);
736                return NULL;
737            }
738            if (!specificMatches.isNull()) {
739                int32_t matchIdx = -1;
740                int32_t matchPos = -1;
741                for (int32_t i = 0; i < specificMatches->size(); i++) {
742                    matchPos  = startIdx + specificMatches->getMatchLengthAt(i);
743                    if (matchPos > parsedPos) {
744                        matchIdx = i;
745                        parsedPos = matchPos;
746                    }
747                }
748                if (matchIdx >= 0) {
749                    if (timeType) {
750                        *timeType = getTimeType(specificMatches->getNameTypeAt(matchIdx));
751                    }
752                    pos.setIndex(matchPos);
753                    getTimeZoneID(specificMatches.getAlias(), matchIdx, tzID);
754                    U_ASSERT(!tzID.isEmpty());
755                    return TimeZone::createTimeZone(tzID);
756                }
757            }
758        }
759        break;
760
761    case UTZFMT_STYLE_GENERIC_LONG:
762    case UTZFMT_STYLE_GENERIC_SHORT:
763    case UTZFMT_STYLE_GENERIC_LOCATION:
764        {
765            int32_t genericNameTypes = 0;
766            switch (style) {
767            case UTZFMT_STYLE_GENERIC_LOCATION:
768                genericNameTypes = UTZGNM_LOCATION;
769                break;
770
771            case UTZFMT_STYLE_GENERIC_LONG:
772                genericNameTypes = UTZGNM_LONG | UTZGNM_LOCATION;
773                break;
774
775            case UTZFMT_STYLE_GENERIC_SHORT:
776                genericNameTypes = UTZGNM_SHORT | UTZGNM_LOCATION;
777                break;
778
779            default:
780                U_ASSERT(FALSE);
781            }
782
783            int32_t len = 0;
784            const TimeZoneGenericNames *gnames = getTimeZoneGenericNames(status);
785            if (U_SUCCESS(status)) {
786                len = gnames->findBestMatch(text, startIdx, genericNameTypes, tzID, parsedTimeType, status);
787            }
788            if (U_FAILURE(status)) {
789                pos.setErrorIndex(startIdx);
790                return NULL;
791            }
792            if (len > 0) {
793                // Found a match
794                if (timeType) {
795                    *timeType = parsedTimeType;
796                }
797                pos.setIndex(startIdx + len);
798                U_ASSERT(!tzID.isEmpty());
799                return TimeZone::createTimeZone(tzID);
800            }
801        }
802        break;
803    }
804    evaluated |= STYLE_FLAG(style);
805
806
807    if (parsedPos > startIdx) {
808        // When the specified style is one of SPECIFIC_XXX or GENERIC_XXX, we tried to parse the input
809        // as localized GMT format earlier. If parsedOffset is positive, it means it was successfully
810        // parsed as localized GMT format, but offset digits were not detected (more specifically, GMT
811        // zero format). Then, it tried to find a match within the set of display names, but could not
812        // find a match. At this point, we can safely assume the input text contains the localized
813        // GMT format.
814        U_ASSERT(parsedOffset != UNKNOWN_OFFSET);
815        pos.setIndex(parsedPos);
816        return createTimeZoneForOffset(parsedOffset);
817    }
818
819    // Failed to parse the input text as the time zone format in the specified style.
820    // Check the longest match among other styles below.
821    U_ASSERT(parsedPos < 0);
822    U_ASSERT(parsedOffset == UNKNOWN_OFFSET);
823    tmpPos.setIndex(startIdx);
824    tmpPos.setErrorIndex(-1);
825
826    // ISO 8601
827    if ((evaluated & STYLE_FLAG(UTZFMT_STYLE_ISO8601)) == 0) {
828        UBool hasDigitOffset = FALSE;
829        offset = parseOffsetISO8601(text, tmpPos, FALSE, &hasDigitOffset);
830        if (tmpPos.getErrorIndex() == -1) {
831            if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
832                pos.setIndex(tmpPos.getIndex());
833                return createTimeZoneForOffset(offset);
834            }
835            // Note: When ISO 8601 format contains offset digits, it should not
836            // collide with other formats (except RFC 822, which is compatible with
837            // ISO 8601 basic format). However, ISO 8601 UTC format "Z" (single letter)
838            // may collide with other names. In this case, we need to evaluate other
839            // names.
840            parsedOffset = offset;
841            parsedPos = tmpPos.getIndex();
842            U_ASSERT(parsedPos == startIdx + 1);    // only when "Z" is used
843        }
844        tmpPos.setIndex(startIdx);
845        tmpPos.setErrorIndex(-1);
846    }
847
848    // RFC 822
849    // Note: ISO 8601 parser supports RFC 822 format. So we do not need to parse
850    // it as RFC 822 here. This might be changed in future when we support
851    // strict format option for ISO 8601 or RFC 822.
852
853    //if ((evaluated & STYLE_FLAG(UTZFMT_STYLE_RFC822)) == 0) {
854    //    offset = parseOffsetRFC822(text, tmpPos);
855    //    if (tmpPos.getErrorIndex() == -1) {
856    //        pos.setIndex(tmpPos.getIndex());
857    //        return createTimeZoneForOffset(offset);
858    //    }
859    //    tmpPos.setIndex(startIdx);
860    //    tmpPos.setErrorIndex(-1);
861    //}
862
863    // Localized GMT format
864    if ((evaluated & STYLE_FLAG(UTZFMT_STYLE_LOCALIZED_GMT)) == 0) {
865        UBool hasDigitOffset = FALSE;
866        offset = parseOffsetLocalizedGMT(text, tmpPos, &hasDigitOffset);
867        if (tmpPos.getErrorIndex() == -1) {
868            if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
869                pos.setIndex(tmpPos.getIndex());
870                return createTimeZoneForOffset(offset);
871            }
872            // Evaluate other names - see the comment earlier in this method.
873            parsedOffset = offset;
874            parsedPos = tmpPos.getIndex();
875        }
876    }
877
878    // When ParseOption.ALL_STYLES is available, we also try to look all possible display names.
879    // For example, when style is GENERIC_LONG, "EST" (SPECIFIC_SHORT) is never
880    // used for America/New_York. With parseAllStyles true, this code parses "EST"
881    // as America/New_York.
882
883    // Note: Adding all possible names into the trie used by the implementation is quite heavy operation,
884    // which we want to avoid normally (note that we cache the trie, so this is applicable to the
885    // first time only as long as the cache does not expire).
886    if (parseOptions & UTZFMT_PARSE_OPTION_ALL_STYLES) {
887        // Try all specific names first
888        LocalPointer<TimeZoneNames::MatchInfoCollection> spAllMatches(fTimeZoneNames->find(text, startIdx, ALL_SPECIFIC_NAME_TYPES, status));
889        if (U_FAILURE(status)) {
890            pos.setErrorIndex(startIdx);
891            return NULL;
892        }
893        int32_t spMatchIdx = -1;
894        if (!spAllMatches.isNull()) {
895            int32_t matchPos = -1;
896            for (int32_t i = 0; i < spAllMatches->size(); i++) {
897                matchPos  = startIdx + spAllMatches->getMatchLengthAt(i);
898                if (matchPos > parsedPos) {
899                    spMatchIdx = i;
900                    parsedPos = matchPos;
901                }
902            }
903        }
904        int32_t genMatchLen = -1;
905        if (parsedPos < maxPos) {
906            const TimeZoneGenericNames *gnames = getTimeZoneGenericNames(status);
907            if (U_SUCCESS(status)) {
908                genMatchLen = gnames->findBestMatch(text, startIdx, ALL_GENERIC_NAME_TYPES, tzID, parsedTimeType, status);
909            }
910            if (U_FAILURE(status)) {
911                pos.setErrorIndex(startIdx);
912                return NULL;
913            }
914        }
915        // Pick up better match
916        if (startIdx + genMatchLen > parsedPos) {
917            // use generic name match
918            parsedPos = startIdx + genMatchLen;
919            if (timeType) {
920                *timeType = parsedTimeType;
921            }
922            pos.setIndex(parsedPos);
923            U_ASSERT(!tzID.isEmpty());
924            return TimeZone::createTimeZone(tzID);
925        } else if (spMatchIdx >= 0) {
926            // use specific name match
927            if (timeType) {
928                *timeType = getTimeType(spAllMatches->getNameTypeAt(spMatchIdx));
929            }
930            pos.setIndex(parsedPos);
931            getTimeZoneID(spAllMatches.getAlias(), spMatchIdx, tzID);
932            U_ASSERT(!tzID.isEmpty());
933            return TimeZone::createTimeZone(tzID);
934        }
935    }
936
937    if (parsedPos > startIdx) {
938        // Parsed successfully as one of 'offset' format
939        U_ASSERT(parsedOffset != UNKNOWN_OFFSET);
940        pos.setIndex(parsedPos);
941        return createTimeZoneForOffset(parsedOffset);
942    }
943
944    pos.setErrorIndex(startIdx);
945    return NULL;
946}
947
948void
949TimeZoneFormat::parseObject(const UnicodeString& source, Formattable& result,
950        ParsePosition& parse_pos) const {
951    result.adoptObject(parse(UTZFMT_STYLE_GENERIC_LOCATION, source, parse_pos, UTZFMT_PARSE_OPTION_ALL_STYLES));
952}
953
954
955// ------------------------------------------------------------------
956// Private zone name format/parse implementation
957
958UnicodeString&
959TimeZoneFormat::formatGeneric(const TimeZone& tz, int32_t genType, UDate date, UnicodeString& name) const {
960    UErrorCode status = U_ZERO_ERROR;
961    const TimeZoneGenericNames* gnames = getTimeZoneGenericNames(status);
962    if (U_FAILURE(status)) {
963        name.setToBogus();
964        return name;
965    }
966
967    if (genType == UTZGNM_LOCATION) {
968        const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(tz);
969        if (canonicalID == NULL) {
970            name.setToBogus();
971            return name;
972        }
973        return gnames->getGenericLocationName(UnicodeString(canonicalID), name);
974    }
975    return gnames->getDisplayName(tz, (UTimeZoneGenericNameType)genType, date, name);
976}
977
978UnicodeString&
979TimeZoneFormat::formatSpecific(const TimeZone& tz, UTimeZoneNameType stdType, UTimeZoneNameType dstType,
980        UDate date, UnicodeString& name, UTimeZoneFormatTimeType *timeType) const {
981    if (fTimeZoneNames == NULL) {
982        name.setToBogus();
983        return name;
984    }
985
986    UErrorCode status = U_ZERO_ERROR;
987    UBool isDaylight = tz.inDaylightTime(date, status);
988    const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(tz);
989
990    if (U_FAILURE(status) || canonicalID == NULL) {
991        name.setToBogus();
992        return name;
993    }
994
995    if (isDaylight) {
996        fTimeZoneNames->getDisplayName(UnicodeString(canonicalID), dstType, date, name);
997    } else {
998        fTimeZoneNames->getDisplayName(UnicodeString(canonicalID), stdType, date, name);
999    }
1000
1001    if (timeType && !name.isEmpty()) {
1002        *timeType = isDaylight ? UTZFMT_TIME_TYPE_DAYLIGHT : UTZFMT_TIME_TYPE_STANDARD;
1003    }
1004    return name;
1005}
1006
1007static UMutex gLock = U_MUTEX_INITIALIZER;
1008
1009const TimeZoneGenericNames*
1010TimeZoneFormat::getTimeZoneGenericNames(UErrorCode& status) const {
1011    if (U_FAILURE(status)) {
1012        return NULL;
1013    }
1014
1015    UBool create;
1016    UMTX_CHECK(&gZoneMetaLock, (fTimeZoneGenericNames == NULL), create);
1017    if (create) {
1018        TimeZoneFormat *nonConstThis = const_cast<TimeZoneFormat *>(this);
1019        umtx_lock(&gLock);
1020        {
1021            if (fTimeZoneGenericNames == NULL) {
1022                nonConstThis->fTimeZoneGenericNames = TimeZoneGenericNames::createInstance(fLocale, status);
1023            }
1024        }
1025        umtx_unlock(&gLock);
1026    }
1027
1028    return fTimeZoneGenericNames;
1029}
1030
1031// ------------------------------------------------------------------
1032// Zone offset format and parse
1033
1034UnicodeString&
1035TimeZoneFormat::formatOffsetRFC822(int32_t offset, UnicodeString& result, UErrorCode& status) const {
1036    if (U_FAILURE(status)) {
1037        result.setToBogus();
1038        return result;
1039    }
1040    if (offset <= -MAX_OFFSET || offset >= MAX_OFFSET) {
1041        result.setToBogus();
1042        status = U_ILLEGAL_ARGUMENT_ERROR;
1043        return result;
1044    }
1045
1046    // Note: FIELDS_HMS as maxFields is an ICU extension. RFC822 specification
1047    // defines exactly 4 digits for the offset field in HHss format.
1048    return formatOffsetWithAsciiDigits(offset, 0, FIELDS_HM, FIELDS_HMS, result);
1049}
1050
1051UnicodeString&
1052TimeZoneFormat::formatOffsetISO8601(int32_t offset, UnicodeString& result, UErrorCode& status) const {
1053    if (U_FAILURE(status)) {
1054        result.setToBogus();
1055        return result;
1056    }
1057    if (offset <= -MAX_OFFSET || offset >= MAX_OFFSET) {
1058        result.setToBogus();
1059        status = U_ILLEGAL_ARGUMENT_ERROR;
1060        return result;
1061    }
1062
1063    if (offset == 0) {
1064        result.setTo(ISO8601_UTC);
1065        return result;
1066    }
1067    return formatOffsetWithAsciiDigits(offset, ISO8601_SEP, FIELDS_HM, FIELDS_HMS, result);
1068}
1069
1070UnicodeString&
1071TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset, UnicodeString& result, UErrorCode& status) const {
1072    if (U_FAILURE(status)) {
1073        result.setToBogus();
1074        return result;
1075    }
1076    if (offset <= -MAX_OFFSET || offset >= MAX_OFFSET) {
1077        result.setToBogus();
1078        status = U_ILLEGAL_ARGUMENT_ERROR;
1079        return result;
1080    }
1081
1082    if (offset == 0) {
1083        result.setTo(fGMTZeroFormat);
1084        return result;
1085    }
1086
1087    UBool positive = TRUE;
1088    if (offset < 0) {
1089        offset = -offset;
1090        positive = FALSE;
1091    }
1092
1093    int32_t offsetH = offset / MILLIS_PER_HOUR;
1094    offset = offset % MILLIS_PER_HOUR;
1095    int32_t offsetM = offset / MILLIS_PER_MINUTE;
1096    offset = offset % MILLIS_PER_MINUTE;
1097    int32_t offsetS = offset / MILLIS_PER_SECOND;
1098
1099    U_ASSERT(offsetH <= MAX_OFFSET_HOUR && offsetM <= MAX_OFFSET_MINUTE && offsetS <= MAX_OFFSET_SECOND);
1100
1101    const UVector* offsetPatternItems = NULL;
1102    if (positive) {
1103        offsetPatternItems = (offsetS == 0) ?
1104            fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_HM] :
1105            fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_HMS];
1106    } else {
1107        offsetPatternItems = (offsetS == 0) ?
1108            fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_HM] :
1109            fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_HMS];
1110    }
1111
1112    U_ASSERT(offsetPatternItems != NULL);
1113
1114    // Building the GMT format string
1115    result.setTo(fGMTPatternPrefix);
1116
1117    for (int32_t i = 0; i < offsetPatternItems->size(); i++) {
1118        const GMTOffsetField* item = (GMTOffsetField*)offsetPatternItems->elementAt(i);
1119        GMTOffsetField::FieldType type = item->getType();
1120
1121        switch (type) {
1122        case GMTOffsetField::TEXT:
1123            result.append(item->getPatternText(), -1);
1124            break;
1125
1126        case GMTOffsetField::HOUR:
1127            appendOffsetDigits(result, offsetH, item->getWidth());
1128            break;
1129
1130        case GMTOffsetField::MINUTE:
1131            appendOffsetDigits(result, offsetM, item->getWidth());
1132            break;
1133
1134        case GMTOffsetField::SECOND:
1135            appendOffsetDigits(result, offsetS, item->getWidth());
1136            break;
1137        }
1138    }
1139
1140    result.append(fGMTPatternSuffix);
1141    return result;
1142}
1143
1144int32_t
1145TimeZoneFormat::parseOffsetRFC822(const UnicodeString& text, ParsePosition& pos) const {
1146    int32_t start = pos.getIndex();
1147    if (start >= text.length()) {
1148        pos.setErrorIndex(start);
1149        return 0;
1150    }
1151
1152    int32_t sign = 1;
1153    UChar signChar = text.charAt(start);
1154    if (signChar == PLUS) {
1155        sign = 1;
1156    } else if (signChar == MINUS) {
1157        sign = -1;
1158    } else {
1159        // Not an RFC822 offset string
1160        pos.setErrorIndex(start);
1161        return 0;
1162    }
1163
1164    // Parse digits
1165    pos.setIndex(start + 1);
1166    int32_t offset = parseAbuttingAsciiOffsetFields(text, pos, FIELDS_H, FIELDS_HMS, false);
1167
1168    if (pos.getErrorIndex() != -1) {
1169        pos.setIndex(start);    // reset
1170        pos.setErrorIndex(start);
1171        return 0;
1172    }
1173
1174    return sign * offset;
1175}
1176
1177int32_t
1178TimeZoneFormat::parseOffsetISO8601(const UnicodeString& text, ParsePosition& pos) const {
1179    return parseOffsetISO8601(text, pos, FALSE);
1180}
1181
1182int32_t
1183TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString& text, ParsePosition& pos) const {
1184    return parseOffsetLocalizedGMT(text, pos, NULL);
1185}
1186
1187
1188
1189// ------------------------------------------------------------------
1190// Private zone offset format/parse implementation
1191
1192int32_t
1193TimeZoneFormat::parseOffsetISO8601(const UnicodeString& text, ParsePosition& pos, UBool extendedOnly, UBool* hasDigitOffset /* = NULL */) const {
1194    if (hasDigitOffset) {
1195        *hasDigitOffset = FALSE;
1196    }
1197    int32_t start = pos.getIndex();
1198    if (start >= text.length()) {
1199        pos.setErrorIndex(start);
1200        return 0;
1201    }
1202
1203    UChar firstChar = text.charAt(start);
1204    if (firstChar == ISO8601_UTC || firstChar == (UChar)(ISO8601_UTC + 0x20)) {
1205        // "Z" (or "z") - indicates UTC
1206        pos.setIndex(start + 1);
1207        return 0;
1208    }
1209
1210    int32_t sign = 1;
1211    if (firstChar == PLUS) {
1212        sign = 1;
1213    } else if (firstChar == MINUS) {
1214        sign = -1;
1215    } else {
1216        // Not an ISO 8601 offset string
1217        pos.setErrorIndex(start);
1218        return 0;
1219    }
1220    ParsePosition posOffset(start + 1);
1221    int32_t offset = parseAsciiOffsetFields(text, posOffset, ISO8601_SEP, FIELDS_H, FIELDS_HMS, FALSE);
1222    if (posOffset.getErrorIndex() == -1 && !extendedOnly && (posOffset.getIndex() - start <= 3)) {
1223        // If the text is successfully parsed as extended format with the options above, it can be also parsed
1224        // as basic format. For example, "0230" can be parsed as offset 2:00 (only first digits are valid for
1225        // extended format), but it can be parsed as offset 2:30 with basic format. We use longer result.
1226        ParsePosition posBasic(start + 1);
1227        int32_t tmpOffset = parseAbuttingAsciiOffsetFields(text, posBasic, FIELDS_H, FIELDS_HMS, FALSE);
1228        if (posBasic.getErrorIndex() == -1 && posBasic.getIndex() > posOffset.getIndex()) {
1229            offset = tmpOffset;
1230            posOffset.setIndex(posBasic.getIndex());
1231        }
1232    }
1233
1234    if (posOffset.getErrorIndex() != -1) {
1235        pos.setErrorIndex(start);
1236        return 0;
1237    }
1238
1239    pos.setIndex(posOffset.getIndex());
1240    if (hasDigitOffset) {
1241        *hasDigitOffset = TRUE;
1242    }
1243    return sign * offset;
1244}
1245
1246int32_t
1247TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString& text, ParsePosition& pos, UBool* hasDigitOffset) const {
1248    int32_t start = pos.getIndex();
1249    int32_t idx = start;
1250    UBool parsed = FALSE;
1251    int32_t offset = 0;
1252
1253    if (hasDigitOffset) {
1254        *hasDigitOffset = FALSE;
1255    }
1256
1257    do {
1258        // Prefix part
1259        int32_t len = fGMTPatternPrefix.length();
1260        if (len > 0 && text.caseCompare(idx, len, fGMTPatternPrefix, 0) != 0) {
1261            // prefix match failed
1262            break;
1263        }
1264        idx += len;
1265
1266        // Offset part
1267        offset = parseOffsetFields(text, idx, FALSE, len);
1268        if (len == 0) {
1269            // offset field match failed
1270            break;
1271        }
1272        idx += len;
1273
1274        // Suffix part
1275        len = fGMTPatternSuffix.length();
1276        if (len > 0 && text.caseCompare(idx, len, fGMTPatternSuffix, 0) != 0) {
1277            // no suffix match
1278            break;
1279        }
1280        idx += len;
1281        parsed = TRUE;
1282
1283    } while (false);
1284
1285    if (parsed) {
1286        if (hasDigitOffset) {
1287            *hasDigitOffset = TRUE;
1288        }
1289        pos.setIndex(idx);
1290        return offset;
1291    }
1292
1293    // Try the default patterns
1294    int32_t parsedLength = 0;
1295    offset = parseOffsetDefaultLocalizedGMT(text, start, parsedLength);
1296    if (parsedLength > 0) {
1297        if (hasDigitOffset) {
1298            *hasDigitOffset = TRUE;
1299        }
1300        pos.setIndex(start + parsedLength);
1301        return offset;
1302    }
1303
1304    // Check if this is a GMT zero format
1305    if (text.caseCompare(start, fGMTZeroFormat.length(), fGMTZeroFormat, 0) == 0) {
1306        pos.setIndex(start + fGMTZeroFormat.length());
1307        return 0;
1308    }
1309
1310    // Check if this is a default GMT zero format
1311    for (int32_t i = 0; ALT_GMT_STRINGS[i][0] != 0; i++) {
1312        const UChar* defGMTZero = ALT_GMT_STRINGS[i];
1313        int32_t defGMTZeroLen = u_strlen(defGMTZero);
1314        if (text.caseCompare(start, defGMTZeroLen, defGMTZero, 0) == 0) {
1315            pos.setIndex(start + defGMTZeroLen);
1316            return 0;
1317        }
1318    }
1319
1320    // Nothing matched
1321    pos.setErrorIndex(start);
1322    return 0;
1323}
1324
1325int32_t
1326TimeZoneFormat::parseOffsetFields(const UnicodeString& text, int32_t start, UBool minimumHourWidth, int32_t& parsedLen) const {
1327    int32_t offset = 0;
1328    UBool sawVarHourAndAbuttingField = FALSE;
1329
1330    parsedLen = 0;
1331
1332    for (int32_t patidx = 0; PARSE_GMT_OFFSET_TYPES[patidx] >= 0; patidx++) {
1333        int32_t gmtPatType = PARSE_GMT_OFFSET_TYPES[patidx];
1334        int32_t offsetH = 0, offsetM = 0, offsetS = 0;
1335        int32_t idx = start;
1336        UVector* items = fGMTOffsetPatternItems[gmtPatType];
1337        U_ASSERT(items != NULL);
1338
1339        UBool failed = FALSE;
1340        for (int32_t i = 0; i < items->size(); i++) {
1341            int32_t tmpParsedLen = 0;
1342            const GMTOffsetField* field = (const GMTOffsetField*)items->elementAt(i);
1343            GMTOffsetField::FieldType fieldType = field->getType();
1344            if (fieldType == GMTOffsetField::TEXT) {
1345                const UChar* patStr = field->getPatternText();
1346                tmpParsedLen = u_strlen(patStr);
1347                if (text.caseCompare(idx, tmpParsedLen, patStr, 0) != 0) {
1348                    failed = TRUE;
1349                    break;
1350                }
1351                idx += tmpParsedLen;
1352            } else {
1353                if (fieldType == GMTOffsetField::HOUR) {
1354                    uint8_t minDigits = 1;
1355                    uint8_t maxDigits = minimumHourWidth ? 1 : 2;
1356                    if (!minimumHourWidth && !sawVarHourAndAbuttingField) {
1357                        if (i + 1 < items->size()) {
1358                            const GMTOffsetField* nextField = (const GMTOffsetField*)items->elementAt(i + 1);
1359                            if (nextField->getType() != GMTOffsetField::TEXT) {
1360                                sawVarHourAndAbuttingField = true;
1361                            }
1362                        }
1363                    }
1364                    offsetH = parseOffsetFieldWithLocalizedDigits(text, idx, minDigits, maxDigits, 0, MAX_OFFSET_HOUR, tmpParsedLen);
1365                } else if (fieldType == GMTOffsetField::MINUTE) {
1366                    offsetM = parseOffsetFieldWithLocalizedDigits(text, idx, 2, 2, 0, MAX_OFFSET_MINUTE, tmpParsedLen);
1367                } else if (fieldType == GMTOffsetField::SECOND) {
1368                    offsetS = parseOffsetFieldWithLocalizedDigits(text, idx, 2, 2, 0, MAX_OFFSET_SECOND, tmpParsedLen);
1369                }
1370
1371                if (tmpParsedLen == 0) {
1372                    failed = TRUE;
1373                    break;
1374                }
1375                idx += tmpParsedLen;
1376            }
1377        }
1378        if (!failed) {
1379            int32_t sign = (gmtPatType == UTZFMT_PAT_POSITIVE_HM || gmtPatType == UTZFMT_PAT_POSITIVE_HMS) ? 1 : -1;
1380            offset = ((((offsetH * 60) + offsetM) * 60) + offsetS) * 1000 * sign;
1381            parsedLen = idx - start;
1382            break;
1383        }
1384    }
1385
1386    if (parsedLen == 0 && sawVarHourAndAbuttingField && !minimumHourWidth) {
1387        // When hour field is variable width and another non-literal pattern
1388        // field follows, the parse loop above might eat up the digit from
1389        // the abutting field. For example, with pattern "-Hmm" and input "-100",
1390        // the hour is parsed as -10 and fails to parse minute field.
1391        //
1392        // If this is the case, try parsing the text one more time with the arg
1393        // minimumHourWidth = true
1394        //
1395        // Note: This fallback is not applicable when quitAtHourField is true, because
1396        // the option is designed for supporting the case like "GMT+5". In this case,
1397        // we should get better result for parsing hour digits as much as possible.
1398
1399        return parseOffsetFields(text, start, true, parsedLen);
1400    }
1401
1402    return offset;
1403}
1404
1405int32_t
1406TimeZoneFormat::parseAbuttingOffsetFields(const UnicodeString& text, int32_t start, int32_t& parsedLen) const {
1407    int32_t digits[MAX_OFFSET_DIGITS];
1408    int32_t parsed[MAX_OFFSET_DIGITS];  // accumulative offsets
1409
1410    // Parse digits into int[]
1411    int32_t idx = start;
1412    int32_t len = 0;
1413    int32_t numDigits = 0;
1414    for (int32_t i = 0; i < MAX_OFFSET_DIGITS; i++) {
1415        digits[i] = parseSingleLocalizedDigit(text, idx, len);
1416        if (digits[i] < 0) {
1417            break;
1418        }
1419        idx += len;
1420        parsed[i] = idx - start;
1421        numDigits++;
1422    }
1423
1424    if (numDigits == 0) {
1425        parsedLen = 0;
1426        return 0;
1427    }
1428
1429    int32_t offset = 0;
1430    while (numDigits > 0) {
1431        int32_t hour = 0;
1432        int32_t min = 0;
1433        int32_t sec = 0;
1434
1435        U_ASSERT(numDigits > 0 && numDigits <= MAX_OFFSET_DIGITS);
1436        switch (numDigits) {
1437        case 1: // H
1438            hour = digits[0];
1439            break;
1440        case 2: // HH
1441            hour = digits[0] * 10 + digits[1];
1442            break;
1443        case 3: // Hmm
1444            hour = digits[0];
1445            min = digits[1] * 10 + digits[2];
1446            break;
1447        case 4: // HHmm
1448            hour = digits[0] * 10 + digits[1];
1449            min = digits[2] * 10 + digits[3];
1450            break;
1451        case 5: // Hmmss
1452            hour = digits[0];
1453            min = digits[1] * 10 + digits[2];
1454            sec = digits[3] * 10 + digits[4];
1455            break;
1456        case 6: // HHmmss
1457            hour = digits[0] * 10 + digits[1];
1458            min = digits[2] * 10 + digits[3];
1459            sec = digits[4] * 10 + digits[5];
1460            break;
1461        }
1462        if (hour <= MAX_OFFSET_HOUR && min <= MAX_OFFSET_MINUTE && sec <= MAX_OFFSET_SECOND) {
1463            // found a valid combination
1464            offset = hour * MILLIS_PER_HOUR + min * MILLIS_PER_MINUTE + sec * MILLIS_PER_SECOND;
1465            parsedLen = parsed[numDigits - 1];
1466            break;
1467        }
1468        numDigits--;
1469    }
1470    return offset;
1471}
1472
1473int32_t
1474TimeZoneFormat::parseOffsetDefaultLocalizedGMT(const UnicodeString& text, int start, int32_t& parsedLen) const {
1475    int32_t idx = start;
1476    int32_t offset = 0;
1477    int32_t parsed = 0;
1478
1479    do {
1480        // check global default GMT alternatives
1481        int32_t gmtLen = 0;
1482
1483        for (int32_t i = 0; ALT_GMT_STRINGS[i][0] != 0; i++) {
1484            const UChar* gmt = ALT_GMT_STRINGS[i];
1485            int32_t len = u_strlen(gmt);
1486            if (text.caseCompare(start, len, gmt, 0) == 0) {
1487                gmtLen = len;
1488                break;
1489            }
1490        }
1491        if (gmtLen == 0) {
1492            break;
1493        }
1494        idx += gmtLen;
1495
1496        // offset needs a sign char and a digit at minimum
1497        if (idx + 1 >= text.length()) {
1498            break;
1499        }
1500
1501        // parse sign
1502        int32_t sign = 1;
1503        UChar c = text.charAt(idx);
1504        if (c == PLUS) {
1505            sign = 1;
1506        } else if (c == MINUS) {
1507            sign = -1;
1508        } else {
1509            break;
1510        }
1511        idx++;
1512
1513        // offset part
1514        // try the default pattern with the separator first
1515        int32_t lenWithSep = 0;
1516        int32_t offsetWithSep = parseDefaultOffsetFields(text, idx, DEFAULT_GMT_OFFSET_SEP, lenWithSep);
1517        if (lenWithSep == text.length() - idx) {
1518            // maximum match
1519            offset = offsetWithSep * sign;
1520            idx += lenWithSep;
1521        } else {
1522            // try abutting field pattern
1523            int32_t lenAbut = 0;
1524            int32_t offsetAbut = parseAbuttingOffsetFields(text, idx, lenAbut);
1525
1526            if (lenWithSep > lenAbut) {
1527                offset = offsetWithSep * sign;
1528                idx += lenWithSep;
1529            } else {
1530                offset = offsetAbut * sign;
1531                idx += lenAbut;
1532            }
1533        }
1534        parsed = idx - start;
1535    } while (false);
1536
1537    parsedLen = parsed;
1538    return offset;
1539}
1540
1541int32_t
1542TimeZoneFormat::parseDefaultOffsetFields(const UnicodeString& text, int32_t start, UChar separator, int32_t& parsedLen) const {
1543    int32_t max = text.length();
1544    int32_t idx = start;
1545    int32_t len = 0;
1546    int32_t hour = 0, min = 0, sec = 0;
1547
1548    parsedLen = 0;
1549
1550    do {
1551        hour = parseOffsetFieldWithLocalizedDigits(text, idx, 1, 2, 0, MAX_OFFSET_HOUR, len);
1552        if (len == 0) {
1553            break;
1554        }
1555        idx += len;
1556
1557        if (idx + 1 < max && text.charAt(idx) == separator) {
1558            min = parseOffsetFieldWithLocalizedDigits(text, idx + 1, 2, 2, 0, MAX_OFFSET_MINUTE, len);
1559            if (len == 0) {
1560                break;
1561            }
1562            idx += (1 + len);
1563
1564            if (idx + 1 < max && text.charAt(idx) == separator) {
1565                sec = parseOffsetFieldWithLocalizedDigits(text, idx + 1, 2, 2, 0, MAX_OFFSET_SECOND, len);
1566                if (len == 0) {
1567                    break;
1568                }
1569                idx += (1 + len);
1570            }
1571        }
1572    } while (FALSE);
1573
1574    if (idx == start) {
1575        return 0;
1576    }
1577
1578    parsedLen = idx - start;
1579    return hour * MILLIS_PER_HOUR + min * MILLIS_PER_MINUTE + sec * MILLIS_PER_SECOND;
1580}
1581
1582int32_t
1583TimeZoneFormat::parseOffsetFieldWithLocalizedDigits(const UnicodeString& text, int32_t start, uint8_t minDigits, uint8_t maxDigits, uint16_t minVal, uint16_t maxVal, int32_t& parsedLen) const {
1584    parsedLen = 0;
1585
1586    int32_t decVal = 0;
1587    int32_t numDigits = 0;
1588    int32_t idx = start;
1589    int32_t digitLen = 0;
1590
1591    while (idx < text.length() && numDigits < maxDigits) {
1592        int32_t digit = parseSingleLocalizedDigit(text, idx, digitLen);
1593        if (digit < 0) {
1594            break;
1595        }
1596        int32_t tmpVal = decVal * 10 + digit;
1597        if (tmpVal > maxVal) {
1598            break;
1599        }
1600        decVal = tmpVal;
1601        numDigits++;
1602        idx += digitLen;
1603    }
1604
1605    // Note: maxVal is checked in the while loop
1606    if (numDigits < minDigits || decVal < minVal) {
1607        decVal = -1;
1608        numDigits = 0;
1609    } else {
1610        parsedLen = idx - start;
1611    }
1612
1613    return decVal;
1614}
1615
1616int32_t
1617TimeZoneFormat::parseSingleLocalizedDigit(const UnicodeString& text, int32_t start, int32_t& len) const {
1618    int32_t digit = -1;
1619    len = 0;
1620    if (start < text.length()) {
1621        UChar32 cp = text.char32At(start);
1622
1623        // First, try digits configured for this instance
1624        for (int32_t i = 0; i < 10; i++) {
1625            if (cp == fGMTOffsetDigits[i]) {
1626                digit = i;
1627                break;
1628            }
1629        }
1630        // If failed, check if this is a Unicode digit
1631        if (digit < 0) {
1632            int32_t tmp = u_charDigitValue(cp);
1633            digit = (tmp >= 0 && tmp <= 9) ? tmp : -1;
1634        }
1635
1636        if (digit >= 0) {
1637            int32_t next = text.moveIndex32(start, 1);
1638            len = next - start;
1639        }
1640    }
1641    return digit;
1642}
1643
1644UnicodeString&
1645TimeZoneFormat::formatOffsetWithAsciiDigits(int32_t offset, UChar sep, OffsetFields minFields, OffsetFields maxFields, UnicodeString& result) {
1646    U_ASSERT(maxFields >= minFields);
1647    U_ASSERT(offset > -MAX_OFFSET && offset < MAX_OFFSET);
1648
1649    UChar sign = PLUS;
1650    if (offset < 0) {
1651        sign = MINUS;
1652        offset = -offset;
1653    }
1654    result.setTo(sign);
1655
1656    int fields[3];
1657    fields[0] = offset / MILLIS_PER_HOUR;
1658    offset = offset % MILLIS_PER_HOUR;
1659    fields[1] = offset / MILLIS_PER_MINUTE;
1660    offset = offset % MILLIS_PER_MINUTE;
1661    fields[2] = offset / MILLIS_PER_SECOND;
1662
1663    U_ASSERT(fields[0] >= 0 && fields[0] <= MAX_OFFSET_HOUR);
1664    U_ASSERT(fields[1] >= 0 && fields[1] <= MAX_OFFSET_MINUTE);
1665    U_ASSERT(fields[2] >= 0 && fields[2] <= MAX_OFFSET_SECOND);
1666
1667    int32_t lastIdx = maxFields;
1668    while (lastIdx > minFields) {
1669        if (fields[lastIdx] != 0) {
1670            break;
1671        }
1672        lastIdx--;
1673    }
1674
1675    for (int32_t idx = 0; idx <= lastIdx; idx++) {
1676        if (sep && idx != 0) {
1677            result.append(sep);
1678        }
1679        result.append((UChar)(0x0030 + fields[idx]/10));
1680        result.append((UChar)(0x0030 + fields[idx]%10));
1681    }
1682
1683    return result;
1684}
1685
1686int32_t
1687TimeZoneFormat::parseAbuttingAsciiOffsetFields(const UnicodeString& text, ParsePosition& pos, OffsetFields minFields, OffsetFields maxFields, UBool fixedHourWidth) {
1688    int32_t start = pos.getIndex();
1689
1690    int32_t minDigits = 2 * (minFields + 1) - (fixedHourWidth ? 0 : 1);
1691    int32_t maxDigits = 2 * (maxFields + 1);
1692
1693    U_ASSERT(maxDigits <= MAX_OFFSET_DIGITS);
1694
1695    int32_t digits[MAX_OFFSET_DIGITS] = {};
1696    int32_t numDigits = 0;
1697    int32_t idx = start;
1698    while (numDigits < maxDigits && idx < text.length()) {
1699        UChar uch = text.charAt(idx);
1700        int32_t digit = DIGIT_VAL(uch);
1701        if (digit < 0) {
1702            break;
1703        }
1704        digits[numDigits] = digit;
1705        numDigits++;
1706        idx++;
1707    }
1708
1709    if (fixedHourWidth && (numDigits & 1)) {
1710        // Fixed digits, so the number of digits must be even number. Truncating.
1711        numDigits--;
1712    }
1713
1714    if (numDigits < minDigits) {
1715        pos.setErrorIndex(start);
1716        return 0;
1717    }
1718
1719    int32_t hour = 0, min = 0, sec = 0;
1720    UBool bParsed = FALSE;
1721    while (numDigits >= minDigits) {
1722        switch (numDigits) {
1723        case 1: //H
1724            hour = digits[0];
1725            break;
1726        case 2: //HH
1727            hour = digits[0] * 10 + digits[1];
1728            break;
1729        case 3: //Hmm
1730            hour = digits[0];
1731            min = digits[1] * 10 + digits[2];
1732            break;
1733        case 4: //HHmm
1734            hour = digits[0] * 10 + digits[1];
1735            min = digits[2] * 10 + digits[3];
1736            break;
1737        case 5: //Hmmss
1738            hour = digits[0];
1739            min = digits[1] * 10 + digits[2];
1740            sec = digits[3] * 10 + digits[4];
1741            break;
1742        case 6: //HHmmss
1743            hour = digits[0] * 10 + digits[1];
1744            min = digits[2] * 10 + digits[3];
1745            sec = digits[4] * 10 + digits[5];
1746            break;
1747        }
1748
1749        if (hour <= MAX_OFFSET_HOUR && min <= MAX_OFFSET_MINUTE && sec <= MAX_OFFSET_SECOND) {
1750            // Successfully parsed
1751            bParsed = true;
1752            break;
1753        }
1754
1755        // Truncating
1756        numDigits -= (fixedHourWidth ? 2 : 1);
1757        hour = min = sec = 0;
1758    }
1759
1760    if (!bParsed) {
1761        pos.setErrorIndex(start);
1762        return 0;
1763    }
1764    pos.setIndex(start + numDigits);
1765    return ((((hour * 60) + min) * 60) + sec) * 1000;
1766}
1767
1768int32_t
1769TimeZoneFormat::parseAsciiOffsetFields(const UnicodeString& text, ParsePosition& pos, UChar sep, OffsetFields minFields, OffsetFields maxFields, UBool fixedHourWidth) {
1770    int32_t start = pos.getIndex();
1771    int32_t fieldVal[] = {0, 0, 0};
1772    int32_t fieldLen[] = {0, -1, -1};
1773    for (int32_t idx = start, fieldIdx = 0; idx < text.length() && fieldIdx <= maxFields; idx++) {
1774        UChar c = text.charAt(idx);
1775        if (c == sep) {
1776            if (fieldLen[fieldIdx] < 0) {
1777                // next field - expected
1778                fieldLen[fieldIdx] = 0;
1779            } else if (fieldIdx == 0 && !fixedHourWidth) {
1780                // 1 digit hour, move to next field
1781                fieldIdx++;
1782                fieldLen[fieldIdx] = 0;
1783            } else {
1784                // otherwise, premature field
1785                break;
1786            }
1787            continue;
1788        }
1789        int32_t digit = DIGIT_VAL(c);
1790        if (digit < 0) {
1791            // not a digit
1792            break;
1793        }
1794        fieldVal[fieldIdx] = fieldVal[fieldIdx] * 10 + digit;
1795        fieldLen[fieldIdx]++;
1796        if (fieldLen[fieldIdx] >= 2) {
1797            // parsed 2 digits, move to next field
1798            fieldIdx++;
1799        }
1800    }
1801
1802    int32_t offset = 0;
1803    int32_t parsedLen = 0;
1804    int32_t parsedFields = -1;
1805    do {
1806        // hour
1807        if (fieldLen[0] == 0 || (fieldLen[0] == 1 && fixedHourWidth)) {
1808            break;
1809        }
1810        if (fieldVal[0] > MAX_OFFSET_HOUR) {
1811            if (fixedHourWidth) {
1812                break;
1813            }
1814            offset = (fieldVal[0] / 10) * MILLIS_PER_HOUR;
1815            parsedFields = FIELDS_H;
1816            parsedLen = 1;
1817            break;
1818        }
1819        offset = fieldVal[0] * MILLIS_PER_HOUR;
1820        parsedLen = fieldLen[0];
1821        parsedFields = FIELDS_H;
1822
1823        // minute
1824        if (fieldLen[1] != 2 || fieldVal[1] > MAX_OFFSET_MINUTE) {
1825            break;
1826        }
1827        offset += fieldVal[1] * MILLIS_PER_MINUTE;
1828        parsedLen += (1 + fieldLen[1]);
1829        parsedFields = FIELDS_HM;
1830
1831        // second
1832        if (fieldLen[2] != 2 || fieldVal[2] > MAX_OFFSET_SECOND) {
1833            break;
1834        }
1835        offset += fieldVal[2] * MILLIS_PER_SECOND;
1836        parsedLen += (1 + fieldLen[2]);
1837        parsedFields = FIELDS_HMS;
1838    } while (false);
1839
1840    if (parsedFields < minFields) {
1841        pos.setErrorIndex(start);
1842        return 0;
1843    }
1844
1845    pos.setIndex(start + parsedLen);
1846    return offset;
1847}
1848
1849void
1850TimeZoneFormat::appendOffsetDigits(UnicodeString& buf, int32_t n, uint8_t minDigits) const {
1851    U_ASSERT(n >= 0 && n < 60);
1852    int32_t numDigits = n >= 10 ? 2 : 1;
1853    for (int32_t i = 0; i < minDigits - numDigits; i++) {
1854        buf.append(fGMTOffsetDigits[0]);
1855    }
1856    if (numDigits == 2) {
1857        buf.append(fGMTOffsetDigits[n / 10]);
1858    }
1859    buf.append(fGMTOffsetDigits[n % 10]);
1860}
1861
1862// ------------------------------------------------------------------
1863// Private misc
1864void
1865TimeZoneFormat::initGMTPattern(const UnicodeString& gmtPattern, UErrorCode& status) {
1866    if (U_FAILURE(status)) {
1867        return;
1868    }
1869    // This implementation not perfect, but sufficient practically.
1870    int32_t idx = gmtPattern.indexOf(ARG0, ARG0_LEN, 0);
1871    if (idx < 0) {
1872        status = U_ILLEGAL_ARGUMENT_ERROR;
1873        return;
1874    }
1875    fGMTPattern.setTo(gmtPattern);
1876    unquote(gmtPattern.tempSubString(0, idx), fGMTPatternPrefix);
1877    unquote(gmtPattern.tempSubString(idx + ARG0_LEN), fGMTPatternSuffix);
1878}
1879
1880UnicodeString&
1881TimeZoneFormat::unquote(const UnicodeString& pattern, UnicodeString& result) {
1882    if (pattern.indexOf(SINGLEQUOTE) < 0) {
1883        result.setTo(pattern);
1884        return result;
1885    }
1886    result.remove();
1887    UBool isPrevQuote = FALSE;
1888    UBool inQuote = FALSE;
1889    for (int32_t i = 0; i < pattern.length(); i++) {
1890        UChar c = pattern.charAt(i);
1891        if (c == SINGLEQUOTE) {
1892            if (isPrevQuote) {
1893                result.append(c);
1894                isPrevQuote = FALSE;
1895            } else {
1896                isPrevQuote = TRUE;
1897            }
1898            inQuote = !inQuote;
1899        } else {
1900            isPrevQuote = FALSE;
1901            result.append(c);
1902        }
1903    }
1904    return result;
1905}
1906
1907UVector*
1908TimeZoneFormat::parseOffsetPattern(const UnicodeString& pattern, OffsetFields required, UErrorCode& status) {
1909    if (U_FAILURE(status)) {
1910        return NULL;
1911    }
1912    UVector* result = new UVector(deleteGMTOffsetField, NULL, status);
1913    if (result == NULL) {
1914        status = U_MEMORY_ALLOCATION_ERROR;
1915        return NULL;
1916    }
1917
1918    int32_t checkBits = 0;
1919    UBool isPrevQuote = FALSE;
1920    UBool inQuote = FALSE;
1921    UnicodeString text;
1922    GMTOffsetField::FieldType itemType = GMTOffsetField::TEXT;
1923    int32_t itemLength = 1;
1924
1925    for (int32_t i = 0; i < pattern.length(); i++) {
1926        UChar ch = pattern.charAt(i);
1927        if (ch == SINGLEQUOTE) {
1928            if (isPrevQuote) {
1929                text.append(SINGLEQUOTE);
1930                isPrevQuote = FALSE;
1931            } else {
1932                isPrevQuote = TRUE;
1933                if (itemType != GMTOffsetField::TEXT) {
1934                    if (GMTOffsetField::isValid(itemType, itemLength)) {
1935                        GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, (uint8_t)itemLength, status);
1936                        result->addElement(fld, status);
1937                        if (U_FAILURE(status)) {
1938                            break;
1939                        }
1940                    } else {
1941                        status = U_ILLEGAL_ARGUMENT_ERROR;
1942                        break;
1943                    }
1944                    itemType = GMTOffsetField::TEXT;
1945                }
1946            }
1947            inQuote = !inQuote;
1948        } else {
1949            isPrevQuote = FALSE;
1950            if (inQuote) {
1951                text.append(ch);
1952            } else {
1953                GMTOffsetField::FieldType tmpType = GMTOffsetField::getTypeByLetter(ch);
1954                if (tmpType != GMTOffsetField::TEXT) {
1955                    // an offset time pattern character
1956                    if (tmpType == itemType) {
1957                        itemLength++;
1958                    } else {
1959                        if (itemType == GMTOffsetField::TEXT) {
1960                            if (text.length() > 0) {
1961                                GMTOffsetField* textfld = GMTOffsetField::createText(text, status);
1962                                result->addElement(textfld, status);
1963                                if (U_FAILURE(status)) {
1964                                    break;
1965                                }
1966                                text.remove();
1967                            }
1968                        } else {
1969                            if (GMTOffsetField::isValid(itemType, itemLength)) {
1970                                GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status);
1971                                result->addElement(fld, status);
1972                                if (U_FAILURE(status)) {
1973                                    break;
1974                                }
1975                            } else {
1976                                status = U_ILLEGAL_ARGUMENT_ERROR;
1977                                break;
1978                            }
1979                        }
1980                        itemType = tmpType;
1981                        itemLength = 1;
1982                        checkBits |= tmpType;
1983                    }
1984                } else {
1985                    // a string literal
1986                    if (itemType != GMTOffsetField::TEXT) {
1987                        if (GMTOffsetField::isValid(itemType, itemLength)) {
1988                            GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status);
1989                            result->addElement(fld, status);
1990                            if (U_FAILURE(status)) {
1991                                break;
1992                            }
1993                        } else {
1994                            status = U_ILLEGAL_ARGUMENT_ERROR;
1995                            break;
1996                        }
1997                        itemType = GMTOffsetField::TEXT;
1998                    }
1999                    text.append(ch);
2000                }
2001            }
2002        }
2003    }
2004    // handle last item
2005    if (U_SUCCESS(status)) {
2006        if (itemType == GMTOffsetField::TEXT) {
2007            if (text.length() > 0) {
2008                GMTOffsetField* tfld = GMTOffsetField::createText(text, status);
2009                result->addElement(tfld, status);
2010            }
2011        } else {
2012            if (GMTOffsetField::isValid(itemType, itemLength)) {
2013                GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status);
2014                result->addElement(fld, status);
2015            } else {
2016                status = U_ILLEGAL_ARGUMENT_ERROR;
2017            }
2018        }
2019
2020        // Check all required fields are set
2021        if (U_SUCCESS(status)) {
2022            int32_t reqBits = 0;
2023            switch (required) {
2024            case FIELDS_H:
2025                reqBits = GMTOffsetField::HOUR;
2026                break;
2027            case FIELDS_HM:
2028                reqBits = GMTOffsetField::HOUR | GMTOffsetField::MINUTE;
2029                break;
2030            case FIELDS_HMS:
2031                reqBits = GMTOffsetField::HOUR | GMTOffsetField::MINUTE | GMTOffsetField::SECOND;
2032                break;
2033            }
2034            if (checkBits == reqBits) {
2035                // all required fields are set, no extra fields
2036                return result;
2037            }
2038        }
2039    }
2040
2041    // error
2042    delete result;
2043    return NULL;
2044}
2045
2046UnicodeString&
2047TimeZoneFormat::expandOffsetPattern(const UnicodeString& offsetHM, UnicodeString& result) {
2048    U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN) == 2);
2049
2050    int32_t idx_mm = offsetHM.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN, 2, 0);
2051    if (idx_mm < 0) {
2052        // we cannot do anything with this...
2053        result.setTo(offsetHM);
2054        result.append(DEFAULT_GMT_OFFSET_SEP);
2055        result.append(DEFAULT_GMT_OFFSET_SECOND_PATTERN, -1);
2056        return result;
2057    }
2058
2059    UnicodeString sep;
2060    int32_t idx_H = offsetHM.tempSubString(0, idx_mm).lastIndexOf((UChar)0x0048 /* H */);
2061    if (idx_H >= 0) {
2062        sep = offsetHM.tempSubString(idx_H + 1, idx_mm - (idx_H + 1));
2063    }
2064    result.setTo(offsetHM.tempSubString(0, idx_mm + 2));
2065    result.append(sep);
2066    result.append(DEFAULT_GMT_OFFSET_SECOND_PATTERN, -1);
2067    result.append(offsetHM.tempSubString(idx_mm + 2));
2068    return result;
2069}
2070
2071void
2072TimeZoneFormat::initGMTOffsetPatterns(UErrorCode& status) {
2073    for (int32_t type = 0; type <= UTZFMT_PAT_NEGATIVE_HMS; type++) {
2074        switch (type) {
2075        case UTZFMT_PAT_POSITIVE_HM:
2076        case UTZFMT_PAT_NEGATIVE_HM:
2077            fGMTOffsetPatternItems[type] = parseOffsetPattern(fGMTOffsetPatterns[type], FIELDS_HM, status);
2078            break;
2079        case UTZFMT_PAT_POSITIVE_HMS:
2080        case UTZFMT_PAT_NEGATIVE_HMS:
2081            fGMTOffsetPatternItems[type] = parseOffsetPattern(fGMTOffsetPatterns[type], FIELDS_HMS, status);
2082            break;
2083        }
2084    }
2085}
2086
2087UBool
2088TimeZoneFormat::toCodePoints(const UnicodeString& str, UChar32* codeArray, int32_t size) {
2089    int32_t count = str.countChar32();
2090    if (count != size) {
2091        return FALSE;
2092    }
2093
2094    for (int32_t idx = 0, start = 0; idx < size; idx++) {
2095        codeArray[idx] = str.char32At(start);
2096        start = str.moveIndex32(start, 1);
2097    }
2098
2099    return TRUE;
2100}
2101
2102TimeZone*
2103TimeZoneFormat::createTimeZoneForOffset(int32_t offset) const {
2104    if (offset == 0) {
2105        // when offset is 0, we should use "Etc/GMT"
2106        return TimeZone::createTimeZone(UnicodeString(TZID_GMT));
2107    }
2108    return ZoneMeta::createCustomTimeZone(offset);
2109}
2110
2111UTimeZoneFormatTimeType
2112TimeZoneFormat::getTimeType(UTimeZoneNameType nameType) {
2113    switch (nameType) {
2114    case UTZNM_LONG_STANDARD:
2115    case UTZNM_SHORT_STANDARD:
2116        return UTZFMT_TIME_TYPE_STANDARD;
2117
2118    case UTZNM_LONG_DAYLIGHT:
2119    case UTZNM_SHORT_DAYLIGHT:
2120        return UTZFMT_TIME_TYPE_DAYLIGHT;
2121
2122    default:
2123        U_ASSERT(FALSE);
2124    }
2125    return UTZFMT_TIME_TYPE_UNKNOWN;
2126}
2127
2128UnicodeString&
2129TimeZoneFormat::getTimeZoneID(const TimeZoneNames::MatchInfoCollection* matches, int32_t idx, UnicodeString& tzID) const {
2130    if (!matches->getTimeZoneIDAt(idx, tzID)) {
2131        UnicodeString mzID;
2132        if (matches->getMetaZoneIDAt(idx, mzID)) {
2133            fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, tzID);
2134        }
2135    }
2136    return tzID;
2137}
2138
2139U_NAMESPACE_END
2140
2141#endif
2142