1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 2011-2015, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
10 #include "unicode/utypes.h"
12 #if !UCONFIG_NO_FORMATTING
14 #include "unicode/calendar.h"
15 #include "unicode/tzfmt.h"
16 #include "unicode/numsys.h"
17 #include "unicode/strenum.h"
18 #include "unicode/uchar.h"
19 #include "unicode/udat.h"
20 #include "unicode/ustring.h"
21 #include "unicode/utf16.h"
33 #include "tznames_impl.h" // TextTrieMap
34 #include "patternprops.h"
38 // Bit flags used by the parse method.
39 // The order must match UTimeZoneFormatStyle enum.
40 #define ISO_Z_STYLE_FLAG 0x0080
41 #define ISO_LOCAL_STYLE_FLAG 0x0100
42 static const int16_t STYLE_PARSE_FLAGS
[] = {
43 0x0001, // UTZFMT_STYLE_GENERIC_LOCATION,
44 0x0002, // UTZFMT_STYLE_GENERIC_LONG,
45 0x0004, // UTZFMT_STYLE_GENERIC_SHORT,
46 0x0008, // UTZFMT_STYLE_SPECIFIC_LONG,
47 0x0010, // UTZFMT_STYLE_SPECIFIC_SHORT,
48 0x0020, // UTZFMT_STYLE_LOCALIZED_GMT,
49 0x0040, // UTZFMT_STYLE_LOCALIZED_GMT_SHORT,
50 ISO_Z_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_SHORT,
51 ISO_LOCAL_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT,
52 ISO_Z_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_FIXED,
53 ISO_LOCAL_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED,
54 ISO_Z_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_FULL,
55 ISO_LOCAL_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL,
56 ISO_Z_STYLE_FLAG
, // UTZFMT_STYLE_ISO_EXTENDED_FIXED,
57 ISO_LOCAL_STYLE_FLAG
, // UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED,
58 ISO_Z_STYLE_FLAG
, // UTZFMT_STYLE_ISO_EXTENDED_FULL,
59 ISO_LOCAL_STYLE_FLAG
, // UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL,
60 0x0200, // UTZFMT_STYLE_ZONE_ID,
61 0x0400, // UTZFMT_STYLE_ZONE_ID_SHORT,
62 0x0800 // UTZFMT_STYLE_EXEMPLAR_LOCATION
65 static const char gZoneStringsTag
[] = "zoneStrings";
66 static const char gGmtFormatTag
[]= "gmtFormat";
67 static const char gGmtZeroFormatTag
[] = "gmtZeroFormat";
68 static const char gHourFormatTag
[]= "hourFormat";
70 static const UChar TZID_GMT
[] = {0x0045, 0x0074, 0x0063, 0x002F, 0x0047, 0x004D, 0x0054, 0}; // Etc/GMT
71 static const UChar UNKNOWN_ZONE_ID
[] = {
72 0x0045, 0x0074, 0x0063, 0x002F, 0x0055, 0x006E, 0x006B, 0x006E, 0x006F, 0x0077, 0x006E, 0}; // Etc/Unknown
73 static const UChar UNKNOWN_SHORT_ZONE_ID
[] = {0x0075, 0x006E, 0x006B, 0}; // unk
74 static const UChar UNKNOWN_LOCATION
[] = {0x0055, 0x006E, 0x006B, 0x006E, 0x006F, 0x0077, 0x006E, 0}; // Unknown
76 static const UChar DEFAULT_GMT_PATTERN
[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0}; // GMT{0}
77 //static const UChar DEFAULT_GMT_ZERO[] = {0x0047, 0x004D, 0x0054, 0}; // GMT
78 static const UChar DEFAULT_GMT_POSITIVE_HM
[] = {0x002B, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // +H:mm
79 static const UChar DEFAULT_GMT_POSITIVE_HMS
[] = {0x002B, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // +H:mm:ss
80 static const UChar DEFAULT_GMT_NEGATIVE_HM
[] = {0x002D, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // -H:mm
81 static const UChar DEFAULT_GMT_NEGATIVE_HMS
[] = {0x002D, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // -H:mm:ss
82 static const UChar DEFAULT_GMT_POSITIVE_H
[] = {0x002B, 0x0048, 0}; // +H
83 static const UChar DEFAULT_GMT_NEGATIVE_H
[] = {0x002D, 0x0048, 0}; // -H
85 static const UChar32 DEFAULT_GMT_DIGITS
[] = {
86 0x0030, 0x0031, 0x0032, 0x0033, 0x0034,
87 0x0035, 0x0036, 0x0037, 0x0038, 0x0039
90 static const UChar DEFAULT_GMT_OFFSET_SEP
= 0x003A; // ':'
92 static const UChar ARG0
[] = {0x007B, 0x0030, 0x007D}; // "{0}"
93 static const int32_t ARG0_LEN
= 3;
95 static const UChar DEFAULT_GMT_OFFSET_MINUTE_PATTERN
[] = {0x006D, 0x006D, 0}; // "mm"
96 static const UChar DEFAULT_GMT_OFFSET_SECOND_PATTERN
[] = {0x0073, 0x0073, 0}; // "ss"
98 static const UChar ALT_GMT_STRINGS
[][4] = {
99 {0x0047, 0x004D, 0x0054, 0}, // GMT
100 {0x0055, 0x0054, 0x0043, 0}, // UTC
101 {0x0055, 0x0054, 0, 0}, // UT
105 // Order of GMT offset pattern parsing, *_HMS must be evaluated first
106 // because *_HM is most likely a substring of *_HMS
107 static const int32_t PARSE_GMT_OFFSET_TYPES
[] = {
108 UTZFMT_PAT_POSITIVE_HMS
,
109 UTZFMT_PAT_NEGATIVE_HMS
,
110 UTZFMT_PAT_POSITIVE_HM
,
111 UTZFMT_PAT_NEGATIVE_HM
,
112 UTZFMT_PAT_POSITIVE_H
,
113 UTZFMT_PAT_NEGATIVE_H
,
117 static const UChar SINGLEQUOTE
= 0x0027;
118 static const UChar PLUS
= 0x002B;
119 static const UChar MINUS
= 0x002D;
120 static const UChar ISO8601_UTC
= 0x005A; // 'Z'
121 static const UChar ISO8601_SEP
= 0x003A; // ':'
123 static const int32_t MILLIS_PER_HOUR
= 60 * 60 * 1000;
124 static const int32_t MILLIS_PER_MINUTE
= 60 * 1000;
125 static const int32_t MILLIS_PER_SECOND
= 1000;
127 // Maximum offset (exclusive) in millisecond supported by offset formats
128 static int32_t MAX_OFFSET
= 24 * MILLIS_PER_HOUR
;
130 // Maximum values for GMT offset fields
131 static const int32_t MAX_OFFSET_HOUR
= 23;
132 static const int32_t MAX_OFFSET_MINUTE
= 59;
133 static const int32_t MAX_OFFSET_SECOND
= 59;
135 static const int32_t UNKNOWN_OFFSET
= 0x7FFFFFFF;
137 static const int32_t ALL_SIMPLE_NAME_TYPES
= UTZNM_LONG_STANDARD
| UTZNM_LONG_DAYLIGHT
| UTZNM_SHORT_STANDARD
| UTZNM_SHORT_DAYLIGHT
| UTZNM_EXEMPLAR_LOCATION
;
138 static const int32_t ALL_GENERIC_NAME_TYPES
= UTZGNM_LOCATION
| UTZGNM_LONG
| UTZGNM_SHORT
;
140 #define DIGIT_VAL(c) (0x0030 <= (c) && (c) <= 0x0039 ? (c) - 0x0030 : -1)
141 #define MAX_OFFSET_DIGITS 6
143 // Time Zone ID/Short ID trie
144 static TextTrieMap
*gZoneIdTrie
= NULL
;
145 static icu::UInitOnce gZoneIdTrieInitOnce
= U_INITONCE_INITIALIZER
;
147 static TextTrieMap
*gShortZoneIdTrie
= NULL
;
148 static icu::UInitOnce gShortZoneIdTrieInitOnce
= U_INITONCE_INITIALIZER
;
150 static UMutex
*gLock() {
151 static UMutex
*m
= STATIC_NEW(UMutex
);
157 * Cleanup callback func
159 static UBool U_CALLCONV
tzfmt_cleanup(void)
161 if (gZoneIdTrie
!= NULL
) {
165 gZoneIdTrieInitOnce
.reset();
167 if (gShortZoneIdTrie
!= NULL
) {
168 delete gShortZoneIdTrie
;
170 gShortZoneIdTrie
= NULL
;
171 gShortZoneIdTrieInitOnce
.reset();
177 // ------------------------------------------------------------------
180 // This class represents a localized GMT offset pattern
181 // item and used by TimeZoneFormat
182 // ------------------------------------------------------------------
183 class GMTOffsetField
: public UMemory
{
192 virtual ~GMTOffsetField();
194 static GMTOffsetField
* createText(const UnicodeString
& text
, UErrorCode
& status
);
195 static GMTOffsetField
* createTimeField(FieldType type
, uint8_t width
, UErrorCode
& status
);
196 static UBool
isValid(FieldType type
, int32_t width
);
197 static FieldType
getTypeByLetter(UChar ch
);
199 FieldType
getType() const;
200 uint8_t getWidth() const;
201 const UChar
* getPatternText(void) const;
211 GMTOffsetField::GMTOffsetField()
212 : fText(NULL
), fType(TEXT
), fWidth(0) {
215 GMTOffsetField::~GMTOffsetField() {
222 GMTOffsetField::createText(const UnicodeString
& text
, UErrorCode
& status
) {
223 if (U_FAILURE(status
)) {
226 GMTOffsetField
* result
= new GMTOffsetField();
227 if (result
== NULL
) {
228 status
= U_MEMORY_ALLOCATION_ERROR
;
232 int32_t len
= text
.length();
233 result
->fText
= (UChar
*)uprv_malloc((len
+ 1) * sizeof(UChar
));
234 if (result
->fText
== NULL
) {
235 status
= U_MEMORY_ALLOCATION_ERROR
;
239 u_strncpy(result
->fText
, text
.getBuffer(), len
);
240 result
->fText
[len
] = 0;
241 result
->fType
= TEXT
;
247 GMTOffsetField::createTimeField(FieldType type
, uint8_t width
, UErrorCode
& status
) {
248 U_ASSERT(type
!= TEXT
);
249 if (U_FAILURE(status
)) {
252 GMTOffsetField
* result
= new GMTOffsetField();
253 if (result
== NULL
) {
254 status
= U_MEMORY_ALLOCATION_ERROR
;
258 result
->fType
= type
;
259 result
->fWidth
= width
;
265 GMTOffsetField::isValid(FieldType type
, int32_t width
) {
268 return (width
== 1 || width
== 2);
278 GMTOffsetField::FieldType
279 GMTOffsetField::getTypeByLetter(UChar ch
) {
280 if (ch
== 0x0048 /* H */) {
282 } else if (ch
== 0x006D /* m */) {
284 } else if (ch
== 0x0073 /* s */) {
290 inline GMTOffsetField::FieldType
291 GMTOffsetField::getType() const {
296 GMTOffsetField::getWidth() const {
301 GMTOffsetField::getPatternText(void) const {
307 static void U_CALLCONV
308 deleteGMTOffsetField(void *obj
) {
309 delete static_cast<GMTOffsetField
*>(obj
);
314 // ------------------------------------------------------------------
316 // ------------------------------------------------------------------
317 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeZoneFormat
)
319 TimeZoneFormat::TimeZoneFormat(const Locale
& locale
, UErrorCode
& status
)
320 : fLocale(locale
), fTimeZoneNames(NULL
), fTimeZoneGenericNames(NULL
),
321 fDefParseOptionFlags(0), fTZDBTimeZoneNames(NULL
) {
323 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
; i
++) {
324 fGMTOffsetPatternItems
[i
] = NULL
;
327 const char* region
= fLocale
.getCountry();
328 int32_t regionLen
= static_cast<int32_t>(uprv_strlen(region
));
329 if (regionLen
== 0) {
330 char loc
[ULOC_FULLNAME_CAPACITY
];
331 uloc_addLikelySubtags(fLocale
.getName(), loc
, sizeof(loc
), &status
);
333 regionLen
= uloc_getCountry(loc
, fTargetRegion
, sizeof(fTargetRegion
), &status
);
334 if (U_SUCCESS(status
)) {
335 fTargetRegion
[regionLen
] = 0;
339 } else if (regionLen
< (int32_t)sizeof(fTargetRegion
)) {
340 uprv_strcpy(fTargetRegion
, region
);
342 fTargetRegion
[0] = 0;
345 fTimeZoneNames
= TimeZoneNames::createInstance(locale
, status
);
346 // fTimeZoneGenericNames is lazily instantiated
347 if (U_FAILURE(status
)) {
351 const UChar
* gmtPattern
= NULL
;
352 const UChar
* hourFormats
= NULL
;
354 UResourceBundle
*zoneBundle
= ures_open(U_ICUDATA_ZONE
, locale
.getName(), &status
);
355 UResourceBundle
*zoneStringsArray
= ures_getByKeyWithFallback(zoneBundle
, gZoneStringsTag
, NULL
, &status
);
356 if (U_SUCCESS(status
)) {
359 resStr
= ures_getStringByKeyWithFallback(zoneStringsArray
, gGmtFormatTag
, &len
, &status
);
363 resStr
= ures_getStringByKeyWithFallback(zoneStringsArray
, gGmtZeroFormatTag
, &len
, &status
);
365 fGMTZeroFormat
.setTo(TRUE
, resStr
, len
);
367 resStr
= ures_getStringByKeyWithFallback(zoneStringsArray
, gHourFormatTag
, &len
, &status
);
369 hourFormats
= resStr
;
371 ures_close(zoneStringsArray
);
372 ures_close(zoneBundle
);
375 if (gmtPattern
== NULL
) {
376 gmtPattern
= DEFAULT_GMT_PATTERN
;
378 initGMTPattern(UnicodeString(TRUE
, gmtPattern
, -1), status
);
380 UBool useDefaultOffsetPatterns
= TRUE
;
382 UChar
*sep
= u_strchr(hourFormats
, (UChar
)0x003B /* ';' */);
384 UErrorCode tmpStatus
= U_ZERO_ERROR
;
385 fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HM
].setTo(FALSE
, hourFormats
, (int32_t)(sep
- hourFormats
));
386 fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HM
].setTo(TRUE
, sep
+ 1, -1);
387 expandOffsetPattern(fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HM
], fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HMS
], tmpStatus
);
388 expandOffsetPattern(fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HM
], fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HMS
], tmpStatus
);
389 truncateOffsetPattern(fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HM
], fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_H
], tmpStatus
);
390 truncateOffsetPattern(fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HM
], fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_H
], tmpStatus
);
391 if (U_SUCCESS(tmpStatus
)) {
392 useDefaultOffsetPatterns
= FALSE
;
396 if (useDefaultOffsetPatterns
) {
397 fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_H
].setTo(TRUE
, DEFAULT_GMT_POSITIVE_H
, -1);
398 fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HM
].setTo(TRUE
, DEFAULT_GMT_POSITIVE_HM
, -1);
399 fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HMS
].setTo(TRUE
, DEFAULT_GMT_POSITIVE_HMS
, -1);
400 fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_H
].setTo(TRUE
, DEFAULT_GMT_NEGATIVE_H
, -1);
401 fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HM
].setTo(TRUE
, DEFAULT_GMT_NEGATIVE_HM
, -1);
402 fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HMS
].setTo(TRUE
, DEFAULT_GMT_NEGATIVE_HMS
, -1);
404 initGMTOffsetPatterns(status
);
406 NumberingSystem
* ns
= NumberingSystem::createInstance(locale
, status
);
407 UBool useDefDigits
= TRUE
;
408 if (ns
&& !ns
->isAlgorithmic()) {
409 UnicodeString digits
= ns
->getDescription();
410 useDefDigits
= !toCodePoints(digits
, fGMTOffsetDigits
, 10);
413 uprv_memcpy(fGMTOffsetDigits
, DEFAULT_GMT_DIGITS
, sizeof(UChar32
) * 10);
418 TimeZoneFormat::TimeZoneFormat(const TimeZoneFormat
& other
)
419 : Format(other
), fTimeZoneNames(NULL
), fTimeZoneGenericNames(NULL
),
420 fTZDBTimeZoneNames(NULL
) {
422 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
; i
++) {
423 fGMTOffsetPatternItems
[i
] = NULL
;
429 TimeZoneFormat::~TimeZoneFormat() {
430 delete fTimeZoneNames
;
431 delete fTimeZoneGenericNames
;
432 delete fTZDBTimeZoneNames
;
433 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
; i
++) {
434 delete fGMTOffsetPatternItems
[i
];
439 TimeZoneFormat::operator=(const TimeZoneFormat
& other
) {
440 if (this == &other
) {
444 delete fTimeZoneNames
;
445 delete fTimeZoneGenericNames
;
446 fTimeZoneGenericNames
= NULL
;
447 delete fTZDBTimeZoneNames
;
448 fTZDBTimeZoneNames
= NULL
;
450 fLocale
= other
.fLocale
;
451 uprv_memcpy(fTargetRegion
, other
.fTargetRegion
, sizeof(fTargetRegion
));
453 fTimeZoneNames
= other
.fTimeZoneNames
->clone();
454 if (other
.fTimeZoneGenericNames
) {
455 // TODO: this test has dubious thread safety.
456 fTimeZoneGenericNames
= other
.fTimeZoneGenericNames
->clone();
459 fGMTPattern
= other
.fGMTPattern
;
460 fGMTPatternPrefix
= other
.fGMTPatternPrefix
;
461 fGMTPatternSuffix
= other
.fGMTPatternSuffix
;
463 UErrorCode status
= U_ZERO_ERROR
;
464 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
; i
++) {
465 fGMTOffsetPatterns
[i
] = other
.fGMTOffsetPatterns
[i
];
466 delete fGMTOffsetPatternItems
[i
];
467 fGMTOffsetPatternItems
[i
] = NULL
;
469 initGMTOffsetPatterns(status
);
470 U_ASSERT(U_SUCCESS(status
));
472 fGMTZeroFormat
= other
.fGMTZeroFormat
;
474 uprv_memcpy(fGMTOffsetDigits
, other
.fGMTOffsetDigits
, sizeof(fGMTOffsetDigits
));
476 fDefParseOptionFlags
= other
.fDefParseOptionFlags
;
483 TimeZoneFormat::operator==(const Format
& other
) const {
484 TimeZoneFormat
* tzfmt
= (TimeZoneFormat
*)&other
;
487 fLocale
== tzfmt
->fLocale
488 && fGMTPattern
== tzfmt
->fGMTPattern
489 && fGMTZeroFormat
== tzfmt
->fGMTZeroFormat
490 && *fTimeZoneNames
== *tzfmt
->fTimeZoneNames
;
492 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
&& isEqual
; i
++) {
493 isEqual
= fGMTOffsetPatterns
[i
] == tzfmt
->fGMTOffsetPatterns
[i
];
495 for (int32_t i
= 0; i
< 10 && isEqual
; i
++) {
496 isEqual
= fGMTOffsetDigits
[i
] == tzfmt
->fGMTOffsetDigits
[i
];
499 // Check fTimeZoneGenericNames. For now,
500 // if fTimeZoneNames is same, fTimeZoneGenericNames should
501 // be also equivalent.
506 TimeZoneFormat::clone() const {
507 return new TimeZoneFormat(*this);
510 TimeZoneFormat
* U_EXPORT2
511 TimeZoneFormat::createInstance(const Locale
& locale
, UErrorCode
& status
) {
512 TimeZoneFormat
* tzfmt
= new TimeZoneFormat(locale
, status
);
513 if (U_SUCCESS(status
)) {
520 // ------------------------------------------------------------------
524 TimeZoneFormat::getTimeZoneNames() const {
525 return (const TimeZoneNames
*)fTimeZoneNames
;
529 TimeZoneFormat::adoptTimeZoneNames(TimeZoneNames
*tznames
) {
530 delete fTimeZoneNames
;
531 fTimeZoneNames
= tznames
;
533 // TODO - We should also update fTimeZoneGenericNames
537 TimeZoneFormat::setTimeZoneNames(const TimeZoneNames
&tznames
) {
538 delete fTimeZoneNames
;
539 fTimeZoneNames
= tznames
.clone();
541 // TODO - We should also update fTimeZoneGenericNames
545 TimeZoneFormat::setDefaultParseOptions(uint32_t flags
) {
546 fDefParseOptionFlags
= flags
;
550 TimeZoneFormat::getDefaultParseOptions(void) const {
551 return fDefParseOptionFlags
;
556 TimeZoneFormat::getGMTPattern(UnicodeString
& pattern
) const {
557 return pattern
.setTo(fGMTPattern
);
561 TimeZoneFormat::setGMTPattern(const UnicodeString
& pattern
, UErrorCode
& status
) {
562 initGMTPattern(pattern
, status
);
566 TimeZoneFormat::getGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type
, UnicodeString
& pattern
) const {
567 return pattern
.setTo(fGMTOffsetPatterns
[type
]);
571 TimeZoneFormat::setGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type
, const UnicodeString
& pattern
, UErrorCode
& status
) {
572 if (U_FAILURE(status
)) {
575 if (pattern
== fGMTOffsetPatterns
[type
]) {
580 OffsetFields required
= FIELDS_HM
;
582 case UTZFMT_PAT_POSITIVE_H
:
583 case UTZFMT_PAT_NEGATIVE_H
:
586 case UTZFMT_PAT_POSITIVE_HM
:
587 case UTZFMT_PAT_NEGATIVE_HM
:
588 required
= FIELDS_HM
;
590 case UTZFMT_PAT_POSITIVE_HMS
:
591 case UTZFMT_PAT_NEGATIVE_HMS
:
592 required
= FIELDS_HMS
;
598 UVector
* patternItems
= parseOffsetPattern(pattern
, required
, status
);
599 if (patternItems
== NULL
) {
603 fGMTOffsetPatterns
[type
].setTo(pattern
);
604 delete fGMTOffsetPatternItems
[type
];
605 fGMTOffsetPatternItems
[type
] = patternItems
;
606 checkAbuttingHoursAndMinutes();
610 TimeZoneFormat::getGMTOffsetDigits(UnicodeString
& digits
) const {
612 for (int32_t i
= 0; i
< 10; i
++) {
613 digits
.append(fGMTOffsetDigits
[i
]);
619 TimeZoneFormat::setGMTOffsetDigits(const UnicodeString
& digits
, UErrorCode
& status
) {
620 if (U_FAILURE(status
)) {
623 UChar32 digitArray
[10];
624 if (!toCodePoints(digits
, digitArray
, 10)) {
625 status
= U_ILLEGAL_ARGUMENT_ERROR
;
628 uprv_memcpy(fGMTOffsetDigits
, digitArray
, sizeof(UChar32
)*10);
632 TimeZoneFormat::getGMTZeroFormat(UnicodeString
& gmtZeroFormat
) const {
633 return gmtZeroFormat
.setTo(fGMTZeroFormat
);
637 TimeZoneFormat::setGMTZeroFormat(const UnicodeString
& gmtZeroFormat
, UErrorCode
& status
) {
638 if (U_SUCCESS(status
)) {
639 if (gmtZeroFormat
.isEmpty()) {
640 status
= U_ILLEGAL_ARGUMENT_ERROR
;
641 } else if (gmtZeroFormat
!= fGMTZeroFormat
) {
642 fGMTZeroFormat
.setTo(gmtZeroFormat
);
647 // ------------------------------------------------------------------
651 TimeZoneFormat::format(UTimeZoneFormatStyle style
, const TimeZone
& tz
, UDate date
,
652 UnicodeString
& name
, UTimeZoneFormatTimeType
* timeType
/* = NULL */) const {
654 *timeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
657 UBool noOffsetFormatFallback
= FALSE
;
660 case UTZFMT_STYLE_GENERIC_LOCATION
:
661 formatGeneric(tz
, UTZGNM_LOCATION
, date
, name
);
663 case UTZFMT_STYLE_GENERIC_LONG
:
664 formatGeneric(tz
, UTZGNM_LONG
, date
, name
);
666 case UTZFMT_STYLE_GENERIC_SHORT
:
667 formatGeneric(tz
, UTZGNM_SHORT
, date
, name
);
669 case UTZFMT_STYLE_SPECIFIC_LONG
:
670 formatSpecific(tz
, UTZNM_LONG_STANDARD
, UTZNM_LONG_DAYLIGHT
, date
, name
, timeType
);
672 case UTZFMT_STYLE_SPECIFIC_SHORT
:
673 formatSpecific(tz
, UTZNM_SHORT_STANDARD
, UTZNM_SHORT_DAYLIGHT
, date
, name
, timeType
);
676 case UTZFMT_STYLE_ZONE_ID
:
678 noOffsetFormatFallback
= TRUE
;
680 case UTZFMT_STYLE_ZONE_ID_SHORT
:
682 const UChar
* shortID
= ZoneMeta::getShortID(tz
);
683 if (shortID
== NULL
) {
684 shortID
= UNKNOWN_SHORT_ZONE_ID
;
686 name
.setTo(shortID
, -1);
688 noOffsetFormatFallback
= TRUE
;
691 case UTZFMT_STYLE_EXEMPLAR_LOCATION
:
692 formatExemplarLocation(tz
, name
);
693 noOffsetFormatFallback
= TRUE
;
697 // will be handled below
701 if (name
.isEmpty() && !noOffsetFormatFallback
) {
702 UErrorCode status
= U_ZERO_ERROR
;
703 int32_t rawOffset
, dstOffset
;
704 tz
.getOffset(date
, FALSE
, rawOffset
, dstOffset
, status
);
705 int32_t offset
= rawOffset
+ dstOffset
;
706 if (U_SUCCESS(status
)) {
708 case UTZFMT_STYLE_GENERIC_LOCATION
:
709 case UTZFMT_STYLE_GENERIC_LONG
:
710 case UTZFMT_STYLE_SPECIFIC_LONG
:
711 case UTZFMT_STYLE_LOCALIZED_GMT
:
712 formatOffsetLocalizedGMT(offset
, name
, status
);
715 case UTZFMT_STYLE_GENERIC_SHORT
:
716 case UTZFMT_STYLE_SPECIFIC_SHORT
:
717 case UTZFMT_STYLE_LOCALIZED_GMT_SHORT
:
718 formatOffsetShortLocalizedGMT(offset
, name
, status
);
721 case UTZFMT_STYLE_ISO_BASIC_SHORT
:
722 formatOffsetISO8601Basic(offset
, TRUE
, TRUE
, TRUE
, name
, status
);
725 case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT
:
726 formatOffsetISO8601Basic(offset
, FALSE
, TRUE
, TRUE
, name
, status
);
729 case UTZFMT_STYLE_ISO_BASIC_FIXED
:
730 formatOffsetISO8601Basic(offset
, TRUE
, FALSE
, TRUE
, name
, status
);
733 case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED
:
734 formatOffsetISO8601Basic(offset
, FALSE
, FALSE
, TRUE
, name
, status
);
737 case UTZFMT_STYLE_ISO_EXTENDED_FIXED
:
738 formatOffsetISO8601Extended(offset
, TRUE
, FALSE
, TRUE
, name
, status
);
741 case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED
:
742 formatOffsetISO8601Extended(offset
, FALSE
, FALSE
, TRUE
, name
, status
);
745 case UTZFMT_STYLE_ISO_BASIC_FULL
:
746 formatOffsetISO8601Basic(offset
, TRUE
, FALSE
, FALSE
, name
, status
);
749 case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
:
750 formatOffsetISO8601Basic(offset
, FALSE
, FALSE
, FALSE
, name
, status
);
753 case UTZFMT_STYLE_ISO_EXTENDED_FULL
:
754 formatOffsetISO8601Extended(offset
, TRUE
, FALSE
, FALSE
, name
, status
);
757 case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL
:
758 formatOffsetISO8601Extended(offset
, FALSE
, FALSE
, FALSE
, name
, status
);
762 // UTZFMT_STYLE_ZONE_ID, UTZFMT_STYLE_ZONE_ID_SHORT, UTZFMT_STYLE_EXEMPLAR_LOCATION
767 *timeType
= (dstOffset
!= 0) ? UTZFMT_TIME_TYPE_DAYLIGHT
: UTZFMT_TIME_TYPE_STANDARD
;
776 TimeZoneFormat::format(const Formattable
& obj
, UnicodeString
& appendTo
,
777 FieldPosition
& pos
, UErrorCode
& status
) const {
778 if (U_FAILURE(status
)) {
781 UDate date
= Calendar::getNow();
782 if (obj
.getType() == Formattable::kObject
) {
783 const UObject
* formatObj
= obj
.getObject();
784 const TimeZone
* tz
= dynamic_cast<const TimeZone
*>(formatObj
);
786 const Calendar
* cal
= dynamic_cast<const Calendar
*>(formatObj
);
788 tz
= &cal
->getTimeZone();
789 date
= cal
->getTime(status
);
793 int32_t rawOffset
, dstOffset
;
794 tz
->getOffset(date
, FALSE
, rawOffset
, dstOffset
, status
);
795 UChar buf
[ZONE_NAME_U16_MAX
];
796 UnicodeString
result(buf
, 0, UPRV_LENGTHOF(buf
));
797 formatOffsetLocalizedGMT(rawOffset
+ dstOffset
, result
, status
);
798 if (U_SUCCESS(status
)) {
799 appendTo
.append(result
);
800 if (pos
.getField() == UDAT_TIMEZONE_FIELD
) {
801 pos
.setBeginIndex(0);
802 pos
.setEndIndex(result
.length());
811 TimeZoneFormat::parse(UTimeZoneFormatStyle style
, const UnicodeString
& text
, ParsePosition
& pos
,
812 UTimeZoneFormatTimeType
* timeType
/*= NULL*/) const {
813 return parse(style
, text
, pos
, getDefaultParseOptions(), timeType
);
817 TimeZoneFormat::parse(UTimeZoneFormatStyle style
, const UnicodeString
& text
, ParsePosition
& pos
,
818 int32_t parseOptions
, UTimeZoneFormatTimeType
* timeType
/* = NULL */) const {
820 *timeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
823 int32_t startIdx
= pos
.getIndex();
824 int32_t maxPos
= text
.length();
827 // Styles using localized GMT format as fallback
828 UBool fallbackLocalizedGMT
=
829 (style
== UTZFMT_STYLE_SPECIFIC_LONG
|| style
== UTZFMT_STYLE_GENERIC_LONG
|| style
== UTZFMT_STYLE_GENERIC_LOCATION
);
830 UBool fallbackShortLocalizedGMT
=
831 (style
== UTZFMT_STYLE_SPECIFIC_SHORT
|| style
== UTZFMT_STYLE_GENERIC_SHORT
);
833 int32_t evaluated
= 0; // bit flags representing already evaluated styles
834 ParsePosition
tmpPos(startIdx
);
836 int32_t parsedOffset
= UNKNOWN_OFFSET
; // stores successfully parsed offset for later use
837 int32_t parsedPos
= -1; // stores successfully parsed offset position for later use
839 // Try localized GMT format first if necessary
840 if (fallbackLocalizedGMT
|| fallbackShortLocalizedGMT
) {
841 UBool hasDigitOffset
= FALSE
;
842 offset
= parseOffsetLocalizedGMT(text
, tmpPos
, fallbackShortLocalizedGMT
, &hasDigitOffset
);
843 if (tmpPos
.getErrorIndex() == -1) {
844 // Even when the input text was successfully parsed as a localized GMT format text,
845 // we may still need to evaluate the specified style if -
846 // 1) GMT zero format was used, and
847 // 2) The input text was not completely processed
848 if (tmpPos
.getIndex() == maxPos
|| hasDigitOffset
) {
849 pos
.setIndex(tmpPos
.getIndex());
850 return createTimeZoneForOffset(offset
);
852 parsedOffset
= offset
;
853 parsedPos
= tmpPos
.getIndex();
855 // Note: For now, no distinction between long/short localized GMT format in the parser.
856 // This might be changed in future.
857 // evaluated |= (fallbackLocalizedGMT ? STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT] : STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT]);
858 evaluated
|= STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT
] | STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT_SHORT
];
861 UErrorCode status
= U_ZERO_ERROR
;
863 UnicodeString
tzID(tzIDBuf
, 0, UPRV_LENGTHOF(tzIDBuf
));
865 UBool parseTZDBAbbrev
= ((parseOptions
& UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
) != 0);
867 // Try the specified style
869 case UTZFMT_STYLE_LOCALIZED_GMT
:
871 tmpPos
.setIndex(startIdx
);
872 tmpPos
.setErrorIndex(-1);
874 offset
= parseOffsetLocalizedGMT(text
, tmpPos
);
875 if (tmpPos
.getErrorIndex() == -1) {
876 pos
.setIndex(tmpPos
.getIndex());
877 return createTimeZoneForOffset(offset
);
880 // Note: For now, no distinction between long/short localized GMT format in the parser.
881 // This might be changed in future.
882 evaluated
|= STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT_SHORT
];
886 case UTZFMT_STYLE_LOCALIZED_GMT_SHORT
:
888 tmpPos
.setIndex(startIdx
);
889 tmpPos
.setErrorIndex(-1);
891 offset
= parseOffsetShortLocalizedGMT(text
, tmpPos
);
892 if (tmpPos
.getErrorIndex() == -1) {
893 pos
.setIndex(tmpPos
.getIndex());
894 return createTimeZoneForOffset(offset
);
897 // Note: For now, no distinction between long/short localized GMT format in the parser.
898 // This might be changed in future.
899 evaluated
|= STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT
];
903 case UTZFMT_STYLE_ISO_BASIC_SHORT
:
904 case UTZFMT_STYLE_ISO_BASIC_FIXED
:
905 case UTZFMT_STYLE_ISO_BASIC_FULL
:
906 case UTZFMT_STYLE_ISO_EXTENDED_FIXED
:
907 case UTZFMT_STYLE_ISO_EXTENDED_FULL
:
909 tmpPos
.setIndex(startIdx
);
910 tmpPos
.setErrorIndex(-1);
912 offset
= parseOffsetISO8601(text
, tmpPos
);
913 if (tmpPos
.getErrorIndex() == -1) {
914 pos
.setIndex(tmpPos
.getIndex());
915 return createTimeZoneForOffset(offset
);
921 case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT
:
922 case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED
:
923 case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
:
924 case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED
:
925 case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL
:
927 tmpPos
.setIndex(startIdx
);
928 tmpPos
.setErrorIndex(-1);
930 // Exclude the case of UTC Indicator "Z" here
931 UBool hasDigitOffset
= FALSE
;
932 offset
= parseOffsetISO8601(text
, tmpPos
, FALSE
, &hasDigitOffset
);
933 if (tmpPos
.getErrorIndex() == -1 && hasDigitOffset
) {
934 pos
.setIndex(tmpPos
.getIndex());
935 return createTimeZoneForOffset(offset
);
941 case UTZFMT_STYLE_SPECIFIC_LONG
:
942 case UTZFMT_STYLE_SPECIFIC_SHORT
:
945 int32_t nameTypes
= 0;
946 if (style
== UTZFMT_STYLE_SPECIFIC_LONG
) {
947 nameTypes
= (UTZNM_LONG_STANDARD
| UTZNM_LONG_DAYLIGHT
);
949 U_ASSERT(style
== UTZFMT_STYLE_SPECIFIC_SHORT
);
950 nameTypes
= (UTZNM_SHORT_STANDARD
| UTZNM_SHORT_DAYLIGHT
);
952 LocalPointer
<TimeZoneNames::MatchInfoCollection
> specificMatches(fTimeZoneNames
->find(text
, startIdx
, nameTypes
, status
));
953 if (U_FAILURE(status
)) {
954 pos
.setErrorIndex(startIdx
);
957 if (!specificMatches
.isNull()) {
958 int32_t matchIdx
= -1;
959 int32_t matchPos
= -1;
960 for (int32_t i
= 0; i
< specificMatches
->size(); i
++) {
961 matchPos
= startIdx
+ specificMatches
->getMatchLengthAt(i
);
962 if (matchPos
> parsedPos
) {
964 parsedPos
= matchPos
;
969 *timeType
= getTimeType(specificMatches
->getNameTypeAt(matchIdx
));
971 pos
.setIndex(matchPos
);
972 getTimeZoneID(specificMatches
.getAlias(), matchIdx
, tzID
);
973 U_ASSERT(!tzID
.isEmpty());
974 return TimeZone::createTimeZone(tzID
);
978 if (parseTZDBAbbrev
&& style
== UTZFMT_STYLE_SPECIFIC_SHORT
) {
979 U_ASSERT((nameTypes
& UTZNM_SHORT_STANDARD
) != 0);
980 U_ASSERT((nameTypes
& UTZNM_SHORT_DAYLIGHT
) != 0);
982 const TZDBTimeZoneNames
*tzdbTimeZoneNames
= getTZDBTimeZoneNames(status
);
983 if (U_SUCCESS(status
)) {
984 LocalPointer
<TimeZoneNames::MatchInfoCollection
> tzdbNameMatches(
985 tzdbTimeZoneNames
->find(text
, startIdx
, nameTypes
, status
));
986 if (U_FAILURE(status
)) {
987 pos
.setErrorIndex(startIdx
);
990 if (!tzdbNameMatches
.isNull()) {
991 int32_t matchIdx
= -1;
992 int32_t matchPos
= -1;
993 for (int32_t i
= 0; i
< tzdbNameMatches
->size(); i
++) {
994 matchPos
= startIdx
+ tzdbNameMatches
->getMatchLengthAt(i
);
995 if (matchPos
> parsedPos
) {
997 parsedPos
= matchPos
;
1000 if (matchIdx
>= 0) {
1002 *timeType
= getTimeType(tzdbNameMatches
->getNameTypeAt(matchIdx
));
1004 pos
.setIndex(matchPos
);
1005 getTimeZoneID(tzdbNameMatches
.getAlias(), matchIdx
, tzID
);
1006 U_ASSERT(!tzID
.isEmpty());
1007 return TimeZone::createTimeZone(tzID
);
1014 case UTZFMT_STYLE_GENERIC_LONG
:
1015 case UTZFMT_STYLE_GENERIC_SHORT
:
1016 case UTZFMT_STYLE_GENERIC_LOCATION
:
1018 int32_t genericNameTypes
= 0;
1020 case UTZFMT_STYLE_GENERIC_LOCATION
:
1021 genericNameTypes
= UTZGNM_LOCATION
;
1024 case UTZFMT_STYLE_GENERIC_LONG
:
1025 genericNameTypes
= UTZGNM_LONG
| UTZGNM_LOCATION
;
1028 case UTZFMT_STYLE_GENERIC_SHORT
:
1029 genericNameTypes
= UTZGNM_SHORT
| UTZGNM_LOCATION
;
1037 UTimeZoneFormatTimeType tt
= UTZFMT_TIME_TYPE_UNKNOWN
;
1038 const TimeZoneGenericNames
*gnames
= getTimeZoneGenericNames(status
);
1039 if (U_SUCCESS(status
)) {
1040 len
= gnames
->findBestMatch(text
, startIdx
, genericNameTypes
, tzID
, tt
, status
);
1042 if (U_FAILURE(status
)) {
1043 pos
.setErrorIndex(startIdx
);
1051 pos
.setIndex(startIdx
+ len
);
1052 U_ASSERT(!tzID
.isEmpty());
1053 return TimeZone::createTimeZone(tzID
);
1058 case UTZFMT_STYLE_ZONE_ID
:
1060 tmpPos
.setIndex(startIdx
);
1061 tmpPos
.setErrorIndex(-1);
1063 parseZoneID(text
, tmpPos
, tzID
);
1064 if (tmpPos
.getErrorIndex() == -1) {
1065 pos
.setIndex(tmpPos
.getIndex());
1066 return TimeZone::createTimeZone(tzID
);
1070 case UTZFMT_STYLE_ZONE_ID_SHORT
:
1072 tmpPos
.setIndex(startIdx
);
1073 tmpPos
.setErrorIndex(-1);
1075 parseShortZoneID(text
, tmpPos
, tzID
);
1076 if (tmpPos
.getErrorIndex() == -1) {
1077 pos
.setIndex(tmpPos
.getIndex());
1078 return TimeZone::createTimeZone(tzID
);
1082 case UTZFMT_STYLE_EXEMPLAR_LOCATION
:
1084 tmpPos
.setIndex(startIdx
);
1085 tmpPos
.setErrorIndex(-1);
1087 parseExemplarLocation(text
, tmpPos
, tzID
);
1088 if (tmpPos
.getErrorIndex() == -1) {
1089 pos
.setIndex(tmpPos
.getIndex());
1090 return TimeZone::createTimeZone(tzID
);
1095 evaluated
|= STYLE_PARSE_FLAGS
[style
];
1098 if (parsedPos
> startIdx
) {
1099 // When the specified style is one of SPECIFIC_XXX or GENERIC_XXX, we tried to parse the input
1100 // as localized GMT format earlier. If parsedOffset is positive, it means it was successfully
1101 // parsed as localized GMT format, but offset digits were not detected (more specifically, GMT
1102 // zero format). Then, it tried to find a match within the set of display names, but could not
1103 // find a match. At this point, we can safely assume the input text contains the localized
1105 U_ASSERT(parsedOffset
!= UNKNOWN_OFFSET
);
1106 pos
.setIndex(parsedPos
);
1107 return createTimeZoneForOffset(parsedOffset
);
1110 // Failed to parse the input text as the time zone format in the specified style.
1111 // Check the longest match among other styles below.
1112 UChar parsedIDBuf
[32];
1113 UnicodeString
parsedID(parsedIDBuf
, 0, UPRV_LENGTHOF(parsedIDBuf
));
1114 UTimeZoneFormatTimeType parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1116 U_ASSERT(parsedPos
< 0);
1117 U_ASSERT(parsedOffset
== UNKNOWN_OFFSET
);
1120 if (parsedPos
< maxPos
&&
1121 ((evaluated
& ISO_Z_STYLE_FLAG
) == 0 || (evaluated
& ISO_LOCAL_STYLE_FLAG
) == 0)) {
1122 tmpPos
.setIndex(startIdx
);
1123 tmpPos
.setErrorIndex(-1);
1125 UBool hasDigitOffset
= FALSE
;
1126 offset
= parseOffsetISO8601(text
, tmpPos
, FALSE
, &hasDigitOffset
);
1127 if (tmpPos
.getErrorIndex() == -1) {
1128 if (tmpPos
.getIndex() == maxPos
|| hasDigitOffset
) {
1129 pos
.setIndex(tmpPos
.getIndex());
1130 return createTimeZoneForOffset(offset
);
1132 // Note: When ISO 8601 format contains offset digits, it should not
1133 // collide with other formats. However, ISO 8601 UTC format "Z" (single letter)
1134 // may collide with other names. In this case, we need to evaluate other names.
1135 if (parsedPos
< tmpPos
.getIndex()) {
1136 parsedOffset
= offset
;
1137 parsedID
.setToBogus();
1138 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1139 parsedPos
= tmpPos
.getIndex();
1140 U_ASSERT(parsedPos
== startIdx
+ 1); // only when "Z" is used
1145 // Localized GMT format
1146 if (parsedPos
< maxPos
&&
1147 (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT
]) == 0) {
1148 tmpPos
.setIndex(startIdx
);
1149 tmpPos
.setErrorIndex(-1);
1151 UBool hasDigitOffset
= FALSE
;
1152 offset
= parseOffsetLocalizedGMT(text
, tmpPos
, FALSE
, &hasDigitOffset
);
1153 if (tmpPos
.getErrorIndex() == -1) {
1154 if (tmpPos
.getIndex() == maxPos
|| hasDigitOffset
) {
1155 pos
.setIndex(tmpPos
.getIndex());
1156 return createTimeZoneForOffset(offset
);
1158 // Evaluate other names - see the comment earlier in this method.
1159 if (parsedPos
< tmpPos
.getIndex()) {
1160 parsedOffset
= offset
;
1161 parsedID
.setToBogus();
1162 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1163 parsedPos
= tmpPos
.getIndex();
1168 if (parsedPos
< maxPos
&&
1169 (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT_SHORT
]) == 0) {
1170 tmpPos
.setIndex(startIdx
);
1171 tmpPos
.setErrorIndex(-1);
1173 UBool hasDigitOffset
= FALSE
;
1174 offset
= parseOffsetLocalizedGMT(text
, tmpPos
, TRUE
, &hasDigitOffset
);
1175 if (tmpPos
.getErrorIndex() == -1) {
1176 if (tmpPos
.getIndex() == maxPos
|| hasDigitOffset
) {
1177 pos
.setIndex(tmpPos
.getIndex());
1178 return createTimeZoneForOffset(offset
);
1180 // Evaluate other names - see the comment earlier in this method.
1181 if (parsedPos
< tmpPos
.getIndex()) {
1182 parsedOffset
= offset
;
1183 parsedID
.setToBogus();
1184 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1185 parsedPos
= tmpPos
.getIndex();
1190 // When ParseOption.ALL_STYLES is available, we also try to look all possible display names and IDs.
1191 // For example, when style is GENERIC_LONG, "EST" (SPECIFIC_SHORT) is never
1192 // used for America/New_York. With parseAllStyles true, this code parses "EST"
1193 // as America/New_York.
1195 // Note: Adding all possible names into the trie used by the implementation is quite heavy operation,
1196 // which we want to avoid normally (note that we cache the trie, so this is applicable to the
1197 // first time only as long as the cache does not expire).
1199 if (parseOptions
& UTZFMT_PARSE_OPTION_ALL_STYLES
) {
1200 // Try all specific names and exemplar location names
1201 if (parsedPos
< maxPos
) {
1202 LocalPointer
<TimeZoneNames::MatchInfoCollection
> specificMatches(fTimeZoneNames
->find(text
, startIdx
, ALL_SIMPLE_NAME_TYPES
, status
));
1203 if (U_FAILURE(status
)) {
1204 pos
.setErrorIndex(startIdx
);
1207 int32_t specificMatchIdx
= -1;
1208 int32_t matchPos
= -1;
1209 if (!specificMatches
.isNull()) {
1210 for (int32_t i
= 0; i
< specificMatches
->size(); i
++) {
1211 if (startIdx
+ specificMatches
->getMatchLengthAt(i
) > matchPos
) {
1212 specificMatchIdx
= i
;
1213 matchPos
= startIdx
+ specificMatches
->getMatchLengthAt(i
);
1217 if (parsedPos
< matchPos
) {
1218 U_ASSERT(specificMatchIdx
>= 0);
1219 parsedPos
= matchPos
;
1220 getTimeZoneID(specificMatches
.getAlias(), specificMatchIdx
, parsedID
);
1221 parsedTimeType
= getTimeType(specificMatches
->getNameTypeAt(specificMatchIdx
));
1222 parsedOffset
= UNKNOWN_OFFSET
;
1225 if (parseTZDBAbbrev
&& parsedPos
< maxPos
&& (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_SPECIFIC_SHORT
]) == 0) {
1226 const TZDBTimeZoneNames
*tzdbTimeZoneNames
= getTZDBTimeZoneNames(status
);
1227 if (U_SUCCESS(status
)) {
1228 LocalPointer
<TimeZoneNames::MatchInfoCollection
> tzdbNameMatches(
1229 tzdbTimeZoneNames
->find(text
, startIdx
, ALL_SIMPLE_NAME_TYPES
, status
));
1230 if (U_FAILURE(status
)) {
1231 pos
.setErrorIndex(startIdx
);
1234 int32_t tzdbNameMatchIdx
= -1;
1235 int32_t matchPos
= -1;
1236 if (!tzdbNameMatches
.isNull()) {
1237 for (int32_t i
= 0; i
< tzdbNameMatches
->size(); i
++) {
1238 if (startIdx
+ tzdbNameMatches
->getMatchLengthAt(i
) > matchPos
) {
1239 tzdbNameMatchIdx
= i
;
1240 matchPos
= startIdx
+ tzdbNameMatches
->getMatchLengthAt(i
);
1244 if (parsedPos
< matchPos
) {
1245 U_ASSERT(tzdbNameMatchIdx
>= 0);
1246 parsedPos
= matchPos
;
1247 getTimeZoneID(tzdbNameMatches
.getAlias(), tzdbNameMatchIdx
, parsedID
);
1248 parsedTimeType
= getTimeType(tzdbNameMatches
->getNameTypeAt(tzdbNameMatchIdx
));
1249 parsedOffset
= UNKNOWN_OFFSET
;
1253 // Try generic names
1254 if (parsedPos
< maxPos
) {
1255 int32_t genMatchLen
= -1;
1256 UTimeZoneFormatTimeType tt
= UTZFMT_TIME_TYPE_UNKNOWN
;
1258 const TimeZoneGenericNames
*gnames
= getTimeZoneGenericNames(status
);
1259 if (U_SUCCESS(status
)) {
1260 genMatchLen
= gnames
->findBestMatch(text
, startIdx
, ALL_GENERIC_NAME_TYPES
, tzID
, tt
, status
);
1262 if (U_FAILURE(status
)) {
1263 pos
.setErrorIndex(startIdx
);
1267 if (genMatchLen
> 0 && parsedPos
< startIdx
+ genMatchLen
) {
1268 parsedPos
= startIdx
+ genMatchLen
;
1269 parsedID
.setTo(tzID
);
1270 parsedTimeType
= tt
;
1271 parsedOffset
= UNKNOWN_OFFSET
;
1276 if (parsedPos
< maxPos
&& (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_ZONE_ID
]) == 0) {
1277 tmpPos
.setIndex(startIdx
);
1278 tmpPos
.setErrorIndex(-1);
1280 parseZoneID(text
, tmpPos
, tzID
);
1281 if (tmpPos
.getErrorIndex() == -1 && parsedPos
< tmpPos
.getIndex()) {
1282 parsedPos
= tmpPos
.getIndex();
1283 parsedID
.setTo(tzID
);
1284 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1285 parsedOffset
= UNKNOWN_OFFSET
;
1288 // Try short time zone ID
1289 if (parsedPos
< maxPos
&& (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_ZONE_ID
]) == 0) {
1290 tmpPos
.setIndex(startIdx
);
1291 tmpPos
.setErrorIndex(-1);
1293 parseShortZoneID(text
, tmpPos
, tzID
);
1294 if (tmpPos
.getErrorIndex() == -1 && parsedPos
< tmpPos
.getIndex()) {
1295 parsedPos
= tmpPos
.getIndex();
1296 parsedID
.setTo(tzID
);
1297 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1298 parsedOffset
= UNKNOWN_OFFSET
;
1303 if (parsedPos
> startIdx
) {
1304 // Parsed successfully
1306 if (parsedID
.length() > 0) {
1307 parsedTZ
= TimeZone::createTimeZone(parsedID
);
1309 U_ASSERT(parsedOffset
!= UNKNOWN_OFFSET
);
1310 parsedTZ
= createTimeZoneForOffset(parsedOffset
);
1313 *timeType
= parsedTimeType
;
1315 pos
.setIndex(parsedPos
);
1319 pos
.setErrorIndex(startIdx
);
1324 TimeZoneFormat::parseObject(const UnicodeString
& source
, Formattable
& result
,
1325 ParsePosition
& parse_pos
) const {
1326 result
.adoptObject(parse(UTZFMT_STYLE_GENERIC_LOCATION
, source
, parse_pos
, UTZFMT_PARSE_OPTION_ALL_STYLES
));
1330 // ------------------------------------------------------------------
1331 // Private zone name format/parse implementation
1334 TimeZoneFormat::formatGeneric(const TimeZone
& tz
, int32_t genType
, UDate date
, UnicodeString
& name
) const {
1335 UErrorCode status
= U_ZERO_ERROR
;
1336 const TimeZoneGenericNames
* gnames
= getTimeZoneGenericNames(status
);
1337 if (U_FAILURE(status
)) {
1342 if (genType
== UTZGNM_LOCATION
) {
1343 const UChar
* canonicalID
= ZoneMeta::getCanonicalCLDRID(tz
);
1344 if (canonicalID
== NULL
) {
1348 return gnames
->getGenericLocationName(UnicodeString(TRUE
, canonicalID
, -1), name
);
1350 return gnames
->getDisplayName(tz
, (UTimeZoneGenericNameType
)genType
, date
, name
);
1354 TimeZoneFormat::formatSpecific(const TimeZone
& tz
, UTimeZoneNameType stdType
, UTimeZoneNameType dstType
,
1355 UDate date
, UnicodeString
& name
, UTimeZoneFormatTimeType
*timeType
) const {
1356 if (fTimeZoneNames
== NULL
) {
1361 UErrorCode status
= U_ZERO_ERROR
;
1362 UBool isDaylight
= tz
.inDaylightTime(date
, status
);
1363 const UChar
* canonicalID
= ZoneMeta::getCanonicalCLDRID(tz
);
1365 if (U_FAILURE(status
) || canonicalID
== NULL
) {
1371 fTimeZoneNames
->getDisplayName(UnicodeString(TRUE
, canonicalID
, -1), dstType
, date
, name
);
1373 fTimeZoneNames
->getDisplayName(UnicodeString(TRUE
, canonicalID
, -1), stdType
, date
, name
);
1376 if (timeType
&& !name
.isEmpty()) {
1377 *timeType
= isDaylight
? UTZFMT_TIME_TYPE_DAYLIGHT
: UTZFMT_TIME_TYPE_STANDARD
;
1382 const TimeZoneGenericNames
*
1383 TimeZoneFormat::getTimeZoneGenericNames(UErrorCode
& status
) const {
1384 if (U_FAILURE(status
)) {
1389 if (fTimeZoneGenericNames
== NULL
) {
1390 TimeZoneFormat
*nonConstThis
= const_cast<TimeZoneFormat
*>(this);
1391 nonConstThis
->fTimeZoneGenericNames
= TimeZoneGenericNames::createInstance(fLocale
, status
);
1393 umtx_unlock(gLock());
1395 return fTimeZoneGenericNames
;
1398 const TZDBTimeZoneNames
*
1399 TimeZoneFormat::getTZDBTimeZoneNames(UErrorCode
& status
) const {
1400 if (U_FAILURE(status
)) {
1405 if (fTZDBTimeZoneNames
== NULL
) {
1406 TZDBTimeZoneNames
*tzdbNames
= new TZDBTimeZoneNames(fLocale
);
1407 if (tzdbNames
== NULL
) {
1408 status
= U_MEMORY_ALLOCATION_ERROR
;
1410 TimeZoneFormat
*nonConstThis
= const_cast<TimeZoneFormat
*>(this);
1411 nonConstThis
->fTZDBTimeZoneNames
= tzdbNames
;
1414 umtx_unlock(gLock());
1416 return fTZDBTimeZoneNames
;
1420 TimeZoneFormat::formatExemplarLocation(const TimeZone
& tz
, UnicodeString
& name
) const {
1421 UChar locationBuf
[ZONE_NAME_U16_MAX
];
1422 UnicodeString
location(locationBuf
, 0, UPRV_LENGTHOF(locationBuf
));
1423 const UChar
* canonicalID
= ZoneMeta::getCanonicalCLDRID(tz
);
1426 fTimeZoneNames
->getExemplarLocationName(UnicodeString(TRUE
, canonicalID
, -1), location
);
1428 if (location
.length() > 0) {
1429 name
.setTo(location
);
1431 // Use "unknown" location
1432 fTimeZoneNames
->getExemplarLocationName(UnicodeString(TRUE
, UNKNOWN_ZONE_ID
, -1), location
);
1433 if (location
.length() > 0) {
1434 name
.setTo(location
);
1437 name
.setTo(UNKNOWN_LOCATION
, -1);
1444 // ------------------------------------------------------------------
1445 // Zone offset format and parse
1448 TimeZoneFormat::formatOffsetISO8601Basic(int32_t offset
, UBool useUtcIndicator
, UBool isShort
, UBool ignoreSeconds
,
1449 UnicodeString
& result
, UErrorCode
& status
) const {
1450 return formatOffsetISO8601(offset
, TRUE
, useUtcIndicator
, isShort
, ignoreSeconds
, result
, status
);
1454 TimeZoneFormat::formatOffsetISO8601Extended(int32_t offset
, UBool useUtcIndicator
, UBool isShort
, UBool ignoreSeconds
,
1455 UnicodeString
& result
, UErrorCode
& status
) const {
1456 return formatOffsetISO8601(offset
, FALSE
, useUtcIndicator
, isShort
, ignoreSeconds
, result
, status
);
1460 TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset
, UnicodeString
& result
, UErrorCode
& status
) const {
1461 return formatOffsetLocalizedGMT(offset
, FALSE
, result
, status
);
1465 TimeZoneFormat::formatOffsetShortLocalizedGMT(int32_t offset
, UnicodeString
& result
, UErrorCode
& status
) const {
1466 return formatOffsetLocalizedGMT(offset
, TRUE
, result
, status
);
1470 TimeZoneFormat::parseOffsetISO8601(const UnicodeString
& text
, ParsePosition
& pos
) const {
1471 return parseOffsetISO8601(text
, pos
, FALSE
);
1475 TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString
& text
, ParsePosition
& pos
) const {
1476 return parseOffsetLocalizedGMT(text
, pos
, FALSE
, NULL
);
1480 TimeZoneFormat::parseOffsetShortLocalizedGMT(const UnicodeString
& text
, ParsePosition
& pos
) const {
1481 return parseOffsetLocalizedGMT(text
, pos
, TRUE
, NULL
);
1484 // ------------------------------------------------------------------
1485 // Private zone offset format/parse implementation
1488 TimeZoneFormat::formatOffsetISO8601(int32_t offset
, UBool isBasic
, UBool useUtcIndicator
,
1489 UBool isShort
, UBool ignoreSeconds
, UnicodeString
& result
, UErrorCode
& status
) const {
1490 if (U_FAILURE(status
)) {
1491 result
.setToBogus();
1494 int32_t absOffset
= offset
< 0 ? -offset
: offset
;
1495 if (useUtcIndicator
&& (absOffset
< MILLIS_PER_SECOND
|| (ignoreSeconds
&& absOffset
< MILLIS_PER_MINUTE
))) {
1496 result
.setTo(ISO8601_UTC
);
1500 OffsetFields minFields
= isShort
? FIELDS_H
: FIELDS_HM
;
1501 OffsetFields maxFields
= ignoreSeconds
? FIELDS_HM
: FIELDS_HMS
;
1502 UChar sep
= isBasic
? 0 : ISO8601_SEP
;
1504 // Note: FIELDS_HMS as maxFields is a CLDR/ICU extension. ISO 8601 specification does
1505 // not support seconds field.
1507 if (absOffset
>= MAX_OFFSET
) {
1508 result
.setToBogus();
1509 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1514 fields
[0] = absOffset
/ MILLIS_PER_HOUR
;
1515 absOffset
= absOffset
% MILLIS_PER_HOUR
;
1516 fields
[1] = absOffset
/ MILLIS_PER_MINUTE
;
1517 absOffset
= absOffset
% MILLIS_PER_MINUTE
;
1518 fields
[2] = absOffset
/ MILLIS_PER_SECOND
;
1520 U_ASSERT(fields
[0] >= 0 && fields
[0] <= MAX_OFFSET_HOUR
);
1521 U_ASSERT(fields
[1] >= 0 && fields
[1] <= MAX_OFFSET_MINUTE
);
1522 U_ASSERT(fields
[2] >= 0 && fields
[2] <= MAX_OFFSET_SECOND
);
1524 int32_t lastIdx
= maxFields
;
1525 while (lastIdx
> minFields
) {
1526 if (fields
[lastIdx
] != 0) {
1534 // if all output fields are 0s, do not use negative sign
1535 for (int32_t idx
= 0; idx
<= lastIdx
; idx
++) {
1536 if (fields
[idx
] != 0) {
1544 for (int32_t idx
= 0; idx
<= lastIdx
; idx
++) {
1545 if (sep
&& idx
!= 0) {
1548 result
.append((UChar
)(0x0030 + fields
[idx
]/10));
1549 result
.append((UChar
)(0x0030 + fields
[idx
]%10
));
1556 TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset
, UBool isShort
, UnicodeString
& result
, UErrorCode
& status
) const {
1557 if (U_FAILURE(status
)) {
1558 result
.setToBogus();
1561 if (offset
<= -MAX_OFFSET
|| offset
>= MAX_OFFSET
) {
1562 result
.setToBogus();
1563 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1568 result
.setTo(fGMTZeroFormat
);
1572 UBool positive
= TRUE
;
1578 int32_t offsetH
= offset
/ MILLIS_PER_HOUR
;
1579 offset
= offset
% MILLIS_PER_HOUR
;
1580 int32_t offsetM
= offset
/ MILLIS_PER_MINUTE
;
1581 offset
= offset
% MILLIS_PER_MINUTE
;
1582 int32_t offsetS
= offset
/ MILLIS_PER_SECOND
;
1584 U_ASSERT(offsetH
<= MAX_OFFSET_HOUR
&& offsetM
<= MAX_OFFSET_MINUTE
&& offsetS
<= MAX_OFFSET_SECOND
);
1586 const UVector
* offsetPatternItems
= NULL
;
1589 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_POSITIVE_HMS
];
1590 } else if (offsetM
!= 0 || !isShort
) {
1591 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_POSITIVE_HM
];
1593 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_POSITIVE_H
];
1597 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_NEGATIVE_HMS
];
1598 } else if (offsetM
!= 0 || !isShort
) {
1599 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_NEGATIVE_HM
];
1601 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_NEGATIVE_H
];
1605 U_ASSERT(offsetPatternItems
!= NULL
);
1607 // Building the GMT format string
1608 result
.setTo(fGMTPatternPrefix
);
1610 for (int32_t i
= 0; i
< offsetPatternItems
->size(); i
++) {
1611 const GMTOffsetField
* item
= (GMTOffsetField
*)offsetPatternItems
->elementAt(i
);
1612 GMTOffsetField::FieldType type
= item
->getType();
1615 case GMTOffsetField::TEXT
:
1616 result
.append(item
->getPatternText(), -1);
1619 case GMTOffsetField::HOUR
:
1620 appendOffsetDigits(result
, offsetH
, (isShort
? 1 : 2));
1623 case GMTOffsetField::MINUTE
:
1624 appendOffsetDigits(result
, offsetM
, 2);
1627 case GMTOffsetField::SECOND
:
1628 appendOffsetDigits(result
, offsetS
, 2);
1633 result
.append(fGMTPatternSuffix
);
1638 TimeZoneFormat::parseOffsetISO8601(const UnicodeString
& text
, ParsePosition
& pos
, UBool extendedOnly
, UBool
* hasDigitOffset
/* = NULL */) const {
1639 if (hasDigitOffset
) {
1640 *hasDigitOffset
= FALSE
;
1642 int32_t start
= pos
.getIndex();
1643 if (start
>= text
.length()) {
1644 pos
.setErrorIndex(start
);
1648 UChar firstChar
= text
.charAt(start
);
1649 if (firstChar
== ISO8601_UTC
|| firstChar
== (UChar
)(ISO8601_UTC
+ 0x20)) {
1650 // "Z" (or "z") - indicates UTC
1651 pos
.setIndex(start
+ 1);
1656 if (firstChar
== PLUS
) {
1658 } else if (firstChar
== MINUS
) {
1661 // Not an ISO 8601 offset string
1662 pos
.setErrorIndex(start
);
1665 ParsePosition
posOffset(start
+ 1);
1666 int32_t offset
= parseAsciiOffsetFields(text
, posOffset
, ISO8601_SEP
, FIELDS_H
, FIELDS_HMS
);
1667 if (posOffset
.getErrorIndex() == -1 && !extendedOnly
&& (posOffset
.getIndex() - start
<= 3)) {
1668 // If the text is successfully parsed as extended format with the options above, it can be also parsed
1669 // as basic format. For example, "0230" can be parsed as offset 2:00 (only first digits are valid for
1670 // extended format), but it can be parsed as offset 2:30 with basic format. We use longer result.
1671 ParsePosition
posBasic(start
+ 1);
1672 int32_t tmpOffset
= parseAbuttingAsciiOffsetFields(text
, posBasic
, FIELDS_H
, FIELDS_HMS
, FALSE
);
1673 if (posBasic
.getErrorIndex() == -1 && posBasic
.getIndex() > posOffset
.getIndex()) {
1675 posOffset
.setIndex(posBasic
.getIndex());
1679 if (posOffset
.getErrorIndex() != -1) {
1680 pos
.setErrorIndex(start
);
1684 pos
.setIndex(posOffset
.getIndex());
1685 if (hasDigitOffset
) {
1686 *hasDigitOffset
= TRUE
;
1688 return sign
* offset
;
1692 TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString
& text
, ParsePosition
& pos
, UBool isShort
, UBool
* hasDigitOffset
) const {
1693 int32_t start
= pos
.getIndex();
1695 int32_t parsedLength
= 0;
1697 if (hasDigitOffset
) {
1698 *hasDigitOffset
= FALSE
;
1701 offset
= parseOffsetLocalizedGMTPattern(text
, start
, isShort
, parsedLength
);
1703 // For now, parseOffsetLocalizedGMTPattern handles both long and short
1704 // formats, no matter isShort is true or false. This might be changed in future
1705 // when strict parsing is necessary, or different set of patterns are used for
1706 // short/long formats.
1708 if (parsedLength
== 0) {
1709 offset
= parseOffsetLocalizedGMTPattern(text
, start
, !isShort
, parsedLength
);
1713 if (parsedLength
> 0) {
1714 if (hasDigitOffset
) {
1715 *hasDigitOffset
= TRUE
;
1717 pos
.setIndex(start
+ parsedLength
);
1721 // Try the default patterns
1722 offset
= parseOffsetDefaultLocalizedGMT(text
, start
, parsedLength
);
1723 if (parsedLength
> 0) {
1724 if (hasDigitOffset
) {
1725 *hasDigitOffset
= TRUE
;
1727 pos
.setIndex(start
+ parsedLength
);
1731 // Check if this is a GMT zero format
1732 if (text
.caseCompare(start
, fGMTZeroFormat
.length(), fGMTZeroFormat
, 0) == 0) {
1733 pos
.setIndex(start
+ fGMTZeroFormat
.length());
1737 // Check if this is a default GMT zero format
1738 for (int32_t i
= 0; ALT_GMT_STRINGS
[i
][0] != 0; i
++) {
1739 const UChar
* defGMTZero
= ALT_GMT_STRINGS
[i
];
1740 int32_t defGMTZeroLen
= u_strlen(defGMTZero
);
1741 if (text
.caseCompare(start
, defGMTZeroLen
, defGMTZero
, 0) == 0) {
1742 pos
.setIndex(start
+ defGMTZeroLen
);
1748 pos
.setErrorIndex(start
);
1753 TimeZoneFormat::parseOffsetLocalizedGMTPattern(const UnicodeString
& text
, int32_t start
, UBool
/*isShort*/, int32_t& parsedLen
) const {
1754 int32_t idx
= start
;
1756 UBool parsed
= FALSE
;
1760 int32_t len
= fGMTPatternPrefix
.length();
1761 if (len
> 0 && text
.caseCompare(idx
, len
, fGMTPatternPrefix
, 0) != 0) {
1762 // prefix match failed
1768 offset
= parseOffsetFields(text
, idx
, FALSE
, len
);
1770 // offset field match failed
1775 len
= fGMTPatternSuffix
.length();
1776 if (len
> 0 && text
.caseCompare(idx
, len
, fGMTPatternSuffix
, 0) != 0) {
1784 parsedLen
= parsed
? idx
- start
: 0;
1789 TimeZoneFormat::parseOffsetFields(const UnicodeString
& text
, int32_t start
, UBool
/*isShort*/, int32_t& parsedLen
) const {
1796 int32_t offsetH
, offsetM
, offsetS
;
1797 offsetH
= offsetM
= offsetS
= 0;
1799 for (int32_t patidx
= 0; PARSE_GMT_OFFSET_TYPES
[patidx
] >= 0; patidx
++) {
1800 int32_t gmtPatType
= PARSE_GMT_OFFSET_TYPES
[patidx
];
1801 UVector
* items
= fGMTOffsetPatternItems
[gmtPatType
];
1802 U_ASSERT(items
!= NULL
);
1804 outLen
= parseOffsetFieldsWithPattern(text
, start
, items
, FALSE
, offsetH
, offsetM
, offsetS
);
1806 sign
= (gmtPatType
== UTZFMT_PAT_POSITIVE_H
|| gmtPatType
== UTZFMT_PAT_POSITIVE_HM
|| gmtPatType
== UTZFMT_PAT_POSITIVE_HMS
) ?
1812 if (outLen
> 0 && fAbuttingOffsetHoursAndMinutes
) {
1813 // When hours field is sabutting minutes field,
1814 // the parse result above may not be appropriate.
1815 // For example, "01020" is parsed as 01:02: above,
1816 // but it should be parsed as 00:10:20.
1818 int32_t tmpSign
= 1;
1823 for (int32_t patidx
= 0; PARSE_GMT_OFFSET_TYPES
[patidx
] >= 0; patidx
++) {
1824 int32_t gmtPatType
= PARSE_GMT_OFFSET_TYPES
[patidx
];
1825 UVector
* items
= fGMTOffsetPatternItems
[gmtPatType
];
1826 U_ASSERT(items
!= NULL
);
1828 // forcing parse to use single hour digit
1829 tmpLen
= parseOffsetFieldsWithPattern(text
, start
, items
, TRUE
, tmpH
, tmpM
, tmpS
);
1831 tmpSign
= (gmtPatType
== UTZFMT_PAT_POSITIVE_H
|| gmtPatType
== UTZFMT_PAT_POSITIVE_HM
|| gmtPatType
== UTZFMT_PAT_POSITIVE_HMS
) ?
1836 if (tmpLen
> outLen
) {
1837 // Better parse result with single hour digit
1847 offset
= ((((offsetH
* 60) + offsetM
) * 60) + offsetS
) * 1000 * sign
;
1855 TimeZoneFormat::parseOffsetFieldsWithPattern(const UnicodeString
& text
, int32_t start
,
1856 UVector
* patternItems
, UBool forceSingleHourDigit
, int32_t& hour
, int32_t& min
, int32_t& sec
) const {
1857 UBool failed
= FALSE
;
1858 int32_t offsetH
, offsetM
, offsetS
;
1859 offsetH
= offsetM
= offsetS
= 0;
1860 int32_t idx
= start
;
1862 for (int32_t i
= 0; i
< patternItems
->size(); i
++) {
1864 const GMTOffsetField
* field
= (const GMTOffsetField
*)patternItems
->elementAt(i
);
1865 GMTOffsetField::FieldType fieldType
= field
->getType();
1866 if (fieldType
== GMTOffsetField::TEXT
) {
1867 const UChar
* patStr
= field
->getPatternText();
1868 len
= u_strlen(patStr
);
1870 // When TimeZoneFormat parse() is called from SimpleDateFormat,
1871 // leading space characters might be truncated. If the first pattern text
1872 // starts with such character (e.g. Bidi control), then we need to
1873 // skip the leading space charcters.
1874 if (idx
< text
.length() && !PatternProps::isWhiteSpace(text
.char32At(idx
))) {
1878 U16_GET(patStr
, 0, 0, len
, ch
)
1879 if (PatternProps::isWhiteSpace(ch
)) {
1880 chLen
= U16_LENGTH(ch
);
1890 if (text
.caseCompare(idx
, len
, patStr
, 0) != 0) {
1896 if (fieldType
== GMTOffsetField::HOUR
) {
1897 uint8_t maxDigits
= forceSingleHourDigit
? 1 : 2;
1898 offsetH
= parseOffsetFieldWithLocalizedDigits(text
, idx
, 1, maxDigits
, 0, MAX_OFFSET_HOUR
, len
);
1899 } else if (fieldType
== GMTOffsetField::MINUTE
) {
1900 offsetM
= parseOffsetFieldWithLocalizedDigits(text
, idx
, 2, 2, 0, MAX_OFFSET_MINUTE
, len
);
1901 } else if (fieldType
== GMTOffsetField::SECOND
) {
1902 offsetS
= parseOffsetFieldWithLocalizedDigits(text
, idx
, 2, 2, 0, MAX_OFFSET_SECOND
, len
);
1914 hour
= min
= sec
= 0;
1926 TimeZoneFormat::parseAbuttingOffsetFields(const UnicodeString
& text
, int32_t start
, int32_t& parsedLen
) const {
1927 int32_t digits
[MAX_OFFSET_DIGITS
];
1928 int32_t parsed
[MAX_OFFSET_DIGITS
]; // accumulative offsets
1930 // Parse digits into int[]
1931 int32_t idx
= start
;
1933 int32_t numDigits
= 0;
1934 for (int32_t i
= 0; i
< MAX_OFFSET_DIGITS
; i
++) {
1935 digits
[i
] = parseSingleLocalizedDigit(text
, idx
, len
);
1936 if (digits
[i
] < 0) {
1940 parsed
[i
] = idx
- start
;
1944 if (numDigits
== 0) {
1950 while (numDigits
> 0) {
1955 U_ASSERT(numDigits
> 0 && numDigits
<= MAX_OFFSET_DIGITS
);
1956 switch (numDigits
) {
1961 hour
= digits
[0] * 10 + digits
[1];
1965 min
= digits
[1] * 10 + digits
[2];
1968 hour
= digits
[0] * 10 + digits
[1];
1969 min
= digits
[2] * 10 + digits
[3];
1973 min
= digits
[1] * 10 + digits
[2];
1974 sec
= digits
[3] * 10 + digits
[4];
1977 hour
= digits
[0] * 10 + digits
[1];
1978 min
= digits
[2] * 10 + digits
[3];
1979 sec
= digits
[4] * 10 + digits
[5];
1982 if (hour
<= MAX_OFFSET_HOUR
&& min
<= MAX_OFFSET_MINUTE
&& sec
<= MAX_OFFSET_SECOND
) {
1983 // found a valid combination
1984 offset
= hour
* MILLIS_PER_HOUR
+ min
* MILLIS_PER_MINUTE
+ sec
* MILLIS_PER_SECOND
;
1985 parsedLen
= parsed
[numDigits
- 1];
1994 TimeZoneFormat::parseOffsetDefaultLocalizedGMT(const UnicodeString
& text
, int start
, int32_t& parsedLen
) const {
1995 int32_t idx
= start
;
2000 // check global default GMT alternatives
2003 for (int32_t i
= 0; ALT_GMT_STRINGS
[i
][0] != 0; i
++) {
2004 const UChar
* gmt
= ALT_GMT_STRINGS
[i
];
2005 int32_t len
= u_strlen(gmt
);
2006 if (text
.caseCompare(start
, len
, gmt
, 0) == 0) {
2016 // offset needs a sign char and a digit at minimum
2017 if (idx
+ 1 >= text
.length()) {
2023 UChar c
= text
.charAt(idx
);
2026 } else if (c
== MINUS
) {
2034 // try the default pattern with the separator first
2035 int32_t lenWithSep
= 0;
2036 int32_t offsetWithSep
= parseDefaultOffsetFields(text
, idx
, DEFAULT_GMT_OFFSET_SEP
, lenWithSep
);
2037 if (lenWithSep
== text
.length() - idx
) {
2039 offset
= offsetWithSep
* sign
;
2042 // try abutting field pattern
2043 int32_t lenAbut
= 0;
2044 int32_t offsetAbut
= parseAbuttingOffsetFields(text
, idx
, lenAbut
);
2046 if (lenWithSep
> lenAbut
) {
2047 offset
= offsetWithSep
* sign
;
2050 offset
= offsetAbut
* sign
;
2054 parsed
= idx
- start
;
2062 TimeZoneFormat::parseDefaultOffsetFields(const UnicodeString
& text
, int32_t start
, UChar separator
, int32_t& parsedLen
) const {
2063 int32_t max
= text
.length();
2064 int32_t idx
= start
;
2066 int32_t hour
= 0, min
= 0, sec
= 0;
2071 hour
= parseOffsetFieldWithLocalizedDigits(text
, idx
, 1, 2, 0, MAX_OFFSET_HOUR
, len
);
2077 if (idx
+ 1 < max
&& text
.charAt(idx
) == separator
) {
2078 min
= parseOffsetFieldWithLocalizedDigits(text
, idx
+ 1, 2, 2, 0, MAX_OFFSET_MINUTE
, len
);
2084 if (idx
+ 1 < max
&& text
.charAt(idx
) == separator
) {
2085 sec
= parseOffsetFieldWithLocalizedDigits(text
, idx
+ 1, 2, 2, 0, MAX_OFFSET_SECOND
, len
);
2098 parsedLen
= idx
- start
;
2099 return hour
* MILLIS_PER_HOUR
+ min
* MILLIS_PER_MINUTE
+ sec
* MILLIS_PER_SECOND
;
2103 TimeZoneFormat::parseOffsetFieldWithLocalizedDigits(const UnicodeString
& text
, int32_t start
, uint8_t minDigits
, uint8_t maxDigits
, uint16_t minVal
, uint16_t maxVal
, int32_t& parsedLen
) const {
2107 int32_t numDigits
= 0;
2108 int32_t idx
= start
;
2109 int32_t digitLen
= 0;
2111 while (idx
< text
.length() && numDigits
< maxDigits
) {
2112 int32_t digit
= parseSingleLocalizedDigit(text
, idx
, digitLen
);
2116 int32_t tmpVal
= decVal
* 10 + digit
;
2117 if (tmpVal
> maxVal
) {
2125 // Note: maxVal is checked in the while loop
2126 if (numDigits
< minDigits
|| decVal
< minVal
) {
2130 parsedLen
= idx
- start
;
2137 TimeZoneFormat::parseSingleLocalizedDigit(const UnicodeString
& text
, int32_t start
, int32_t& len
) const {
2140 if (start
< text
.length()) {
2141 UChar32 cp
= text
.char32At(start
);
2143 // First, try digits configured for this instance
2144 for (int32_t i
= 0; i
< 10; i
++) {
2145 if (cp
== fGMTOffsetDigits
[i
]) {
2150 // If failed, check if this is a Unicode digit
2152 int32_t tmp
= u_charDigitValue(cp
);
2153 digit
= (tmp
>= 0 && tmp
<= 9) ? tmp
: -1;
2157 int32_t next
= text
.moveIndex32(start
, 1);
2165 TimeZoneFormat::formatOffsetWithAsciiDigits(int32_t offset
, UChar sep
, OffsetFields minFields
, OffsetFields maxFields
, UnicodeString
& result
) {
2166 U_ASSERT(maxFields
>= minFields
);
2167 U_ASSERT(offset
> -MAX_OFFSET
&& offset
< MAX_OFFSET
);
2177 fields
[0] = offset
/ MILLIS_PER_HOUR
;
2178 offset
= offset
% MILLIS_PER_HOUR
;
2179 fields
[1] = offset
/ MILLIS_PER_MINUTE
;
2180 offset
= offset
% MILLIS_PER_MINUTE
;
2181 fields
[2] = offset
/ MILLIS_PER_SECOND
;
2183 U_ASSERT(fields
[0] >= 0 && fields
[0] <= MAX_OFFSET_HOUR
);
2184 U_ASSERT(fields
[1] >= 0 && fields
[1] <= MAX_OFFSET_MINUTE
);
2185 U_ASSERT(fields
[2] >= 0 && fields
[2] <= MAX_OFFSET_SECOND
);
2187 int32_t lastIdx
= maxFields
;
2188 while (lastIdx
> minFields
) {
2189 if (fields
[lastIdx
] != 0) {
2195 for (int32_t idx
= 0; idx
<= lastIdx
; idx
++) {
2196 if (sep
&& idx
!= 0) {
2199 result
.append((UChar
)(0x0030 + fields
[idx
]/10));
2200 result
.append((UChar
)(0x0030 + fields
[idx
]%10
));
2207 TimeZoneFormat::parseAbuttingAsciiOffsetFields(const UnicodeString
& text
, ParsePosition
& pos
, OffsetFields minFields
, OffsetFields maxFields
, UBool fixedHourWidth
) {
2208 int32_t start
= pos
.getIndex();
2210 int32_t minDigits
= 2 * (minFields
+ 1) - (fixedHourWidth
? 0 : 1);
2211 int32_t maxDigits
= 2 * (maxFields
+ 1);
2213 U_ASSERT(maxDigits
<= MAX_OFFSET_DIGITS
);
2215 int32_t digits
[MAX_OFFSET_DIGITS
] = {};
2216 int32_t numDigits
= 0;
2217 int32_t idx
= start
;
2218 while (numDigits
< maxDigits
&& idx
< text
.length()) {
2219 UChar uch
= text
.charAt(idx
);
2220 int32_t digit
= DIGIT_VAL(uch
);
2224 digits
[numDigits
] = digit
;
2229 if (fixedHourWidth
&& (numDigits
& 1)) {
2230 // Fixed digits, so the number of digits must be even number. Truncating.
2234 if (numDigits
< minDigits
) {
2235 pos
.setErrorIndex(start
);
2239 int32_t hour
= 0, min
= 0, sec
= 0;
2240 UBool bParsed
= FALSE
;
2241 while (numDigits
>= minDigits
) {
2242 switch (numDigits
) {
2247 hour
= digits
[0] * 10 + digits
[1];
2251 min
= digits
[1] * 10 + digits
[2];
2254 hour
= digits
[0] * 10 + digits
[1];
2255 min
= digits
[2] * 10 + digits
[3];
2259 min
= digits
[1] * 10 + digits
[2];
2260 sec
= digits
[3] * 10 + digits
[4];
2263 hour
= digits
[0] * 10 + digits
[1];
2264 min
= digits
[2] * 10 + digits
[3];
2265 sec
= digits
[4] * 10 + digits
[5];
2269 if (hour
<= MAX_OFFSET_HOUR
&& min
<= MAX_OFFSET_MINUTE
&& sec
<= MAX_OFFSET_SECOND
) {
2270 // Successfully parsed
2276 numDigits
-= (fixedHourWidth
? 2 : 1);
2277 hour
= min
= sec
= 0;
2281 pos
.setErrorIndex(start
);
2284 pos
.setIndex(start
+ numDigits
);
2285 return ((((hour
* 60) + min
) * 60) + sec
) * 1000;
2289 TimeZoneFormat::parseAsciiOffsetFields(const UnicodeString
& text
, ParsePosition
& pos
, UChar sep
, OffsetFields minFields
, OffsetFields maxFields
) {
2290 int32_t start
= pos
.getIndex();
2291 int32_t fieldVal
[] = {0, 0, 0};
2292 int32_t fieldLen
[] = {0, -1, -1};
2293 for (int32_t idx
= start
, fieldIdx
= 0; idx
< text
.length() && fieldIdx
<= maxFields
; idx
++) {
2294 UChar c
= text
.charAt(idx
);
2296 if (fieldIdx
== 0) {
2297 if (fieldLen
[0] == 0) {
2301 // 1 digit hour, move to next field
2303 if (fieldLen
[fieldIdx
] != -1) {
2304 // premature minute or seconds field
2307 fieldLen
[fieldIdx
] = 0;
2310 } else if (fieldLen
[fieldIdx
] == -1) {
2311 // no separator after 2 digit field
2314 int32_t digit
= DIGIT_VAL(c
);
2319 fieldVal
[fieldIdx
] = fieldVal
[fieldIdx
] * 10 + digit
;
2320 fieldLen
[fieldIdx
]++;
2321 if (fieldLen
[fieldIdx
] >= 2) {
2322 // parsed 2 digits, move to next field
2328 int32_t parsedLen
= 0;
2329 int32_t parsedFields
= -1;
2332 if (fieldLen
[0] == 0) {
2335 if (fieldVal
[0] > MAX_OFFSET_HOUR
) {
2336 offset
= (fieldVal
[0] / 10) * MILLIS_PER_HOUR
;
2337 parsedFields
= FIELDS_H
;
2341 offset
= fieldVal
[0] * MILLIS_PER_HOUR
;
2342 parsedLen
= fieldLen
[0];
2343 parsedFields
= FIELDS_H
;
2346 if (fieldLen
[1] != 2 || fieldVal
[1] > MAX_OFFSET_MINUTE
) {
2349 offset
+= fieldVal
[1] * MILLIS_PER_MINUTE
;
2350 parsedLen
+= (1 + fieldLen
[1]);
2351 parsedFields
= FIELDS_HM
;
2354 if (fieldLen
[2] != 2 || fieldVal
[2] > MAX_OFFSET_SECOND
) {
2357 offset
+= fieldVal
[2] * MILLIS_PER_SECOND
;
2358 parsedLen
+= (1 + fieldLen
[2]);
2359 parsedFields
= FIELDS_HMS
;
2362 if (parsedFields
< minFields
) {
2363 pos
.setErrorIndex(start
);
2367 pos
.setIndex(start
+ parsedLen
);
2372 TimeZoneFormat::appendOffsetDigits(UnicodeString
& buf
, int32_t n
, uint8_t minDigits
) const {
2373 U_ASSERT(n
>= 0 && n
< 60);
2374 int32_t numDigits
= n
>= 10 ? 2 : 1;
2375 for (int32_t i
= 0; i
< minDigits
- numDigits
; i
++) {
2376 buf
.append(fGMTOffsetDigits
[0]);
2378 if (numDigits
== 2) {
2379 buf
.append(fGMTOffsetDigits
[n
/ 10]);
2381 buf
.append(fGMTOffsetDigits
[n
% 10]);
2384 // ------------------------------------------------------------------
2387 TimeZoneFormat::initGMTPattern(const UnicodeString
& gmtPattern
, UErrorCode
& status
) {
2388 if (U_FAILURE(status
)) {
2391 // This implementation not perfect, but sufficient practically.
2392 int32_t idx
= gmtPattern
.indexOf(ARG0
, ARG0_LEN
, 0);
2394 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2397 fGMTPattern
.setTo(gmtPattern
);
2398 unquote(gmtPattern
.tempSubString(0, idx
), fGMTPatternPrefix
);
2399 unquote(gmtPattern
.tempSubString(idx
+ ARG0_LEN
), fGMTPatternSuffix
);
2403 TimeZoneFormat::unquote(const UnicodeString
& pattern
, UnicodeString
& result
) {
2404 if (pattern
.indexOf(SINGLEQUOTE
) < 0) {
2405 result
.setTo(pattern
);
2409 UBool isPrevQuote
= FALSE
;
2410 UBool inQuote
= FALSE
;
2411 for (int32_t i
= 0; i
< pattern
.length(); i
++) {
2412 UChar c
= pattern
.charAt(i
);
2413 if (c
== SINGLEQUOTE
) {
2416 isPrevQuote
= FALSE
;
2422 isPrevQuote
= FALSE
;
2430 TimeZoneFormat::parseOffsetPattern(const UnicodeString
& pattern
, OffsetFields required
, UErrorCode
& status
) {
2431 if (U_FAILURE(status
)) {
2434 UVector
* result
= new UVector(deleteGMTOffsetField
, NULL
, status
);
2435 if (result
== NULL
) {
2436 status
= U_MEMORY_ALLOCATION_ERROR
;
2440 int32_t checkBits
= 0;
2441 UBool isPrevQuote
= FALSE
;
2442 UBool inQuote
= FALSE
;
2444 UnicodeString
text(textBuf
, 0, UPRV_LENGTHOF(textBuf
));
2445 GMTOffsetField::FieldType itemType
= GMTOffsetField::TEXT
;
2446 int32_t itemLength
= 1;
2448 for (int32_t i
= 0; i
< pattern
.length(); i
++) {
2449 UChar ch
= pattern
.charAt(i
);
2450 if (ch
== SINGLEQUOTE
) {
2452 text
.append(SINGLEQUOTE
);
2453 isPrevQuote
= FALSE
;
2456 if (itemType
!= GMTOffsetField::TEXT
) {
2457 if (GMTOffsetField::isValid(itemType
, itemLength
)) {
2458 GMTOffsetField
* fld
= GMTOffsetField::createTimeField(itemType
, static_cast<uint8_t>(itemLength
), status
);
2459 result
->addElement(fld
, status
);
2460 if (U_FAILURE(status
)) {
2464 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2467 itemType
= GMTOffsetField::TEXT
;
2472 isPrevQuote
= FALSE
;
2476 GMTOffsetField::FieldType tmpType
= GMTOffsetField::getTypeByLetter(ch
);
2477 if (tmpType
!= GMTOffsetField::TEXT
) {
2478 // an offset time pattern character
2479 if (tmpType
== itemType
) {
2482 if (itemType
== GMTOffsetField::TEXT
) {
2483 if (text
.length() > 0) {
2484 GMTOffsetField
* textfld
= GMTOffsetField::createText(text
, status
);
2485 result
->addElement(textfld
, status
);
2486 if (U_FAILURE(status
)) {
2492 if (GMTOffsetField::isValid(itemType
, itemLength
)) {
2493 GMTOffsetField
* fld
= GMTOffsetField::createTimeField(itemType
, static_cast<uint8_t>(itemLength
), status
);
2494 result
->addElement(fld
, status
);
2495 if (U_FAILURE(status
)) {
2499 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2505 checkBits
|= tmpType
;
2509 if (itemType
!= GMTOffsetField::TEXT
) {
2510 if (GMTOffsetField::isValid(itemType
, itemLength
)) {
2511 GMTOffsetField
* fld
= GMTOffsetField::createTimeField(itemType
, static_cast<uint8_t>(itemLength
), status
);
2512 result
->addElement(fld
, status
);
2513 if (U_FAILURE(status
)) {
2517 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2520 itemType
= GMTOffsetField::TEXT
;
2528 if (U_SUCCESS(status
)) {
2529 if (itemType
== GMTOffsetField::TEXT
) {
2530 if (text
.length() > 0) {
2531 GMTOffsetField
* tfld
= GMTOffsetField::createText(text
, status
);
2532 result
->addElement(tfld
, status
);
2535 if (GMTOffsetField::isValid(itemType
, itemLength
)) {
2536 GMTOffsetField
* fld
= GMTOffsetField::createTimeField(itemType
, static_cast<uint8_t>(itemLength
), status
);
2537 result
->addElement(fld
, status
);
2539 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2543 // Check all required fields are set
2544 if (U_SUCCESS(status
)) {
2545 int32_t reqBits
= 0;
2548 reqBits
= GMTOffsetField::HOUR
;
2551 reqBits
= GMTOffsetField::HOUR
| GMTOffsetField::MINUTE
;
2554 reqBits
= GMTOffsetField::HOUR
| GMTOffsetField::MINUTE
| GMTOffsetField::SECOND
;
2557 if (checkBits
== reqBits
) {
2558 // all required fields are set, no extra fields
2570 TimeZoneFormat::expandOffsetPattern(const UnicodeString
& offsetHM
, UnicodeString
& result
, UErrorCode
& status
) {
2571 result
.setToBogus();
2572 if (U_FAILURE(status
)) {
2575 U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN
) == 2);
2577 int32_t idx_mm
= offsetHM
.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN
, 2, 0);
2579 // Bad time zone hour pattern data
2580 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2585 int32_t idx_H
= offsetHM
.tempSubString(0, idx_mm
).lastIndexOf((UChar
)0x0048 /* H */);
2587 sep
= offsetHM
.tempSubString(idx_H
+ 1, idx_mm
- (idx_H
+ 1));
2589 result
.setTo(offsetHM
.tempSubString(0, idx_mm
+ 2));
2591 result
.append(DEFAULT_GMT_OFFSET_SECOND_PATTERN
, -1);
2592 result
.append(offsetHM
.tempSubString(idx_mm
+ 2));
2597 TimeZoneFormat::truncateOffsetPattern(const UnicodeString
& offsetHM
, UnicodeString
& result
, UErrorCode
& status
) {
2598 result
.setToBogus();
2599 if (U_FAILURE(status
)) {
2602 U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN
) == 2);
2604 int32_t idx_mm
= offsetHM
.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN
, 2, 0);
2606 // Bad time zone hour pattern data
2607 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2610 UChar HH
[] = {0x0048, 0x0048};
2611 int32_t idx_HH
= offsetHM
.tempSubString(0, idx_mm
).lastIndexOf(HH
, 2, 0);
2613 return result
.setTo(offsetHM
.tempSubString(0, idx_HH
+ 2));
2615 int32_t idx_H
= offsetHM
.tempSubString(0, idx_mm
).lastIndexOf((UChar
)0x0048, 0);
2617 return result
.setTo(offsetHM
.tempSubString(0, idx_H
+ 1));
2619 // Bad time zone hour pattern data
2620 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2625 TimeZoneFormat::initGMTOffsetPatterns(UErrorCode
& status
) {
2626 for (int32_t type
= 0; type
< UTZFMT_PAT_COUNT
; type
++) {
2628 case UTZFMT_PAT_POSITIVE_H
:
2629 case UTZFMT_PAT_NEGATIVE_H
:
2630 fGMTOffsetPatternItems
[type
] = parseOffsetPattern(fGMTOffsetPatterns
[type
], FIELDS_H
, status
);
2632 case UTZFMT_PAT_POSITIVE_HM
:
2633 case UTZFMT_PAT_NEGATIVE_HM
:
2634 fGMTOffsetPatternItems
[type
] = parseOffsetPattern(fGMTOffsetPatterns
[type
], FIELDS_HM
, status
);
2636 case UTZFMT_PAT_POSITIVE_HMS
:
2637 case UTZFMT_PAT_NEGATIVE_HMS
:
2638 fGMTOffsetPatternItems
[type
] = parseOffsetPattern(fGMTOffsetPatterns
[type
], FIELDS_HMS
, status
);
2642 checkAbuttingHoursAndMinutes();
2646 TimeZoneFormat::checkAbuttingHoursAndMinutes() {
2647 fAbuttingOffsetHoursAndMinutes
= FALSE
;
2648 for (int32_t type
= 0; type
< UTZFMT_PAT_COUNT
; type
++) {
2649 UBool afterH
= FALSE
;
2650 UVector
*items
= fGMTOffsetPatternItems
[type
];
2651 for (int32_t i
= 0; i
< items
->size(); i
++) {
2652 const GMTOffsetField
* item
= (GMTOffsetField
*)items
->elementAt(i
);
2653 GMTOffsetField::FieldType fieldType
= item
->getType();
2654 if (fieldType
!= GMTOffsetField::TEXT
) {
2656 fAbuttingOffsetHoursAndMinutes
= TRUE
;
2658 } else if (fieldType
== GMTOffsetField::HOUR
) {
2661 } else if (afterH
) {
2665 if (fAbuttingOffsetHoursAndMinutes
) {
2672 TimeZoneFormat::toCodePoints(const UnicodeString
& str
, UChar32
* codeArray
, int32_t size
) {
2673 int32_t count
= str
.countChar32();
2674 if (count
!= size
) {
2678 for (int32_t idx
= 0, start
= 0; idx
< size
; idx
++) {
2679 codeArray
[idx
] = str
.char32At(start
);
2680 start
= str
.moveIndex32(start
, 1);
2687 TimeZoneFormat::createTimeZoneForOffset(int32_t offset
) const {
2689 // when offset is 0, we should use "Etc/GMT"
2690 return TimeZone::createTimeZone(UnicodeString(TRUE
, TZID_GMT
, -1));
2692 return ZoneMeta::createCustomTimeZone(offset
);
2695 UTimeZoneFormatTimeType
2696 TimeZoneFormat::getTimeType(UTimeZoneNameType nameType
) {
2698 case UTZNM_LONG_STANDARD
:
2699 case UTZNM_SHORT_STANDARD
:
2700 return UTZFMT_TIME_TYPE_STANDARD
;
2702 case UTZNM_LONG_DAYLIGHT
:
2703 case UTZNM_SHORT_DAYLIGHT
:
2704 return UTZFMT_TIME_TYPE_DAYLIGHT
;
2707 return UTZFMT_TIME_TYPE_UNKNOWN
;
2712 TimeZoneFormat::getTimeZoneID(const TimeZoneNames::MatchInfoCollection
* matches
, int32_t idx
, UnicodeString
& tzID
) const {
2713 if (!matches
->getTimeZoneIDAt(idx
, tzID
)) {
2715 UnicodeString
mzID(mzIDBuf
, 0, UPRV_LENGTHOF(mzIDBuf
));
2716 if (matches
->getMetaZoneIDAt(idx
, mzID
)) {
2717 fTimeZoneNames
->getReferenceZoneID(mzID
, fTargetRegion
, tzID
);
2724 class ZoneIdMatchHandler
: public TextTrieMapSearchResultHandler
{
2726 ZoneIdMatchHandler();
2727 virtual ~ZoneIdMatchHandler();
2729 UBool
handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
);
2730 const UChar
* getID();
2731 int32_t getMatchLen();
2737 ZoneIdMatchHandler::ZoneIdMatchHandler()
2738 : fLen(0), fID(NULL
) {
2741 ZoneIdMatchHandler::~ZoneIdMatchHandler() {
2745 ZoneIdMatchHandler::handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
) {
2746 if (U_FAILURE(status
)) {
2749 if (node
->hasValues()) {
2750 const UChar
* id
= (const UChar
*)node
->getValue(0);
2752 if (fLen
< matchLength
) {
2762 ZoneIdMatchHandler::getID() {
2767 ZoneIdMatchHandler::getMatchLen() {
2772 static void U_CALLCONV
initZoneIdTrie(UErrorCode
&status
) {
2773 U_ASSERT(gZoneIdTrie
== NULL
);
2774 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEFORMAT
, tzfmt_cleanup
);
2775 gZoneIdTrie
= new TextTrieMap(TRUE
, NULL
); // No deleter, because values are pooled by ZoneMeta
2776 if (gZoneIdTrie
== NULL
) {
2777 status
= U_MEMORY_ALLOCATION_ERROR
;
2780 StringEnumeration
*tzenum
= TimeZone::createEnumeration();
2781 const UnicodeString
*id
;
2782 while ((id
= tzenum
->snext(status
)) != NULL
) {
2783 const UChar
* uid
= ZoneMeta::findTimeZoneID(*id
);
2785 gZoneIdTrie
->put(uid
, const_cast<UChar
*>(uid
), status
);
2793 TimeZoneFormat::parseZoneID(const UnicodeString
& text
, ParsePosition
& pos
, UnicodeString
& tzID
) const {
2794 UErrorCode status
= U_ZERO_ERROR
;
2795 umtx_initOnce(gZoneIdTrieInitOnce
, &initZoneIdTrie
, status
);
2797 int32_t start
= pos
.getIndex();
2801 if (U_SUCCESS(status
)) {
2802 LocalPointer
<ZoneIdMatchHandler
> handler(new ZoneIdMatchHandler());
2803 gZoneIdTrie
->search(text
, start
, handler
.getAlias(), status
);
2804 len
= handler
->getMatchLen();
2806 tzID
.setTo(handler
->getID(), -1);
2811 pos
.setIndex(start
+ len
);
2813 pos
.setErrorIndex(start
);
2819 static void U_CALLCONV
initShortZoneIdTrie(UErrorCode
&status
) {
2820 U_ASSERT(gShortZoneIdTrie
== NULL
);
2821 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEFORMAT
, tzfmt_cleanup
);
2822 StringEnumeration
*tzenum
= TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL
, NULL
, NULL
, status
);
2823 if (U_SUCCESS(status
)) {
2824 gShortZoneIdTrie
= new TextTrieMap(TRUE
, NULL
); // No deleter, because values are pooled by ZoneMeta
2825 if (gShortZoneIdTrie
== NULL
) {
2826 status
= U_MEMORY_ALLOCATION_ERROR
;
2828 const UnicodeString
*id
;
2829 while ((id
= tzenum
->snext(status
)) != NULL
) {
2830 const UChar
* uID
= ZoneMeta::findTimeZoneID(*id
);
2831 const UChar
* shortID
= ZoneMeta::getShortID(*id
);
2832 if (shortID
&& uID
) {
2833 gShortZoneIdTrie
->put(shortID
, const_cast<UChar
*>(uID
), status
);
2843 TimeZoneFormat::parseShortZoneID(const UnicodeString
& text
, ParsePosition
& pos
, UnicodeString
& tzID
) const {
2844 UErrorCode status
= U_ZERO_ERROR
;
2845 umtx_initOnce(gShortZoneIdTrieInitOnce
, &initShortZoneIdTrie
, status
);
2847 int32_t start
= pos
.getIndex();
2851 if (U_SUCCESS(status
)) {
2852 LocalPointer
<ZoneIdMatchHandler
> handler(new ZoneIdMatchHandler());
2853 gShortZoneIdTrie
->search(text
, start
, handler
.getAlias(), status
);
2854 len
= handler
->getMatchLen();
2856 tzID
.setTo(handler
->getID(), -1);
2861 pos
.setIndex(start
+ len
);
2863 pos
.setErrorIndex(start
);
2871 TimeZoneFormat::parseExemplarLocation(const UnicodeString
& text
, ParsePosition
& pos
, UnicodeString
& tzID
) const {
2872 int32_t startIdx
= pos
.getIndex();
2873 int32_t parsedPos
= -1;
2876 UErrorCode status
= U_ZERO_ERROR
;
2877 LocalPointer
<TimeZoneNames::MatchInfoCollection
> exemplarMatches(fTimeZoneNames
->find(text
, startIdx
, UTZNM_EXEMPLAR_LOCATION
, status
));
2878 if (U_FAILURE(status
)) {
2879 pos
.setErrorIndex(startIdx
);
2882 int32_t matchIdx
= -1;
2883 if (!exemplarMatches
.isNull()) {
2884 for (int32_t i
= 0; i
< exemplarMatches
->size(); i
++) {
2885 if (startIdx
+ exemplarMatches
->getMatchLengthAt(i
) > parsedPos
) {
2887 parsedPos
= startIdx
+ exemplarMatches
->getMatchLengthAt(i
);
2890 if (parsedPos
> 0) {
2891 pos
.setIndex(parsedPos
);
2892 getTimeZoneID(exemplarMatches
.getAlias(), matchIdx
, tzID
);
2896 if (tzID
.length() == 0) {
2897 pos
.setErrorIndex(startIdx
);