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