2 *******************************************************************************
3 * Copyright (C) 2011-2013, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
8 #include "unicode/utypes.h"
10 #if !UCONFIG_NO_FORMATTING
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"
28 #include "tznames_impl.h" // TextTrieMap
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
59 static const char gZoneStringsTag
[] = "zoneStrings";
60 static const char gGmtFormatTag
[]= "gmtFormat";
61 static const char gGmtZeroFormatTag
[] = "gmtZeroFormat";
62 static const char gHourFormatTag
[]= "hourFormat";
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
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
79 static const UChar32 DEFAULT_GMT_DIGITS
[] = {
80 0x0030, 0x0031, 0x0032, 0x0033, 0x0034,
81 0x0035, 0x0036, 0x0037, 0x0038, 0x0039
84 static const UChar DEFAULT_GMT_OFFSET_SEP
= 0x003A; // ':'
86 static const UChar ARG0
[] = {0x007B, 0x0030, 0x007D}; // "{0}"
87 static const int32_t ARG0_LEN
= 3;
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"
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
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
,
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; // ':'
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;
121 // Maximum offset (exclusive) in millisecond supported by offset formats
122 static int32_t MAX_OFFSET
= 24 * MILLIS_PER_HOUR
;
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;
129 static const int32_t UNKNOWN_OFFSET
= 0x7FFFFFFF;
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
;
134 #define DIGIT_VAL(c) (0x0030 <= (c) && (c) <= 0x0039 ? (c) - 0x0030 : -1)
135 #define MAX_OFFSET_DIGITS 6
137 // Time Zone ID/Short ID trie
138 static TextTrieMap
*gZoneIdTrie
= NULL
;
139 static UBool gZoneIdTrieInitialized
= FALSE
;
141 static TextTrieMap
*gShortZoneIdTrie
= NULL
;
142 static UBool gShortZoneIdTrieInitialized
= FALSE
;
144 static UMutex gLock
= U_MUTEX_INITIALIZER
;
148 * Cleanup callback func
150 static UBool U_CALLCONV
tzfmt_cleanup(void)
152 if (gZoneIdTrie
!= NULL
) {
156 gZoneIdTrieInitialized
= FALSE
;
158 if (gShortZoneIdTrie
!= NULL
) {
159 delete gShortZoneIdTrie
;
161 gShortZoneIdTrie
= NULL
;
162 gShortZoneIdTrieInitialized
= FALSE
;
168 // ------------------------------------------------------------------
171 // This class represents a localized GMT offset pattern
172 // item and used by TimeZoneFormat
173 // ------------------------------------------------------------------
174 class GMTOffsetField
: public UMemory
{
183 virtual ~GMTOffsetField();
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
);
190 FieldType
getType() const;
191 uint8_t getWidth() const;
192 const UChar
* getPatternText(void) const;
202 GMTOffsetField::GMTOffsetField()
203 : fText(NULL
), fType(TEXT
), fWidth(0) {
206 GMTOffsetField::~GMTOffsetField() {
213 GMTOffsetField::createText(const UnicodeString
& text
, UErrorCode
& status
) {
214 if (U_FAILURE(status
)) {
217 GMTOffsetField
* result
= new GMTOffsetField();
218 if (result
== NULL
) {
219 status
= U_MEMORY_ALLOCATION_ERROR
;
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
;
230 u_strncpy(result
->fText
, text
.getBuffer(), len
);
231 result
->fText
[len
] = 0;
232 result
->fType
= TEXT
;
238 GMTOffsetField::createTimeField(FieldType type
, uint8_t width
, UErrorCode
& status
) {
239 U_ASSERT(type
!= TEXT
);
240 if (U_FAILURE(status
)) {
243 GMTOffsetField
* result
= new GMTOffsetField();
244 if (result
== NULL
) {
245 status
= U_MEMORY_ALLOCATION_ERROR
;
249 result
->fType
= type
;
250 result
->fWidth
= width
;
256 GMTOffsetField::isValid(FieldType type
, int32_t width
) {
259 return (width
== 1 || width
== 2);
269 GMTOffsetField::FieldType
270 GMTOffsetField::getTypeByLetter(UChar ch
) {
271 if (ch
== 0x0048 /* H */) {
273 } else if (ch
== 0x006D /* m */) {
275 } else if (ch
== 0x0073 /* s */) {
281 inline GMTOffsetField::FieldType
282 GMTOffsetField::getType() const {
287 GMTOffsetField::getWidth() const {
292 GMTOffsetField::getPatternText(void) const {
298 static void U_CALLCONV
299 deleteGMTOffsetField(void *obj
) {
300 delete static_cast<GMTOffsetField
*>(obj
);
305 // ------------------------------------------------------------------
307 // ------------------------------------------------------------------
308 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeZoneFormat
)
310 TimeZoneFormat::TimeZoneFormat(const Locale
& locale
, UErrorCode
& status
)
311 : fLocale(locale
), fTimeZoneNames(NULL
), fTimeZoneGenericNames(NULL
), fDefParseOptionFlags(0) {
313 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
; i
++) {
314 fGMTOffsetPatternItems
[i
] = NULL
;
317 const char* region
= fLocale
.getCountry();
318 int32_t regionLen
= uprv_strlen(region
);
319 if (regionLen
== 0) {
320 char loc
[ULOC_FULLNAME_CAPACITY
];
321 uloc_addLikelySubtags(fLocale
.getName(), loc
, sizeof(loc
), &status
);
323 regionLen
= uloc_getCountry(loc
, fTargetRegion
, sizeof(fTargetRegion
), &status
);
324 if (U_SUCCESS(status
)) {
325 fTargetRegion
[regionLen
] = 0;
329 } else if (regionLen
< (int32_t)sizeof(fTargetRegion
)) {
330 uprv_strcpy(fTargetRegion
, region
);
332 fTargetRegion
[0] = 0;
335 fTimeZoneNames
= TimeZoneNames::createInstance(locale
, status
);
336 // fTimeZoneGenericNames is lazily instantiated
337 if (U_FAILURE(status
)) {
341 const UChar
* gmtPattern
= NULL
;
342 const UChar
* hourFormats
= NULL
;
344 UResourceBundle
*zoneBundle
= ures_open(U_ICUDATA_ZONE
, locale
.getName(), &status
);
345 UResourceBundle
*zoneStringsArray
= ures_getByKeyWithFallback(zoneBundle
, gZoneStringsTag
, NULL
, &status
);
346 if (U_SUCCESS(status
)) {
349 resStr
= ures_getStringByKeyWithFallback(zoneStringsArray
, gGmtFormatTag
, &len
, &status
);
353 resStr
= ures_getStringByKeyWithFallback(zoneStringsArray
, gGmtZeroFormatTag
, &len
, &status
);
355 fGMTZeroFormat
.setTo(TRUE
, resStr
, len
);
357 resStr
= ures_getStringByKeyWithFallback(zoneStringsArray
, gHourFormatTag
, &len
, &status
);
359 hourFormats
= resStr
;
361 ures_close(zoneStringsArray
);
362 ures_close(zoneBundle
);
365 if (gmtPattern
== NULL
) {
366 gmtPattern
= DEFAULT_GMT_PATTERN
;
368 initGMTPattern(UnicodeString(gmtPattern
, -1), status
);
370 UBool useDefaultOffsetPatterns
= TRUE
;
372 UChar
*sep
= u_strchr(hourFormats
, (UChar
)0x003B /* ';' */);
374 UErrorCode tmpStatus
= U_ZERO_ERROR
;
375 fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HM
].setTo(FALSE
, hourFormats
, (int32_t)(sep
- hourFormats
));
376 fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HM
].setTo(TRUE
, sep
+ 1, -1);
377 expandOffsetPattern(fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HM
], fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HMS
], tmpStatus
);
378 expandOffsetPattern(fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HM
], fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HMS
], tmpStatus
);
379 truncateOffsetPattern(fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HM
], fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_H
], tmpStatus
);
380 truncateOffsetPattern(fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HM
], fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_H
], tmpStatus
);
381 if (U_SUCCESS(tmpStatus
)) {
382 useDefaultOffsetPatterns
= FALSE
;
386 if (useDefaultOffsetPatterns
) {
387 fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_H
].setTo(TRUE
, DEFAULT_GMT_POSITIVE_H
, -1);
388 fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HM
].setTo(TRUE
, DEFAULT_GMT_POSITIVE_HM
, -1);
389 fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HMS
].setTo(TRUE
, DEFAULT_GMT_POSITIVE_HMS
, -1);
390 fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_H
].setTo(TRUE
, DEFAULT_GMT_NEGATIVE_H
, -1);
391 fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HM
].setTo(TRUE
, DEFAULT_GMT_NEGATIVE_HM
, -1);
392 fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HMS
].setTo(TRUE
, DEFAULT_GMT_NEGATIVE_HMS
, -1);
394 initGMTOffsetPatterns(status
);
396 NumberingSystem
* ns
= NumberingSystem::createInstance(locale
, status
);
397 UBool useDefDigits
= TRUE
;
398 if (ns
&& !ns
->isAlgorithmic()) {
399 UnicodeString digits
= ns
->getDescription();
400 useDefDigits
= !toCodePoints(digits
, fGMTOffsetDigits
, 10);
403 uprv_memcpy(fGMTOffsetDigits
, DEFAULT_GMT_DIGITS
, sizeof(UChar32
) * 10);
408 TimeZoneFormat::TimeZoneFormat(const TimeZoneFormat
& other
)
409 : Format(other
), fTimeZoneNames(NULL
), fTimeZoneGenericNames(NULL
) {
411 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
; i
++) {
412 fGMTOffsetPatternItems
[i
] = NULL
;
418 TimeZoneFormat::~TimeZoneFormat() {
419 delete fTimeZoneNames
;
420 delete fTimeZoneGenericNames
;
421 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
; i
++) {
422 delete fGMTOffsetPatternItems
[i
];
427 TimeZoneFormat::operator=(const TimeZoneFormat
& other
) {
428 if (this == &other
) {
432 delete fTimeZoneNames
;
433 delete fTimeZoneGenericNames
;
434 fTimeZoneGenericNames
= NULL
;
436 fLocale
= other
.fLocale
;
437 uprv_memcpy(fTargetRegion
, other
.fTargetRegion
, sizeof(fTargetRegion
));
439 fTimeZoneNames
= other
.fTimeZoneNames
->clone();
440 if (other
.fTimeZoneGenericNames
) {
441 fTimeZoneGenericNames
= other
.fTimeZoneGenericNames
->clone();
444 fGMTPattern
= other
.fGMTPattern
;
445 fGMTPatternPrefix
= other
.fGMTPatternPrefix
;
446 fGMTPatternSuffix
= other
.fGMTPatternSuffix
;
448 UErrorCode status
= U_ZERO_ERROR
;
449 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
; i
++) {
450 fGMTOffsetPatterns
[i
] = other
.fGMTOffsetPatterns
[i
];
451 delete fGMTOffsetPatternItems
[i
];
453 initGMTOffsetPatterns(status
);
454 U_ASSERT(U_SUCCESS(status
));
456 fGMTZeroFormat
= other
.fGMTZeroFormat
;
458 uprv_memcpy(fGMTOffsetDigits
, other
.fGMTOffsetDigits
, sizeof(fGMTOffsetDigits
));
460 fDefParseOptionFlags
= other
.fDefParseOptionFlags
;
467 TimeZoneFormat::operator==(const Format
& other
) const {
468 TimeZoneFormat
* tzfmt
= (TimeZoneFormat
*)&other
;
471 fLocale
== tzfmt
->fLocale
472 && fGMTPattern
== tzfmt
->fGMTPattern
473 && fGMTZeroFormat
== tzfmt
->fGMTZeroFormat
474 && *fTimeZoneNames
== *tzfmt
->fTimeZoneNames
;
476 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
&& isEqual
; i
++) {
477 isEqual
= fGMTOffsetPatterns
[i
] == tzfmt
->fGMTOffsetPatterns
[i
];
479 for (int32_t i
= 0; i
< 10 && isEqual
; i
++) {
480 isEqual
= fGMTOffsetDigits
[i
] == tzfmt
->fGMTOffsetDigits
[i
];
483 // Check fTimeZoneGenericNames. For now,
484 // if fTimeZoneNames is same, fTimeZoneGenericNames should
485 // be also equivalent.
490 TimeZoneFormat::clone() const {
491 return new TimeZoneFormat(*this);
494 TimeZoneFormat
* U_EXPORT2
495 TimeZoneFormat::createInstance(const Locale
& locale
, UErrorCode
& status
) {
496 TimeZoneFormat
* tzfmt
= new TimeZoneFormat(locale
, status
);
497 if (U_SUCCESS(status
)) {
504 // ------------------------------------------------------------------
508 TimeZoneFormat::getTimeZoneNames() const {
509 return (const TimeZoneNames
*)fTimeZoneNames
;
513 TimeZoneFormat::adoptTimeZoneNames(TimeZoneNames
*tznames
) {
514 delete fTimeZoneNames
;
515 fTimeZoneNames
= tznames
;
517 // TODO - We should also update fTimeZoneGenericNames
521 TimeZoneFormat::setTimeZoneNames(const TimeZoneNames
&tznames
) {
522 delete fTimeZoneNames
;
523 fTimeZoneNames
= tznames
.clone();
525 // TODO - We should also update fTimeZoneGenericNames
529 TimeZoneFormat::setDefaultParseOptions(uint32_t flags
) {
530 fDefParseOptionFlags
= flags
;
534 TimeZoneFormat::getDefaultParseOptions(void) const {
535 return fDefParseOptionFlags
;
540 TimeZoneFormat::getGMTPattern(UnicodeString
& pattern
) const {
541 return pattern
.setTo(fGMTPattern
);
545 TimeZoneFormat::setGMTPattern(const UnicodeString
& pattern
, UErrorCode
& status
) {
546 initGMTPattern(pattern
, status
);
550 TimeZoneFormat::getGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type
, UnicodeString
& pattern
) const {
551 return pattern
.setTo(fGMTOffsetPatterns
[type
]);
555 TimeZoneFormat::setGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type
, const UnicodeString
& pattern
, UErrorCode
& status
) {
556 if (U_FAILURE(status
)) {
559 if (pattern
== fGMTOffsetPatterns
[type
]) {
564 OffsetFields required
= FIELDS_HM
;
566 case UTZFMT_PAT_POSITIVE_H
:
567 case UTZFMT_PAT_NEGATIVE_H
:
570 case UTZFMT_PAT_POSITIVE_HM
:
571 case UTZFMT_PAT_NEGATIVE_HM
:
572 required
= FIELDS_HM
;
574 case UTZFMT_PAT_POSITIVE_HMS
:
575 case UTZFMT_PAT_NEGATIVE_HMS
:
576 required
= FIELDS_HMS
;
583 UVector
* patternItems
= parseOffsetPattern(pattern
, required
, status
);
584 if (patternItems
== NULL
) {
588 fGMTOffsetPatterns
[type
].setTo(pattern
);
589 delete fGMTOffsetPatternItems
[type
];
590 fGMTOffsetPatternItems
[type
] = patternItems
;
591 checkAbuttingHoursAndMinutes();
595 TimeZoneFormat::getGMTOffsetDigits(UnicodeString
& digits
) const {
597 for (int32_t i
= 0; i
< 10; i
++) {
598 digits
.append(fGMTOffsetDigits
[i
]);
604 TimeZoneFormat::setGMTOffsetDigits(const UnicodeString
& digits
, UErrorCode
& status
) {
605 if (U_FAILURE(status
)) {
608 UChar32 digitArray
[10];
609 if (!toCodePoints(digits
, digitArray
, 10)) {
610 status
= U_ILLEGAL_ARGUMENT_ERROR
;
613 uprv_memcpy(fGMTOffsetDigits
, digitArray
, sizeof(UChar32
)*10);
617 TimeZoneFormat::getGMTZeroFormat(UnicodeString
& gmtZeroFormat
) const {
618 return gmtZeroFormat
.setTo(fGMTZeroFormat
);
622 TimeZoneFormat::setGMTZeroFormat(const UnicodeString
& gmtZeroFormat
, UErrorCode
& status
) {
623 if (U_SUCCESS(status
)) {
624 if (gmtZeroFormat
.isEmpty()) {
625 status
= U_ILLEGAL_ARGUMENT_ERROR
;
626 } else if (gmtZeroFormat
!= fGMTZeroFormat
) {
627 fGMTZeroFormat
.setTo(gmtZeroFormat
);
632 // ------------------------------------------------------------------
636 TimeZoneFormat::format(UTimeZoneFormatStyle style
, const TimeZone
& tz
, UDate date
,
637 UnicodeString
& name
, UTimeZoneFormatTimeType
* timeType
/* = NULL */) const {
639 *timeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
642 case UTZFMT_STYLE_GENERIC_LOCATION
:
643 formatGeneric(tz
, UTZGNM_LOCATION
, date
, name
);
645 case UTZFMT_STYLE_GENERIC_LONG
:
646 formatGeneric(tz
, UTZGNM_LONG
, date
, name
);
648 case UTZFMT_STYLE_GENERIC_SHORT
:
649 formatGeneric(tz
, UTZGNM_SHORT
, date
, name
);
651 case UTZFMT_STYLE_SPECIFIC_LONG
:
652 formatSpecific(tz
, UTZNM_LONG_STANDARD
, UTZNM_LONG_DAYLIGHT
, date
, name
, timeType
);
654 case UTZFMT_STYLE_SPECIFIC_SHORT
:
655 formatSpecific(tz
, UTZNM_SHORT_STANDARD
, UTZNM_SHORT_DAYLIGHT
, date
, name
, timeType
);
658 // will be handled below
662 if (name
.isEmpty()) {
663 UErrorCode status
= U_ZERO_ERROR
;
664 int32_t rawOffset
, dstOffset
;
665 tz
.getOffset(date
, FALSE
, rawOffset
, dstOffset
, status
);
666 int32_t offset
= rawOffset
+ dstOffset
;
667 if (U_SUCCESS(status
)) {
669 case UTZFMT_STYLE_GENERIC_LOCATION
:
670 case UTZFMT_STYLE_GENERIC_LONG
:
671 case UTZFMT_STYLE_SPECIFIC_LONG
:
672 case UTZFMT_STYLE_LOCALIZED_GMT
:
673 formatOffsetLocalizedGMT(offset
, name
, status
);
676 case UTZFMT_STYLE_GENERIC_SHORT
:
677 case UTZFMT_STYLE_SPECIFIC_SHORT
:
678 case UTZFMT_STYLE_LOCALIZED_GMT_SHORT
:
679 formatOffsetShortLocalizedGMT(offset
, name
, status
);
682 case UTZFMT_STYLE_ISO_BASIC_SHORT
:
683 formatOffsetISO8601Basic(offset
, TRUE
, TRUE
, TRUE
, name
, status
);
686 case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT
:
687 formatOffsetISO8601Basic(offset
, FALSE
, TRUE
, TRUE
, name
, status
);
690 case UTZFMT_STYLE_ISO_BASIC_FIXED
:
691 formatOffsetISO8601Basic(offset
, TRUE
, FALSE
, TRUE
, name
, status
);
694 case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED
:
695 formatOffsetISO8601Basic(offset
, FALSE
, FALSE
, TRUE
, name
, status
);
698 case UTZFMT_STYLE_ISO_EXTENDED_FIXED
:
699 formatOffsetISO8601Extended(offset
, TRUE
, FALSE
, TRUE
, name
, status
);
702 case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED
:
703 formatOffsetISO8601Extended(offset
, FALSE
, FALSE
, TRUE
, name
, status
);
706 case UTZFMT_STYLE_ISO_BASIC_FULL
:
707 formatOffsetISO8601Basic(offset
, TRUE
, FALSE
, FALSE
, name
, status
);
710 case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
:
711 formatOffsetISO8601Basic(offset
, FALSE
, FALSE
, FALSE
, name
, status
);
714 case UTZFMT_STYLE_ISO_EXTENDED_FULL
:
715 formatOffsetISO8601Extended(offset
, TRUE
, FALSE
, FALSE
, name
, status
);
718 case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL
:
719 formatOffsetISO8601Extended(offset
, FALSE
, FALSE
, FALSE
, name
, status
);
722 case UTZFMT_STYLE_ZONE_ID
:
726 case UTZFMT_STYLE_ZONE_ID_SHORT
:
728 const UChar
* shortID
= ZoneMeta::getShortID(tz
);
729 if (shortID
== NULL
) {
730 shortID
= UNKNOWN_SHORT_ZONE_ID
;
732 name
.setTo(shortID
, -1);
736 case UTZFMT_STYLE_EXEMPLAR_LOCATION
:
737 formatExemplarLocation(tz
, name
);
741 *timeType
= (dstOffset
!= 0) ? UTZFMT_TIME_TYPE_DAYLIGHT
: UTZFMT_TIME_TYPE_STANDARD
;
750 TimeZoneFormat::format(const Formattable
& obj
, UnicodeString
& appendTo
,
751 FieldPosition
& pos
, UErrorCode
& status
) const {
752 if (U_FAILURE(status
)) {
755 UDate date
= Calendar::getNow();
756 if (obj
.getType() == Formattable::kObject
) {
757 const UObject
* formatObj
= obj
.getObject();
758 const TimeZone
* tz
= dynamic_cast<const TimeZone
*>(formatObj
);
760 const Calendar
* cal
= dynamic_cast<const Calendar
*>(formatObj
);
762 tz
= &cal
->getTimeZone();
763 date
= cal
->getTime(status
);
767 int32_t rawOffset
, dstOffset
;
768 tz
->getOffset(date
, FALSE
, rawOffset
, dstOffset
, status
);
769 UnicodeString result
;
770 formatOffsetLocalizedGMT(rawOffset
+ dstOffset
, result
, status
);
771 if (U_SUCCESS(status
)) {
772 appendTo
.append(result
);
773 if (pos
.getField() == UDAT_TIMEZONE_FIELD
) {
774 pos
.setBeginIndex(0);
775 pos
.setEndIndex(result
.length());
784 TimeZoneFormat::parse(UTimeZoneFormatStyle style
, const UnicodeString
& text
, ParsePosition
& pos
,
785 UTimeZoneFormatTimeType
* timeType
/*= NULL*/) const {
786 return parse(style
, text
, pos
, getDefaultParseOptions(), timeType
);
790 TimeZoneFormat::parse(UTimeZoneFormatStyle style
, const UnicodeString
& text
, ParsePosition
& pos
,
791 int32_t parseOptions
, UTimeZoneFormatTimeType
* timeType
/* = NULL */) const {
793 *timeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
796 int32_t startIdx
= pos
.getIndex();
797 int32_t maxPos
= text
.length();
800 // Styles using localized GMT format as fallback
801 UBool fallbackLocalizedGMT
=
802 (style
== UTZFMT_STYLE_SPECIFIC_LONG
|| style
== UTZFMT_STYLE_GENERIC_LONG
|| style
== UTZFMT_STYLE_GENERIC_LOCATION
);
803 UBool fallbackShortLocalizedGMT
=
804 (style
== UTZFMT_STYLE_SPECIFIC_SHORT
|| style
== UTZFMT_STYLE_GENERIC_SHORT
);
806 int32_t evaluated
= 0; // bit flags representing already evaluated styles
807 ParsePosition
tmpPos(startIdx
);
809 int32_t parsedOffset
= UNKNOWN_OFFSET
; // stores successfully parsed offset for later use
810 int32_t parsedPos
= -1; // stores successfully parsed offset position for later use
812 // Try localized GMT format first if necessary
813 if (fallbackLocalizedGMT
|| fallbackShortLocalizedGMT
) {
814 UBool hasDigitOffset
= FALSE
;
815 offset
= parseOffsetLocalizedGMT(text
, tmpPos
, fallbackShortLocalizedGMT
, &hasDigitOffset
);
816 if (tmpPos
.getErrorIndex() == -1) {
817 // Even when the input text was successfully parsed as a localized GMT format text,
818 // we may still need to evaluate the specified style if -
819 // 1) GMT zero format was used, and
820 // 2) The input text was not completely processed
821 if (tmpPos
.getIndex() == maxPos
|| hasDigitOffset
) {
822 pos
.setIndex(tmpPos
.getIndex());
823 return createTimeZoneForOffset(offset
);
825 parsedOffset
= offset
;
826 parsedPos
= tmpPos
.getIndex();
828 // Note: For now, no distinction between long/short localized GMT format in the parser.
829 // This might be changed in future.
830 // evaluated |= (fallbackLocalizedGMT ? STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT] : STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT]);
831 evaluated
|= STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT
] | STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT_SHORT
];
834 UErrorCode status
= U_ZERO_ERROR
;
837 // Try the specified style
839 case UTZFMT_STYLE_LOCALIZED_GMT
:
841 tmpPos
.setIndex(startIdx
);
842 tmpPos
.setErrorIndex(-1);
844 offset
= parseOffsetLocalizedGMT(text
, tmpPos
);
845 if (tmpPos
.getErrorIndex() == -1) {
846 pos
.setIndex(tmpPos
.getIndex());
847 return createTimeZoneForOffset(offset
);
850 // Note: For now, no distinction between long/short localized GMT format in the parser.
851 // This might be changed in future.
852 evaluated
|= STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT_SHORT
];
856 case UTZFMT_STYLE_LOCALIZED_GMT_SHORT
:
858 tmpPos
.setIndex(startIdx
);
859 tmpPos
.setErrorIndex(-1);
861 offset
= parseOffsetShortLocalizedGMT(text
, tmpPos
);
862 if (tmpPos
.getErrorIndex() == -1) {
863 pos
.setIndex(tmpPos
.getIndex());
864 return createTimeZoneForOffset(offset
);
867 // Note: For now, no distinction between long/short localized GMT format in the parser.
868 // This might be changed in future.
869 evaluated
|= STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT
];
873 case UTZFMT_STYLE_ISO_BASIC_SHORT
:
874 case UTZFMT_STYLE_ISO_BASIC_FIXED
:
875 case UTZFMT_STYLE_ISO_BASIC_FULL
:
876 case UTZFMT_STYLE_ISO_EXTENDED_FIXED
:
877 case UTZFMT_STYLE_ISO_EXTENDED_FULL
:
879 tmpPos
.setIndex(startIdx
);
880 tmpPos
.setErrorIndex(-1);
882 offset
= parseOffsetISO8601(text
, tmpPos
);
883 if (tmpPos
.getErrorIndex() == -1) {
884 pos
.setIndex(tmpPos
.getIndex());
885 return createTimeZoneForOffset(offset
);
891 case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT
:
892 case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED
:
893 case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
:
894 case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED
:
895 case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL
:
897 tmpPos
.setIndex(startIdx
);
898 tmpPos
.setErrorIndex(-1);
900 // Exclude the case of UTC Indicator "Z" here
901 UBool hasDigitOffset
= FALSE
;
902 offset
= parseOffsetISO8601(text
, tmpPos
, FALSE
, &hasDigitOffset
);
903 if (tmpPos
.getErrorIndex() == -1 && hasDigitOffset
) {
904 pos
.setIndex(tmpPos
.getIndex());
905 return createTimeZoneForOffset(offset
);
911 case UTZFMT_STYLE_SPECIFIC_LONG
:
912 case UTZFMT_STYLE_SPECIFIC_SHORT
:
915 int32_t nameTypes
= 0;
916 if (style
== UTZFMT_STYLE_SPECIFIC_LONG
) {
917 nameTypes
= (UTZNM_LONG_STANDARD
| UTZNM_LONG_DAYLIGHT
);
919 U_ASSERT(style
== UTZFMT_STYLE_SPECIFIC_SHORT
);
920 nameTypes
= (UTZNM_SHORT_STANDARD
| UTZNM_SHORT_DAYLIGHT
);
922 LocalPointer
<TimeZoneNames::MatchInfoCollection
> specificMatches(fTimeZoneNames
->find(text
, startIdx
, nameTypes
, status
));
923 if (U_FAILURE(status
)) {
924 pos
.setErrorIndex(startIdx
);
927 if (!specificMatches
.isNull()) {
928 int32_t matchIdx
= -1;
929 int32_t matchPos
= -1;
930 for (int32_t i
= 0; i
< specificMatches
->size(); i
++) {
931 matchPos
= startIdx
+ specificMatches
->getMatchLengthAt(i
);
932 if (matchPos
> parsedPos
) {
934 parsedPos
= matchPos
;
939 *timeType
= getTimeType(specificMatches
->getNameTypeAt(matchIdx
));
941 pos
.setIndex(matchPos
);
942 getTimeZoneID(specificMatches
.getAlias(), matchIdx
, tzID
);
943 U_ASSERT(!tzID
.isEmpty());
944 return TimeZone::createTimeZone(tzID
);
949 case UTZFMT_STYLE_GENERIC_LONG
:
950 case UTZFMT_STYLE_GENERIC_SHORT
:
951 case UTZFMT_STYLE_GENERIC_LOCATION
:
953 int32_t genericNameTypes
= 0;
955 case UTZFMT_STYLE_GENERIC_LOCATION
:
956 genericNameTypes
= UTZGNM_LOCATION
;
959 case UTZFMT_STYLE_GENERIC_LONG
:
960 genericNameTypes
= UTZGNM_LONG
| UTZGNM_LOCATION
;
963 case UTZFMT_STYLE_GENERIC_SHORT
:
964 genericNameTypes
= UTZGNM_SHORT
| UTZGNM_LOCATION
;
972 UTimeZoneFormatTimeType tt
= UTZFMT_TIME_TYPE_UNKNOWN
;
973 const TimeZoneGenericNames
*gnames
= getTimeZoneGenericNames(status
);
974 if (U_SUCCESS(status
)) {
975 len
= gnames
->findBestMatch(text
, startIdx
, genericNameTypes
, tzID
, tt
, status
);
977 if (U_FAILURE(status
)) {
978 pos
.setErrorIndex(startIdx
);
986 pos
.setIndex(startIdx
+ len
);
987 U_ASSERT(!tzID
.isEmpty());
988 return TimeZone::createTimeZone(tzID
);
993 case UTZFMT_STYLE_ZONE_ID
:
995 tmpPos
.setIndex(startIdx
);
996 tmpPos
.setErrorIndex(-1);
998 parseZoneID(text
, tmpPos
, tzID
);
999 if (tmpPos
.getErrorIndex() == -1) {
1000 pos
.setIndex(tmpPos
.getIndex());
1001 return TimeZone::createTimeZone(tzID
);
1005 case UTZFMT_STYLE_ZONE_ID_SHORT
:
1007 tmpPos
.setIndex(startIdx
);
1008 tmpPos
.setErrorIndex(-1);
1010 parseShortZoneID(text
, tmpPos
, tzID
);
1011 if (tmpPos
.getErrorIndex() == -1) {
1012 pos
.setIndex(tmpPos
.getIndex());
1013 return TimeZone::createTimeZone(tzID
);
1017 case UTZFMT_STYLE_EXEMPLAR_LOCATION
:
1019 tmpPos
.setIndex(startIdx
);
1020 tmpPos
.setErrorIndex(-1);
1022 parseExemplarLocation(text
, tmpPos
, tzID
);
1023 if (tmpPos
.getErrorIndex() == -1) {
1024 pos
.setIndex(tmpPos
.getIndex());
1025 return TimeZone::createTimeZone(tzID
);
1030 evaluated
|= STYLE_PARSE_FLAGS
[style
];
1033 if (parsedPos
> startIdx
) {
1034 // When the specified style is one of SPECIFIC_XXX or GENERIC_XXX, we tried to parse the input
1035 // as localized GMT format earlier. If parsedOffset is positive, it means it was successfully
1036 // parsed as localized GMT format, but offset digits were not detected (more specifically, GMT
1037 // zero format). Then, it tried to find a match within the set of display names, but could not
1038 // find a match. At this point, we can safely assume the input text contains the localized
1040 U_ASSERT(parsedOffset
!= UNKNOWN_OFFSET
);
1041 pos
.setIndex(parsedPos
);
1042 return createTimeZoneForOffset(parsedOffset
);
1045 // Failed to parse the input text as the time zone format in the specified style.
1046 // Check the longest match among other styles below.
1047 UnicodeString parsedID
;
1048 UTimeZoneFormatTimeType parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1050 U_ASSERT(parsedPos
< 0);
1051 U_ASSERT(parsedOffset
== UNKNOWN_OFFSET
);
1054 if (parsedPos
< maxPos
&&
1055 ((evaluated
& ISO_Z_STYLE_FLAG
) == 0 || (evaluated
& ISO_LOCAL_STYLE_FLAG
) == 0)) {
1056 tmpPos
.setIndex(startIdx
);
1057 tmpPos
.setErrorIndex(-1);
1059 UBool hasDigitOffset
= FALSE
;
1060 offset
= parseOffsetISO8601(text
, tmpPos
, FALSE
, &hasDigitOffset
);
1061 if (tmpPos
.getErrorIndex() == -1) {
1062 if (tmpPos
.getIndex() == maxPos
|| hasDigitOffset
) {
1063 pos
.setIndex(tmpPos
.getIndex());
1064 return createTimeZoneForOffset(offset
);
1066 // Note: When ISO 8601 format contains offset digits, it should not
1067 // collide with other formats. However, ISO 8601 UTC format "Z" (single letter)
1068 // may collide with other names. In this case, we need to evaluate other names.
1069 if (parsedPos
< tmpPos
.getIndex()) {
1070 parsedOffset
= offset
;
1071 parsedID
.setToBogus();
1072 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1073 parsedPos
= tmpPos
.getIndex();
1074 U_ASSERT(parsedPos
== startIdx
+ 1); // only when "Z" is used
1079 // Localized GMT format
1080 if (parsedPos
< maxPos
&&
1081 (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT
]) == 0) {
1082 tmpPos
.setIndex(startIdx
);
1083 tmpPos
.setErrorIndex(-1);
1085 UBool hasDigitOffset
= FALSE
;
1086 offset
= parseOffsetLocalizedGMT(text
, tmpPos
, FALSE
, &hasDigitOffset
);
1087 if (tmpPos
.getErrorIndex() == -1) {
1088 if (tmpPos
.getIndex() == maxPos
|| hasDigitOffset
) {
1089 pos
.setIndex(tmpPos
.getIndex());
1090 return createTimeZoneForOffset(offset
);
1092 // Evaluate other names - see the comment earlier in this method.
1093 if (parsedPos
< tmpPos
.getIndex()) {
1094 parsedOffset
= offset
;
1095 parsedID
.setToBogus();
1096 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1097 parsedPos
= tmpPos
.getIndex();
1102 if (parsedPos
< maxPos
&&
1103 (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT_SHORT
]) == 0) {
1104 tmpPos
.setIndex(startIdx
);
1105 tmpPos
.setErrorIndex(-1);
1107 UBool hasDigitOffset
= FALSE
;
1108 offset
= parseOffsetLocalizedGMT(text
, tmpPos
, TRUE
, &hasDigitOffset
);
1109 if (tmpPos
.getErrorIndex() == -1) {
1110 if (tmpPos
.getIndex() == maxPos
|| hasDigitOffset
) {
1111 pos
.setIndex(tmpPos
.getIndex());
1112 return createTimeZoneForOffset(offset
);
1114 // Evaluate other names - see the comment earlier in this method.
1115 if (parsedPos
< tmpPos
.getIndex()) {
1116 parsedOffset
= offset
;
1117 parsedID
.setToBogus();
1118 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1119 parsedPos
= tmpPos
.getIndex();
1124 // When ParseOption.ALL_STYLES is available, we also try to look all possible display names and IDs.
1125 // For example, when style is GENERIC_LONG, "EST" (SPECIFIC_SHORT) is never
1126 // used for America/New_York. With parseAllStyles true, this code parses "EST"
1127 // as America/New_York.
1129 // Note: Adding all possible names into the trie used by the implementation is quite heavy operation,
1130 // which we want to avoid normally (note that we cache the trie, so this is applicable to the
1131 // first time only as long as the cache does not expire).
1133 if (parseOptions
& UTZFMT_PARSE_OPTION_ALL_STYLES
) {
1134 // Try all specific names and exemplar location names
1135 if (parsedPos
< maxPos
) {
1136 LocalPointer
<TimeZoneNames::MatchInfoCollection
> specificMatches(fTimeZoneNames
->find(text
, startIdx
, ALL_SIMPLE_NAME_TYPES
, status
));
1137 if (U_FAILURE(status
)) {
1138 pos
.setErrorIndex(startIdx
);
1141 int32_t specificMatchIdx
= -1;
1142 int32_t matchPos
= -1;
1143 if (!specificMatches
.isNull()) {
1144 for (int32_t i
= 0; i
< specificMatches
->size(); i
++) {
1145 if (startIdx
+ specificMatches
->getMatchLengthAt(i
) > matchPos
) {
1146 specificMatchIdx
= i
;
1147 matchPos
= startIdx
+ specificMatches
->getMatchLengthAt(i
);
1151 if (parsedPos
< matchPos
) {
1152 U_ASSERT(specificMatchIdx
>= 0);
1153 parsedPos
= matchPos
;
1154 getTimeZoneID(specificMatches
.getAlias(), specificMatchIdx
, parsedID
);
1155 parsedTimeType
= getTimeType(specificMatches
->getNameTypeAt(specificMatchIdx
));
1156 parsedOffset
= UNKNOWN_OFFSET
;
1159 // Try generic names
1160 if (parsedPos
< maxPos
) {
1161 int32_t genMatchLen
= -1;
1162 UTimeZoneFormatTimeType tt
= UTZFMT_TIME_TYPE_UNKNOWN
;
1164 const TimeZoneGenericNames
*gnames
= getTimeZoneGenericNames(status
);
1165 if (U_SUCCESS(status
)) {
1166 genMatchLen
= gnames
->findBestMatch(text
, startIdx
, ALL_GENERIC_NAME_TYPES
, tzID
, tt
, status
);
1168 if (U_FAILURE(status
)) {
1169 pos
.setErrorIndex(startIdx
);
1173 if (parsedPos
< startIdx
+ genMatchLen
) {
1174 parsedPos
= startIdx
+ genMatchLen
;
1175 parsedID
.setTo(tzID
);
1176 parsedTimeType
= tt
;
1177 parsedOffset
= UNKNOWN_OFFSET
;
1182 if (parsedPos
< maxPos
&& (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_ZONE_ID
]) == 0) {
1183 tmpPos
.setIndex(startIdx
);
1184 tmpPos
.setErrorIndex(-1);
1186 parseZoneID(text
, tmpPos
, tzID
);
1187 if (tmpPos
.getErrorIndex() == -1 && parsedPos
< tmpPos
.getIndex()) {
1188 parsedPos
= tmpPos
.getIndex();
1189 parsedID
.setTo(tzID
);
1190 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1191 parsedOffset
= UNKNOWN_OFFSET
;
1194 // Try short time zone ID
1195 if (parsedPos
< maxPos
&& (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_ZONE_ID
]) == 0) {
1196 tmpPos
.setIndex(startIdx
);
1197 tmpPos
.setErrorIndex(-1);
1199 parseShortZoneID(text
, tmpPos
, tzID
);
1200 if (tmpPos
.getErrorIndex() == -1 && parsedPos
< tmpPos
.getIndex()) {
1201 parsedPos
= tmpPos
.getIndex();
1202 parsedID
.setTo(tzID
);
1203 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1204 parsedOffset
= UNKNOWN_OFFSET
;
1209 if (parsedPos
> startIdx
) {
1210 // Parsed successfully
1212 if (parsedID
.length() > 0) {
1213 parsedTZ
= TimeZone::createTimeZone(parsedID
);
1215 U_ASSERT(parsedOffset
!= UNKNOWN_OFFSET
);
1216 parsedTZ
= createTimeZoneForOffset(parsedOffset
);
1219 *timeType
= parsedTimeType
;
1221 pos
.setIndex(parsedPos
);
1225 pos
.setErrorIndex(startIdx
);
1230 TimeZoneFormat::parseObject(const UnicodeString
& source
, Formattable
& result
,
1231 ParsePosition
& parse_pos
) const {
1232 result
.adoptObject(parse(UTZFMT_STYLE_GENERIC_LOCATION
, source
, parse_pos
, UTZFMT_PARSE_OPTION_ALL_STYLES
));
1236 // ------------------------------------------------------------------
1237 // Private zone name format/parse implementation
1240 TimeZoneFormat::formatGeneric(const TimeZone
& tz
, int32_t genType
, UDate date
, UnicodeString
& name
) const {
1241 UErrorCode status
= U_ZERO_ERROR
;
1242 const TimeZoneGenericNames
* gnames
= getTimeZoneGenericNames(status
);
1243 if (U_FAILURE(status
)) {
1248 if (genType
== UTZGNM_LOCATION
) {
1249 const UChar
* canonicalID
= ZoneMeta::getCanonicalCLDRID(tz
);
1250 if (canonicalID
== NULL
) {
1254 return gnames
->getGenericLocationName(UnicodeString(canonicalID
), name
);
1256 return gnames
->getDisplayName(tz
, (UTimeZoneGenericNameType
)genType
, date
, name
);
1260 TimeZoneFormat::formatSpecific(const TimeZone
& tz
, UTimeZoneNameType stdType
, UTimeZoneNameType dstType
,
1261 UDate date
, UnicodeString
& name
, UTimeZoneFormatTimeType
*timeType
) const {
1262 if (fTimeZoneNames
== NULL
) {
1267 UErrorCode status
= U_ZERO_ERROR
;
1268 UBool isDaylight
= tz
.inDaylightTime(date
, status
);
1269 const UChar
* canonicalID
= ZoneMeta::getCanonicalCLDRID(tz
);
1271 if (U_FAILURE(status
) || canonicalID
== NULL
) {
1277 fTimeZoneNames
->getDisplayName(UnicodeString(canonicalID
), dstType
, date
, name
);
1279 fTimeZoneNames
->getDisplayName(UnicodeString(canonicalID
), stdType
, date
, name
);
1282 if (timeType
&& !name
.isEmpty()) {
1283 *timeType
= isDaylight
? UTZFMT_TIME_TYPE_DAYLIGHT
: UTZFMT_TIME_TYPE_STANDARD
;
1288 const TimeZoneGenericNames
*
1289 TimeZoneFormat::getTimeZoneGenericNames(UErrorCode
& status
) const {
1290 if (U_FAILURE(status
)) {
1295 UMTX_CHECK(&gZoneMetaLock
, (fTimeZoneGenericNames
== NULL
), create
);
1297 TimeZoneFormat
*nonConstThis
= const_cast<TimeZoneFormat
*>(this);
1300 if (fTimeZoneGenericNames
== NULL
) {
1301 nonConstThis
->fTimeZoneGenericNames
= TimeZoneGenericNames::createInstance(fLocale
, status
);
1304 umtx_unlock(&gLock
);
1307 return fTimeZoneGenericNames
;
1311 TimeZoneFormat::formatExemplarLocation(const TimeZone
& tz
, UnicodeString
& name
) const {
1312 UnicodeString location
;
1313 const UChar
* canonicalID
= ZoneMeta::getCanonicalCLDRID(tz
);
1316 fTimeZoneNames
->getExemplarLocationName(UnicodeString(canonicalID
), location
);
1318 if (location
.length() > 0) {
1319 name
.setTo(location
);
1321 // Use "unknown" location
1322 fTimeZoneNames
->getExemplarLocationName(UnicodeString(UNKNOWN_ZONE_ID
), location
);
1323 if (location
.length() > 0) {
1324 name
.setTo(location
);
1327 name
.setTo(UNKNOWN_LOCATION
, -1);
1334 // ------------------------------------------------------------------
1335 // Zone offset format and parse
1338 TimeZoneFormat::formatOffsetISO8601Basic(int32_t offset
, UBool useUtcIndicator
, UBool isShort
, UBool ignoreSeconds
,
1339 UnicodeString
& result
, UErrorCode
& status
) const {
1340 return formatOffsetISO8601(offset
, TRUE
, useUtcIndicator
, isShort
, ignoreSeconds
, result
, status
);
1344 TimeZoneFormat::formatOffsetISO8601Extended(int32_t offset
, UBool useUtcIndicator
, UBool isShort
, UBool ignoreSeconds
,
1345 UnicodeString
& result
, UErrorCode
& status
) const {
1346 return formatOffsetISO8601(offset
, FALSE
, useUtcIndicator
, isShort
, ignoreSeconds
, result
, status
);
1350 TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset
, UnicodeString
& result
, UErrorCode
& status
) const {
1351 return formatOffsetLocalizedGMT(offset
, FALSE
, result
, status
);
1355 TimeZoneFormat::formatOffsetShortLocalizedGMT(int32_t offset
, UnicodeString
& result
, UErrorCode
& status
) const {
1356 return formatOffsetLocalizedGMT(offset
, TRUE
, result
, status
);
1360 TimeZoneFormat::parseOffsetISO8601(const UnicodeString
& text
, ParsePosition
& pos
) const {
1361 return parseOffsetISO8601(text
, pos
, FALSE
);
1365 TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString
& text
, ParsePosition
& pos
) const {
1366 return parseOffsetLocalizedGMT(text
, pos
, FALSE
, NULL
);
1370 TimeZoneFormat::parseOffsetShortLocalizedGMT(const UnicodeString
& text
, ParsePosition
& pos
) const {
1371 return parseOffsetLocalizedGMT(text
, pos
, TRUE
, NULL
);
1374 // ------------------------------------------------------------------
1375 // Private zone offset format/parse implementation
1378 TimeZoneFormat::formatOffsetISO8601(int32_t offset
, UBool isBasic
, UBool useUtcIndicator
,
1379 UBool isShort
, UBool ignoreSeconds
, UnicodeString
& result
, UErrorCode
& status
) const {
1380 if (U_FAILURE(status
)) {
1381 result
.setToBogus();
1384 int32_t absOffset
= offset
< 0 ? -offset
: offset
;
1385 if (useUtcIndicator
&& (absOffset
< MILLIS_PER_SECOND
|| (ignoreSeconds
&& absOffset
< MILLIS_PER_MINUTE
))) {
1386 result
.setTo(ISO8601_UTC
);
1390 OffsetFields minFields
= isShort
? FIELDS_H
: FIELDS_HM
;
1391 OffsetFields maxFields
= ignoreSeconds
? FIELDS_HM
: FIELDS_HMS
;
1392 UChar sep
= isBasic
? 0 : ISO8601_SEP
;
1394 // Note: FIELDS_HMS as maxFields is a CLDR/ICU extension. ISO 8601 specification does
1395 // not support seconds field.
1397 if (absOffset
>= MAX_OFFSET
) {
1398 result
.setToBogus();
1399 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1404 fields
[0] = absOffset
/ MILLIS_PER_HOUR
;
1405 absOffset
= absOffset
% MILLIS_PER_HOUR
;
1406 fields
[1] = absOffset
/ MILLIS_PER_MINUTE
;
1407 absOffset
= absOffset
% MILLIS_PER_MINUTE
;
1408 fields
[2] = absOffset
/ MILLIS_PER_SECOND
;
1410 U_ASSERT(fields
[0] >= 0 && fields
[0] <= MAX_OFFSET_HOUR
);
1411 U_ASSERT(fields
[1] >= 0 && fields
[1] <= MAX_OFFSET_MINUTE
);
1412 U_ASSERT(fields
[2] >= 0 && fields
[2] <= MAX_OFFSET_SECOND
);
1414 int32_t lastIdx
= maxFields
;
1415 while (lastIdx
> minFields
) {
1416 if (fields
[lastIdx
] != 0) {
1424 // if all output fields are 0s, do not use negative sign
1425 for (int32_t idx
= 0; idx
<= lastIdx
; idx
++) {
1426 if (fields
[idx
] != 0) {
1434 for (int32_t idx
= 0; idx
<= lastIdx
; idx
++) {
1435 if (sep
&& idx
!= 0) {
1438 result
.append((UChar
)(0x0030 + fields
[idx
]/10));
1439 result
.append((UChar
)(0x0030 + fields
[idx
]%10
));
1446 TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset
, UBool isShort
, UnicodeString
& result
, UErrorCode
& status
) const {
1447 if (U_FAILURE(status
)) {
1448 result
.setToBogus();
1451 if (offset
<= -MAX_OFFSET
|| offset
>= MAX_OFFSET
) {
1452 result
.setToBogus();
1453 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1458 result
.setTo(fGMTZeroFormat
);
1462 UBool positive
= TRUE
;
1468 int32_t offsetH
= offset
/ MILLIS_PER_HOUR
;
1469 offset
= offset
% MILLIS_PER_HOUR
;
1470 int32_t offsetM
= offset
/ MILLIS_PER_MINUTE
;
1471 offset
= offset
% MILLIS_PER_MINUTE
;
1472 int32_t offsetS
= offset
/ MILLIS_PER_SECOND
;
1474 U_ASSERT(offsetH
<= MAX_OFFSET_HOUR
&& offsetM
<= MAX_OFFSET_MINUTE
&& offsetS
<= MAX_OFFSET_SECOND
);
1476 const UVector
* offsetPatternItems
= NULL
;
1479 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_POSITIVE_HMS
];
1480 } else if (offsetM
!= 0 || !isShort
) {
1481 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_POSITIVE_HM
];
1483 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_POSITIVE_H
];
1487 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_NEGATIVE_HMS
];
1488 } else if (offsetM
!= 0 || !isShort
) {
1489 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_NEGATIVE_HM
];
1491 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_NEGATIVE_H
];
1495 U_ASSERT(offsetPatternItems
!= NULL
);
1497 // Building the GMT format string
1498 result
.setTo(fGMTPatternPrefix
);
1500 for (int32_t i
= 0; i
< offsetPatternItems
->size(); i
++) {
1501 const GMTOffsetField
* item
= (GMTOffsetField
*)offsetPatternItems
->elementAt(i
);
1502 GMTOffsetField::FieldType type
= item
->getType();
1505 case GMTOffsetField::TEXT
:
1506 result
.append(item
->getPatternText(), -1);
1509 case GMTOffsetField::HOUR
:
1510 appendOffsetDigits(result
, offsetH
, (isShort
? 1 : 2));
1513 case GMTOffsetField::MINUTE
:
1514 appendOffsetDigits(result
, offsetM
, 2);
1517 case GMTOffsetField::SECOND
:
1518 appendOffsetDigits(result
, offsetS
, 2);
1523 result
.append(fGMTPatternSuffix
);
1528 TimeZoneFormat::parseOffsetISO8601(const UnicodeString
& text
, ParsePosition
& pos
, UBool extendedOnly
, UBool
* hasDigitOffset
/* = NULL */) const {
1529 if (hasDigitOffset
) {
1530 *hasDigitOffset
= FALSE
;
1532 int32_t start
= pos
.getIndex();
1533 if (start
>= text
.length()) {
1534 pos
.setErrorIndex(start
);
1538 UChar firstChar
= text
.charAt(start
);
1539 if (firstChar
== ISO8601_UTC
|| firstChar
== (UChar
)(ISO8601_UTC
+ 0x20)) {
1540 // "Z" (or "z") - indicates UTC
1541 pos
.setIndex(start
+ 1);
1546 if (firstChar
== PLUS
) {
1548 } else if (firstChar
== MINUS
) {
1551 // Not an ISO 8601 offset string
1552 pos
.setErrorIndex(start
);
1555 ParsePosition
posOffset(start
+ 1);
1556 int32_t offset
= parseAsciiOffsetFields(text
, posOffset
, ISO8601_SEP
, FIELDS_H
, FIELDS_HMS
);
1557 if (posOffset
.getErrorIndex() == -1 && !extendedOnly
&& (posOffset
.getIndex() - start
<= 3)) {
1558 // If the text is successfully parsed as extended format with the options above, it can be also parsed
1559 // as basic format. For example, "0230" can be parsed as offset 2:00 (only first digits are valid for
1560 // extended format), but it can be parsed as offset 2:30 with basic format. We use longer result.
1561 ParsePosition
posBasic(start
+ 1);
1562 int32_t tmpOffset
= parseAbuttingAsciiOffsetFields(text
, posBasic
, FIELDS_H
, FIELDS_HMS
, FALSE
);
1563 if (posBasic
.getErrorIndex() == -1 && posBasic
.getIndex() > posOffset
.getIndex()) {
1565 posOffset
.setIndex(posBasic
.getIndex());
1569 if (posOffset
.getErrorIndex() != -1) {
1570 pos
.setErrorIndex(start
);
1574 pos
.setIndex(posOffset
.getIndex());
1575 if (hasDigitOffset
) {
1576 *hasDigitOffset
= TRUE
;
1578 return sign
* offset
;
1582 TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString
& text
, ParsePosition
& pos
, UBool isShort
, UBool
* hasDigitOffset
) const {
1583 int32_t start
= pos
.getIndex();
1585 int32_t parsedLength
= 0;
1587 if (hasDigitOffset
) {
1588 *hasDigitOffset
= FALSE
;
1591 offset
= parseOffsetLocalizedGMTPattern(text
, start
, isShort
, parsedLength
);
1593 // For now, parseOffsetLocalizedGMTPattern handles both long and short
1594 // formats, no matter isShort is true or false. This might be changed in future
1595 // when strict parsing is necessary, or different set of patterns are used for
1596 // short/long formats.
1598 if (parsedLength
== 0) {
1599 offset
= parseOffsetLocalizedGMTPattern(text
, start
, !isShort
, parsedLength
);
1603 if (parsedLength
> 0) {
1604 if (hasDigitOffset
) {
1605 *hasDigitOffset
= TRUE
;
1607 pos
.setIndex(start
+ parsedLength
);
1611 // Try the default patterns
1612 offset
= parseOffsetDefaultLocalizedGMT(text
, start
, parsedLength
);
1613 if (parsedLength
> 0) {
1614 if (hasDigitOffset
) {
1615 *hasDigitOffset
= TRUE
;
1617 pos
.setIndex(start
+ parsedLength
);
1621 // Check if this is a GMT zero format
1622 if (text
.caseCompare(start
, fGMTZeroFormat
.length(), fGMTZeroFormat
, 0) == 0) {
1623 pos
.setIndex(start
+ fGMTZeroFormat
.length());
1627 // Check if this is a default GMT zero format
1628 for (int32_t i
= 0; ALT_GMT_STRINGS
[i
][0] != 0; i
++) {
1629 const UChar
* defGMTZero
= ALT_GMT_STRINGS
[i
];
1630 int32_t defGMTZeroLen
= u_strlen(defGMTZero
);
1631 if (text
.caseCompare(start
, defGMTZeroLen
, defGMTZero
, 0) == 0) {
1632 pos
.setIndex(start
+ defGMTZeroLen
);
1638 pos
.setErrorIndex(start
);
1643 TimeZoneFormat::parseOffsetLocalizedGMTPattern(const UnicodeString
& text
, int32_t start
, UBool
/*isShort*/, int32_t& parsedLen
) const {
1644 int32_t idx
= start
;
1646 UBool parsed
= FALSE
;
1650 int32_t len
= fGMTPatternPrefix
.length();
1651 if (len
> 0 && text
.caseCompare(idx
, len
, fGMTPatternPrefix
, 0) != 0) {
1652 // prefix match failed
1658 offset
= parseOffsetFields(text
, idx
, FALSE
, len
);
1660 // offset field match failed
1665 len
= fGMTPatternSuffix
.length();
1666 if (len
> 0 && text
.caseCompare(idx
, len
, fGMTPatternSuffix
, 0) != 0) {
1674 parsedLen
= parsed
? idx
- start
: 0;
1679 TimeZoneFormat::parseOffsetFields(const UnicodeString
& text
, int32_t start
, UBool
/*isShort*/, int32_t& parsedLen
) const {
1686 int32_t offsetH
, offsetM
, offsetS
;
1687 offsetH
= offsetM
= offsetS
= 0;
1689 for (int32_t patidx
= 0; PARSE_GMT_OFFSET_TYPES
[patidx
] >= 0; patidx
++) {
1690 int32_t gmtPatType
= PARSE_GMT_OFFSET_TYPES
[patidx
];
1691 UVector
* items
= fGMTOffsetPatternItems
[gmtPatType
];
1692 U_ASSERT(items
!= NULL
);
1694 outLen
= parseOffsetFieldsWithPattern(text
, start
, items
, FALSE
, offsetH
, offsetM
, offsetS
);
1696 sign
= (gmtPatType
== UTZFMT_PAT_POSITIVE_H
|| gmtPatType
== UTZFMT_PAT_POSITIVE_HM
|| gmtPatType
== UTZFMT_PAT_POSITIVE_HMS
) ?
1702 if (outLen
> 0 && fAbuttingOffsetHoursAndMinutes
) {
1703 // When hours field is sabutting minutes field,
1704 // the parse result above may not be appropriate.
1705 // For example, "01020" is parsed as 01:02: above,
1706 // but it should be parsed as 00:10:20.
1708 int32_t tmpSign
= 1;
1709 int32_t tmpH
, tmpM
, tmpS
;
1711 for (int32_t patidx
= 0; PARSE_GMT_OFFSET_TYPES
[patidx
] >= 0; patidx
++) {
1712 int32_t gmtPatType
= PARSE_GMT_OFFSET_TYPES
[patidx
];
1713 UVector
* items
= fGMTOffsetPatternItems
[gmtPatType
];
1714 U_ASSERT(items
!= NULL
);
1716 // forcing parse to use single hour digit
1717 tmpLen
= parseOffsetFieldsWithPattern(text
, start
, items
, TRUE
, tmpH
, tmpM
, tmpS
);
1719 tmpSign
= (gmtPatType
== UTZFMT_PAT_POSITIVE_H
|| gmtPatType
== UTZFMT_PAT_POSITIVE_HM
|| gmtPatType
== UTZFMT_PAT_POSITIVE_HMS
) ?
1724 if (tmpLen
> outLen
) {
1725 // Better parse result with single hour digit
1735 offset
= ((((offsetH
* 60) + offsetM
) * 60) + offsetS
) * 1000 * sign
;
1743 TimeZoneFormat::parseOffsetFieldsWithPattern(const UnicodeString
& text
, int32_t start
,
1744 UVector
* patternItems
, UBool forceSingleHourDigit
, int32_t& hour
, int32_t& min
, int32_t& sec
) const {
1745 UBool failed
= FALSE
;
1746 int32_t offsetH
, offsetM
, offsetS
;
1747 offsetH
= offsetM
= offsetS
= 0;
1748 int32_t idx
= start
;
1750 for (int32_t i
= 0; i
< patternItems
->size(); i
++) {
1752 const GMTOffsetField
* field
= (const GMTOffsetField
*)patternItems
->elementAt(i
);
1753 GMTOffsetField::FieldType fieldType
= field
->getType();
1754 if (fieldType
== GMTOffsetField::TEXT
) {
1755 const UChar
* patStr
= field
->getPatternText();
1756 len
= u_strlen(patStr
);
1757 if (text
.caseCompare(idx
, len
, patStr
, 0) != 0) {
1763 if (fieldType
== GMTOffsetField::HOUR
) {
1764 uint8_t maxDigits
= forceSingleHourDigit
? 1 : 2;
1765 offsetH
= parseOffsetFieldWithLocalizedDigits(text
, idx
, 1, maxDigits
, 0, MAX_OFFSET_HOUR
, len
);
1766 } else if (fieldType
== GMTOffsetField::MINUTE
) {
1767 offsetM
= parseOffsetFieldWithLocalizedDigits(text
, idx
, 2, 2, 0, MAX_OFFSET_MINUTE
, len
);
1768 } else if (fieldType
== GMTOffsetField::SECOND
) {
1769 offsetS
= parseOffsetFieldWithLocalizedDigits(text
, idx
, 2, 2, 0, MAX_OFFSET_SECOND
, len
);
1781 hour
= min
= sec
= 0;
1793 TimeZoneFormat::parseAbuttingOffsetFields(const UnicodeString
& text
, int32_t start
, int32_t& parsedLen
) const {
1794 int32_t digits
[MAX_OFFSET_DIGITS
];
1795 int32_t parsed
[MAX_OFFSET_DIGITS
]; // accumulative offsets
1797 // Parse digits into int[]
1798 int32_t idx
= start
;
1800 int32_t numDigits
= 0;
1801 for (int32_t i
= 0; i
< MAX_OFFSET_DIGITS
; i
++) {
1802 digits
[i
] = parseSingleLocalizedDigit(text
, idx
, len
);
1803 if (digits
[i
] < 0) {
1807 parsed
[i
] = idx
- start
;
1811 if (numDigits
== 0) {
1817 while (numDigits
> 0) {
1822 U_ASSERT(numDigits
> 0 && numDigits
<= MAX_OFFSET_DIGITS
);
1823 switch (numDigits
) {
1828 hour
= digits
[0] * 10 + digits
[1];
1832 min
= digits
[1] * 10 + digits
[2];
1835 hour
= digits
[0] * 10 + digits
[1];
1836 min
= digits
[2] * 10 + digits
[3];
1840 min
= digits
[1] * 10 + digits
[2];
1841 sec
= digits
[3] * 10 + digits
[4];
1844 hour
= digits
[0] * 10 + digits
[1];
1845 min
= digits
[2] * 10 + digits
[3];
1846 sec
= digits
[4] * 10 + digits
[5];
1849 if (hour
<= MAX_OFFSET_HOUR
&& min
<= MAX_OFFSET_MINUTE
&& sec
<= MAX_OFFSET_SECOND
) {
1850 // found a valid combination
1851 offset
= hour
* MILLIS_PER_HOUR
+ min
* MILLIS_PER_MINUTE
+ sec
* MILLIS_PER_SECOND
;
1852 parsedLen
= parsed
[numDigits
- 1];
1861 TimeZoneFormat::parseOffsetDefaultLocalizedGMT(const UnicodeString
& text
, int start
, int32_t& parsedLen
) const {
1862 int32_t idx
= start
;
1867 // check global default GMT alternatives
1870 for (int32_t i
= 0; ALT_GMT_STRINGS
[i
][0] != 0; i
++) {
1871 const UChar
* gmt
= ALT_GMT_STRINGS
[i
];
1872 int32_t len
= u_strlen(gmt
);
1873 if (text
.caseCompare(start
, len
, gmt
, 0) == 0) {
1883 // offset needs a sign char and a digit at minimum
1884 if (idx
+ 1 >= text
.length()) {
1890 UChar c
= text
.charAt(idx
);
1893 } else if (c
== MINUS
) {
1901 // try the default pattern with the separator first
1902 int32_t lenWithSep
= 0;
1903 int32_t offsetWithSep
= parseDefaultOffsetFields(text
, idx
, DEFAULT_GMT_OFFSET_SEP
, lenWithSep
);
1904 if (lenWithSep
== text
.length() - idx
) {
1906 offset
= offsetWithSep
* sign
;
1909 // try abutting field pattern
1910 int32_t lenAbut
= 0;
1911 int32_t offsetAbut
= parseAbuttingOffsetFields(text
, idx
, lenAbut
);
1913 if (lenWithSep
> lenAbut
) {
1914 offset
= offsetWithSep
* sign
;
1917 offset
= offsetAbut
* sign
;
1921 parsed
= idx
- start
;
1929 TimeZoneFormat::parseDefaultOffsetFields(const UnicodeString
& text
, int32_t start
, UChar separator
, int32_t& parsedLen
) const {
1930 int32_t max
= text
.length();
1931 int32_t idx
= start
;
1933 int32_t hour
= 0, min
= 0, sec
= 0;
1938 hour
= parseOffsetFieldWithLocalizedDigits(text
, idx
, 1, 2, 0, MAX_OFFSET_HOUR
, len
);
1944 if (idx
+ 1 < max
&& text
.charAt(idx
) == separator
) {
1945 min
= parseOffsetFieldWithLocalizedDigits(text
, idx
+ 1, 2, 2, 0, MAX_OFFSET_MINUTE
, len
);
1951 if (idx
+ 1 < max
&& text
.charAt(idx
) == separator
) {
1952 sec
= parseOffsetFieldWithLocalizedDigits(text
, idx
+ 1, 2, 2, 0, MAX_OFFSET_SECOND
, len
);
1965 parsedLen
= idx
- start
;
1966 return hour
* MILLIS_PER_HOUR
+ min
* MILLIS_PER_MINUTE
+ sec
* MILLIS_PER_SECOND
;
1970 TimeZoneFormat::parseOffsetFieldWithLocalizedDigits(const UnicodeString
& text
, int32_t start
, uint8_t minDigits
, uint8_t maxDigits
, uint16_t minVal
, uint16_t maxVal
, int32_t& parsedLen
) const {
1974 int32_t numDigits
= 0;
1975 int32_t idx
= start
;
1976 int32_t digitLen
= 0;
1978 while (idx
< text
.length() && numDigits
< maxDigits
) {
1979 int32_t digit
= parseSingleLocalizedDigit(text
, idx
, digitLen
);
1983 int32_t tmpVal
= decVal
* 10 + digit
;
1984 if (tmpVal
> maxVal
) {
1992 // Note: maxVal is checked in the while loop
1993 if (numDigits
< minDigits
|| decVal
< minVal
) {
1997 parsedLen
= idx
- start
;
2004 TimeZoneFormat::parseSingleLocalizedDigit(const UnicodeString
& text
, int32_t start
, int32_t& len
) const {
2007 if (start
< text
.length()) {
2008 UChar32 cp
= text
.char32At(start
);
2010 // First, try digits configured for this instance
2011 for (int32_t i
= 0; i
< 10; i
++) {
2012 if (cp
== fGMTOffsetDigits
[i
]) {
2017 // If failed, check if this is a Unicode digit
2019 int32_t tmp
= u_charDigitValue(cp
);
2020 digit
= (tmp
>= 0 && tmp
<= 9) ? tmp
: -1;
2024 int32_t next
= text
.moveIndex32(start
, 1);
2032 TimeZoneFormat::formatOffsetWithAsciiDigits(int32_t offset
, UChar sep
, OffsetFields minFields
, OffsetFields maxFields
, UnicodeString
& result
) {
2033 U_ASSERT(maxFields
>= minFields
);
2034 U_ASSERT(offset
> -MAX_OFFSET
&& offset
< MAX_OFFSET
);
2044 fields
[0] = offset
/ MILLIS_PER_HOUR
;
2045 offset
= offset
% MILLIS_PER_HOUR
;
2046 fields
[1] = offset
/ MILLIS_PER_MINUTE
;
2047 offset
= offset
% MILLIS_PER_MINUTE
;
2048 fields
[2] = offset
/ MILLIS_PER_SECOND
;
2050 U_ASSERT(fields
[0] >= 0 && fields
[0] <= MAX_OFFSET_HOUR
);
2051 U_ASSERT(fields
[1] >= 0 && fields
[1] <= MAX_OFFSET_MINUTE
);
2052 U_ASSERT(fields
[2] >= 0 && fields
[2] <= MAX_OFFSET_SECOND
);
2054 int32_t lastIdx
= maxFields
;
2055 while (lastIdx
> minFields
) {
2056 if (fields
[lastIdx
] != 0) {
2062 for (int32_t idx
= 0; idx
<= lastIdx
; idx
++) {
2063 if (sep
&& idx
!= 0) {
2066 result
.append((UChar
)(0x0030 + fields
[idx
]/10));
2067 result
.append((UChar
)(0x0030 + fields
[idx
]%10
));
2074 TimeZoneFormat::parseAbuttingAsciiOffsetFields(const UnicodeString
& text
, ParsePosition
& pos
, OffsetFields minFields
, OffsetFields maxFields
, UBool fixedHourWidth
) {
2075 int32_t start
= pos
.getIndex();
2077 int32_t minDigits
= 2 * (minFields
+ 1) - (fixedHourWidth
? 0 : 1);
2078 int32_t maxDigits
= 2 * (maxFields
+ 1);
2080 U_ASSERT(maxDigits
<= MAX_OFFSET_DIGITS
);
2082 int32_t digits
[MAX_OFFSET_DIGITS
] = {};
2083 int32_t numDigits
= 0;
2084 int32_t idx
= start
;
2085 while (numDigits
< maxDigits
&& idx
< text
.length()) {
2086 UChar uch
= text
.charAt(idx
);
2087 int32_t digit
= DIGIT_VAL(uch
);
2091 digits
[numDigits
] = digit
;
2096 if (fixedHourWidth
&& (numDigits
& 1)) {
2097 // Fixed digits, so the number of digits must be even number. Truncating.
2101 if (numDigits
< minDigits
) {
2102 pos
.setErrorIndex(start
);
2106 int32_t hour
= 0, min
= 0, sec
= 0;
2107 UBool bParsed
= FALSE
;
2108 while (numDigits
>= minDigits
) {
2109 switch (numDigits
) {
2114 hour
= digits
[0] * 10 + digits
[1];
2118 min
= digits
[1] * 10 + digits
[2];
2121 hour
= digits
[0] * 10 + digits
[1];
2122 min
= digits
[2] * 10 + digits
[3];
2126 min
= digits
[1] * 10 + digits
[2];
2127 sec
= digits
[3] * 10 + digits
[4];
2130 hour
= digits
[0] * 10 + digits
[1];
2131 min
= digits
[2] * 10 + digits
[3];
2132 sec
= digits
[4] * 10 + digits
[5];
2136 if (hour
<= MAX_OFFSET_HOUR
&& min
<= MAX_OFFSET_MINUTE
&& sec
<= MAX_OFFSET_SECOND
) {
2137 // Successfully parsed
2143 numDigits
-= (fixedHourWidth
? 2 : 1);
2144 hour
= min
= sec
= 0;
2148 pos
.setErrorIndex(start
);
2151 pos
.setIndex(start
+ numDigits
);
2152 return ((((hour
* 60) + min
) * 60) + sec
) * 1000;
2156 TimeZoneFormat::parseAsciiOffsetFields(const UnicodeString
& text
, ParsePosition
& pos
, UChar sep
, OffsetFields minFields
, OffsetFields maxFields
) {
2157 int32_t start
= pos
.getIndex();
2158 int32_t fieldVal
[] = {0, 0, 0};
2159 int32_t fieldLen
[] = {0, -1, -1};
2160 for (int32_t idx
= start
, fieldIdx
= 0; idx
< text
.length() && fieldIdx
<= maxFields
; idx
++) {
2161 UChar c
= text
.charAt(idx
);
2163 if (fieldIdx
== 0) {
2164 if (fieldLen
[0] == 0) {
2168 // 1 digit hour, move to next field
2170 if (fieldLen
[fieldIdx
] != -1) {
2171 // premature minute or seconds field
2174 fieldLen
[fieldIdx
] = 0;
2177 } else if (fieldLen
[fieldIdx
] == -1) {
2178 // no separator after 2 digit field
2181 int32_t digit
= DIGIT_VAL(c
);
2186 fieldVal
[fieldIdx
] = fieldVal
[fieldIdx
] * 10 + digit
;
2187 fieldLen
[fieldIdx
]++;
2188 if (fieldLen
[fieldIdx
] >= 2) {
2189 // parsed 2 digits, move to next field
2195 int32_t parsedLen
= 0;
2196 int32_t parsedFields
= -1;
2199 if (fieldLen
[0] == 0) {
2202 if (fieldVal
[0] > MAX_OFFSET_HOUR
) {
2203 offset
= (fieldVal
[0] / 10) * MILLIS_PER_HOUR
;
2204 parsedFields
= FIELDS_H
;
2208 offset
= fieldVal
[0] * MILLIS_PER_HOUR
;
2209 parsedLen
= fieldLen
[0];
2210 parsedFields
= FIELDS_H
;
2213 if (fieldLen
[1] != 2 || fieldVal
[1] > MAX_OFFSET_MINUTE
) {
2216 offset
+= fieldVal
[1] * MILLIS_PER_MINUTE
;
2217 parsedLen
+= (1 + fieldLen
[1]);
2218 parsedFields
= FIELDS_HM
;
2221 if (fieldLen
[2] != 2 || fieldVal
[2] > MAX_OFFSET_SECOND
) {
2224 offset
+= fieldVal
[2] * MILLIS_PER_SECOND
;
2225 parsedLen
+= (1 + fieldLen
[2]);
2226 parsedFields
= FIELDS_HMS
;
2229 if (parsedFields
< minFields
) {
2230 pos
.setErrorIndex(start
);
2234 pos
.setIndex(start
+ parsedLen
);
2239 TimeZoneFormat::appendOffsetDigits(UnicodeString
& buf
, int32_t n
, uint8_t minDigits
) const {
2240 U_ASSERT(n
>= 0 && n
< 60);
2241 int32_t numDigits
= n
>= 10 ? 2 : 1;
2242 for (int32_t i
= 0; i
< minDigits
- numDigits
; i
++) {
2243 buf
.append(fGMTOffsetDigits
[0]);
2245 if (numDigits
== 2) {
2246 buf
.append(fGMTOffsetDigits
[n
/ 10]);
2248 buf
.append(fGMTOffsetDigits
[n
% 10]);
2251 // ------------------------------------------------------------------
2254 TimeZoneFormat::initGMTPattern(const UnicodeString
& gmtPattern
, UErrorCode
& status
) {
2255 if (U_FAILURE(status
)) {
2258 // This implementation not perfect, but sufficient practically.
2259 int32_t idx
= gmtPattern
.indexOf(ARG0
, ARG0_LEN
, 0);
2261 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2264 fGMTPattern
.setTo(gmtPattern
);
2265 unquote(gmtPattern
.tempSubString(0, idx
), fGMTPatternPrefix
);
2266 unquote(gmtPattern
.tempSubString(idx
+ ARG0_LEN
), fGMTPatternSuffix
);
2270 TimeZoneFormat::unquote(const UnicodeString
& pattern
, UnicodeString
& result
) {
2271 if (pattern
.indexOf(SINGLEQUOTE
) < 0) {
2272 result
.setTo(pattern
);
2276 UBool isPrevQuote
= FALSE
;
2277 UBool inQuote
= FALSE
;
2278 for (int32_t i
= 0; i
< pattern
.length(); i
++) {
2279 UChar c
= pattern
.charAt(i
);
2280 if (c
== SINGLEQUOTE
) {
2283 isPrevQuote
= FALSE
;
2289 isPrevQuote
= FALSE
;
2297 TimeZoneFormat::parseOffsetPattern(const UnicodeString
& pattern
, OffsetFields required
, UErrorCode
& status
) {
2298 if (U_FAILURE(status
)) {
2301 UVector
* result
= new UVector(deleteGMTOffsetField
, NULL
, status
);
2302 if (result
== NULL
) {
2303 status
= U_MEMORY_ALLOCATION_ERROR
;
2307 int32_t checkBits
= 0;
2308 UBool isPrevQuote
= FALSE
;
2309 UBool inQuote
= FALSE
;
2311 GMTOffsetField::FieldType itemType
= GMTOffsetField::TEXT
;
2312 int32_t itemLength
= 1;
2314 for (int32_t i
= 0; i
< pattern
.length(); i
++) {
2315 UChar ch
= pattern
.charAt(i
);
2316 if (ch
== SINGLEQUOTE
) {
2318 text
.append(SINGLEQUOTE
);
2319 isPrevQuote
= FALSE
;
2322 if (itemType
!= GMTOffsetField::TEXT
) {
2323 if (GMTOffsetField::isValid(itemType
, itemLength
)) {
2324 GMTOffsetField
* fld
= GMTOffsetField::createTimeField(itemType
, (uint8_t)itemLength
, status
);
2325 result
->addElement(fld
, status
);
2326 if (U_FAILURE(status
)) {
2330 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2333 itemType
= GMTOffsetField::TEXT
;
2338 isPrevQuote
= FALSE
;
2342 GMTOffsetField::FieldType tmpType
= GMTOffsetField::getTypeByLetter(ch
);
2343 if (tmpType
!= GMTOffsetField::TEXT
) {
2344 // an offset time pattern character
2345 if (tmpType
== itemType
) {
2348 if (itemType
== GMTOffsetField::TEXT
) {
2349 if (text
.length() > 0) {
2350 GMTOffsetField
* textfld
= GMTOffsetField::createText(text
, status
);
2351 result
->addElement(textfld
, status
);
2352 if (U_FAILURE(status
)) {
2358 if (GMTOffsetField::isValid(itemType
, itemLength
)) {
2359 GMTOffsetField
* fld
= GMTOffsetField::createTimeField(itemType
, itemLength
, status
);
2360 result
->addElement(fld
, status
);
2361 if (U_FAILURE(status
)) {
2365 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2371 checkBits
|= tmpType
;
2375 if (itemType
!= GMTOffsetField::TEXT
) {
2376 if (GMTOffsetField::isValid(itemType
, itemLength
)) {
2377 GMTOffsetField
* fld
= GMTOffsetField::createTimeField(itemType
, itemLength
, status
);
2378 result
->addElement(fld
, status
);
2379 if (U_FAILURE(status
)) {
2383 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2386 itemType
= GMTOffsetField::TEXT
;
2394 if (U_SUCCESS(status
)) {
2395 if (itemType
== GMTOffsetField::TEXT
) {
2396 if (text
.length() > 0) {
2397 GMTOffsetField
* tfld
= GMTOffsetField::createText(text
, status
);
2398 result
->addElement(tfld
, status
);
2401 if (GMTOffsetField::isValid(itemType
, itemLength
)) {
2402 GMTOffsetField
* fld
= GMTOffsetField::createTimeField(itemType
, itemLength
, status
);
2403 result
->addElement(fld
, status
);
2405 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2409 // Check all required fields are set
2410 if (U_SUCCESS(status
)) {
2411 int32_t reqBits
= 0;
2414 reqBits
= GMTOffsetField::HOUR
;
2417 reqBits
= GMTOffsetField::HOUR
| GMTOffsetField::MINUTE
;
2420 reqBits
= GMTOffsetField::HOUR
| GMTOffsetField::MINUTE
| GMTOffsetField::SECOND
;
2423 if (checkBits
== reqBits
) {
2424 // all required fields are set, no extra fields
2436 TimeZoneFormat::expandOffsetPattern(const UnicodeString
& offsetHM
, UnicodeString
& result
, UErrorCode
& status
) {
2437 result
.setToBogus();
2438 if (U_FAILURE(status
)) {
2441 U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN
) == 2);
2443 int32_t idx_mm
= offsetHM
.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN
, 2, 0);
2445 // Bad time zone hour pattern data
2446 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2451 int32_t idx_H
= offsetHM
.tempSubString(0, idx_mm
).lastIndexOf((UChar
)0x0048 /* H */);
2453 sep
= offsetHM
.tempSubString(idx_H
+ 1, idx_mm
- (idx_H
+ 1));
2455 result
.setTo(offsetHM
.tempSubString(0, idx_mm
+ 2));
2457 result
.append(DEFAULT_GMT_OFFSET_SECOND_PATTERN
, -1);
2458 result
.append(offsetHM
.tempSubString(idx_mm
+ 2));
2463 TimeZoneFormat::truncateOffsetPattern(const UnicodeString
& offsetHM
, UnicodeString
& result
, UErrorCode
& status
) {
2464 result
.setToBogus();
2465 if (U_FAILURE(status
)) {
2468 U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN
) == 2);
2470 int32_t idx_mm
= offsetHM
.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN
, 2, 0);
2472 // Bad time zone hour pattern data
2473 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2476 UChar HH
[] = {0x0048, 0x0048};
2477 int32_t idx_HH
= offsetHM
.tempSubString(0, idx_mm
).lastIndexOf(HH
, 2, 0);
2479 return result
.setTo(offsetHM
.tempSubString(0, idx_HH
+ 2));
2481 int32_t idx_H
= offsetHM
.tempSubString(0, idx_mm
).lastIndexOf((UChar
)0x0048, 0);
2483 return result
.setTo(offsetHM
.tempSubString(0, idx_H
+ 1));
2485 // Bad time zone hour pattern data
2486 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2491 TimeZoneFormat::initGMTOffsetPatterns(UErrorCode
& status
) {
2492 for (int32_t type
= 0; type
< UTZFMT_PAT_COUNT
; type
++) {
2494 case UTZFMT_PAT_POSITIVE_H
:
2495 case UTZFMT_PAT_NEGATIVE_H
:
2496 fGMTOffsetPatternItems
[type
] = parseOffsetPattern(fGMTOffsetPatterns
[type
], FIELDS_H
, status
);
2498 case UTZFMT_PAT_POSITIVE_HM
:
2499 case UTZFMT_PAT_NEGATIVE_HM
:
2500 fGMTOffsetPatternItems
[type
] = parseOffsetPattern(fGMTOffsetPatterns
[type
], FIELDS_HM
, status
);
2502 case UTZFMT_PAT_POSITIVE_HMS
:
2503 case UTZFMT_PAT_NEGATIVE_HMS
:
2504 fGMTOffsetPatternItems
[type
] = parseOffsetPattern(fGMTOffsetPatterns
[type
], FIELDS_HMS
, status
);
2508 checkAbuttingHoursAndMinutes();
2512 TimeZoneFormat::checkAbuttingHoursAndMinutes() {
2513 fAbuttingOffsetHoursAndMinutes
= FALSE
;
2514 for (int32_t type
= 0; type
< UTZFMT_PAT_COUNT
; type
++) {
2515 UBool afterH
= FALSE
;
2516 UVector
*items
= fGMTOffsetPatternItems
[type
];
2517 for (int32_t i
= 0; i
< items
->size(); i
++) {
2518 const GMTOffsetField
* item
= (GMTOffsetField
*)items
->elementAt(i
);
2519 GMTOffsetField::FieldType type
= item
->getType();
2520 if (type
!= GMTOffsetField::TEXT
) {
2522 fAbuttingOffsetHoursAndMinutes
= TRUE
;
2524 } else if (type
== GMTOffsetField::HOUR
) {
2527 } else if (afterH
) {
2531 if (fAbuttingOffsetHoursAndMinutes
) {
2538 TimeZoneFormat::toCodePoints(const UnicodeString
& str
, UChar32
* codeArray
, int32_t size
) {
2539 int32_t count
= str
.countChar32();
2540 if (count
!= size
) {
2544 for (int32_t idx
= 0, start
= 0; idx
< size
; idx
++) {
2545 codeArray
[idx
] = str
.char32At(start
);
2546 start
= str
.moveIndex32(start
, 1);
2553 TimeZoneFormat::createTimeZoneForOffset(int32_t offset
) const {
2555 // when offset is 0, we should use "Etc/GMT"
2556 return TimeZone::createTimeZone(UnicodeString(TZID_GMT
));
2558 return ZoneMeta::createCustomTimeZone(offset
);
2561 UTimeZoneFormatTimeType
2562 TimeZoneFormat::getTimeType(UTimeZoneNameType nameType
) {
2564 case UTZNM_LONG_STANDARD
:
2565 case UTZNM_SHORT_STANDARD
:
2566 return UTZFMT_TIME_TYPE_STANDARD
;
2568 case UTZNM_LONG_DAYLIGHT
:
2569 case UTZNM_SHORT_DAYLIGHT
:
2570 return UTZFMT_TIME_TYPE_DAYLIGHT
;
2575 return UTZFMT_TIME_TYPE_UNKNOWN
;
2579 TimeZoneFormat::getTimeZoneID(const TimeZoneNames::MatchInfoCollection
* matches
, int32_t idx
, UnicodeString
& tzID
) const {
2580 if (!matches
->getTimeZoneIDAt(idx
, tzID
)) {
2582 if (matches
->getMetaZoneIDAt(idx
, mzID
)) {
2583 fTimeZoneNames
->getReferenceZoneID(mzID
, fTargetRegion
, tzID
);
2590 class ZoneIdMatchHandler
: public TextTrieMapSearchResultHandler
{
2592 ZoneIdMatchHandler();
2593 virtual ~ZoneIdMatchHandler();
2595 UBool
handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
);
2596 const UChar
* getID();
2597 int32_t getMatchLen();
2603 ZoneIdMatchHandler::ZoneIdMatchHandler()
2604 : fLen(0), fID(NULL
) {
2607 ZoneIdMatchHandler::~ZoneIdMatchHandler() {
2611 ZoneIdMatchHandler::handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
) {
2612 if (U_FAILURE(status
)) {
2615 if (node
->hasValues()) {
2616 const UChar
* id
= (const UChar
*)node
->getValue(0);
2618 if (fLen
< matchLength
) {
2628 ZoneIdMatchHandler::getID() {
2633 ZoneIdMatchHandler::getMatchLen() {
2638 TimeZoneFormat::parseZoneID(const UnicodeString
& text
, ParsePosition
& pos
, UnicodeString
& tzID
) const {
2639 UErrorCode status
= U_ZERO_ERROR
;
2641 UMTX_CHECK(&gLock
, gZoneIdTrieInitialized
, initialized
);
2645 if (!gZoneIdTrieInitialized
) {
2646 StringEnumeration
*tzenum
= TimeZone::createEnumeration();
2647 TextTrieMap
* trie
= new TextTrieMap(TRUE
, NULL
); // No deleter, because values are pooled by ZoneMeta
2649 const UnicodeString
*id
;
2650 while ((id
= tzenum
->snext(status
))) {
2651 const UChar
* uid
= ZoneMeta::findTimeZoneID(*id
);
2653 trie
->put(uid
, const_cast<UChar
*>(uid
), status
);
2656 if (U_SUCCESS(status
)) {
2658 gZoneIdTrieInitialized
= initialized
= TRUE
;
2659 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEFORMAT
, tzfmt_cleanup
);
2667 umtx_unlock(&gLock
);
2670 int32_t start
= pos
.getIndex();
2675 LocalPointer
<ZoneIdMatchHandler
> handler(new ZoneIdMatchHandler());
2676 gZoneIdTrie
->search(text
, start
, handler
.getAlias(), status
);
2677 len
= handler
->getMatchLen();
2679 tzID
.setTo(handler
->getID(), -1);
2684 pos
.setIndex(start
+ len
);
2686 pos
.setErrorIndex(start
);
2693 TimeZoneFormat::parseShortZoneID(const UnicodeString
& text
, ParsePosition
& pos
, UnicodeString
& tzID
) const {
2694 UErrorCode status
= U_ZERO_ERROR
;
2696 UMTX_CHECK(&gLock
, gShortZoneIdTrieInitialized
, initialized
);
2700 if (!gShortZoneIdTrieInitialized
) {
2701 StringEnumeration
*tzenum
= TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL
, NULL
, NULL
, status
);
2702 if (U_SUCCESS(status
)) {
2703 TextTrieMap
* trie
= new TextTrieMap(TRUE
, NULL
); // No deleter, because values are pooled by ZoneMeta
2705 const UnicodeString
*id
;
2706 while ((id
= tzenum
->snext(status
))) {
2707 const UChar
* uID
= ZoneMeta::findTimeZoneID(*id
);
2708 const UChar
* shortID
= ZoneMeta::getShortID(*id
);
2709 if (shortID
&& uID
) {
2710 trie
->put(shortID
, const_cast<UChar
*>(uID
), status
);
2713 if (U_SUCCESS(status
)) {
2714 gShortZoneIdTrie
= trie
;
2715 gShortZoneIdTrieInitialized
= initialized
= TRUE
;
2716 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEFORMAT
, tzfmt_cleanup
);
2725 umtx_unlock(&gLock
);
2728 int32_t start
= pos
.getIndex();
2733 LocalPointer
<ZoneIdMatchHandler
> handler(new ZoneIdMatchHandler());
2734 gShortZoneIdTrie
->search(text
, start
, handler
.getAlias(), status
);
2735 len
= handler
->getMatchLen();
2737 tzID
.setTo(handler
->getID(), -1);
2742 pos
.setIndex(start
+ len
);
2744 pos
.setErrorIndex(start
);
2752 TimeZoneFormat::parseExemplarLocation(const UnicodeString
& text
, ParsePosition
& pos
, UnicodeString
& tzID
) const {
2753 int32_t startIdx
= pos
.getIndex();
2754 int32_t parsedPos
= -1;
2757 UErrorCode status
= U_ZERO_ERROR
;
2758 LocalPointer
<TimeZoneNames::MatchInfoCollection
> exemplarMatches(fTimeZoneNames
->find(text
, startIdx
, UTZNM_EXEMPLAR_LOCATION
, status
));
2759 if (U_FAILURE(status
)) {
2760 pos
.setErrorIndex(startIdx
);
2763 int32_t matchIdx
= -1;
2764 if (!exemplarMatches
.isNull()) {
2765 for (int32_t i
= 0; i
< exemplarMatches
->size(); i
++) {
2766 if (startIdx
+ exemplarMatches
->getMatchLengthAt(i
) > parsedPos
) {
2768 parsedPos
= startIdx
+ exemplarMatches
->getMatchLengthAt(i
);
2771 if (parsedPos
> 0) {
2772 pos
.setIndex(parsedPos
);
2773 getTimeZoneID(exemplarMatches
.getAlias(), matchIdx
, tzID
);
2777 if (tzID
.length() == 0) {
2778 pos
.setErrorIndex(startIdx
);