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