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