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