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"
22 #include "bytesinkutil.h"
36 #include "tznames_impl.h" // TextTrieMap
37 #include "patternprops.h"
41 // Bit flags used by the parse method.
42 // The order must match UTimeZoneFormatStyle enum.
43 #define ISO_Z_STYLE_FLAG 0x0080
44 #define ISO_LOCAL_STYLE_FLAG 0x0100
45 static const int16_t STYLE_PARSE_FLAGS
[] = {
46 0x0001, // UTZFMT_STYLE_GENERIC_LOCATION,
47 0x0002, // UTZFMT_STYLE_GENERIC_LONG,
48 0x0004, // UTZFMT_STYLE_GENERIC_SHORT,
49 0x0008, // UTZFMT_STYLE_SPECIFIC_LONG,
50 0x0010, // UTZFMT_STYLE_SPECIFIC_SHORT,
51 0x0020, // UTZFMT_STYLE_LOCALIZED_GMT,
52 0x0040, // UTZFMT_STYLE_LOCALIZED_GMT_SHORT,
53 ISO_Z_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_SHORT,
54 ISO_LOCAL_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT,
55 ISO_Z_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_FIXED,
56 ISO_LOCAL_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED,
57 ISO_Z_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_FULL,
58 ISO_LOCAL_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL,
59 ISO_Z_STYLE_FLAG
, // UTZFMT_STYLE_ISO_EXTENDED_FIXED,
60 ISO_LOCAL_STYLE_FLAG
, // UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED,
61 ISO_Z_STYLE_FLAG
, // UTZFMT_STYLE_ISO_EXTENDED_FULL,
62 ISO_LOCAL_STYLE_FLAG
, // UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL,
63 0x0200, // UTZFMT_STYLE_ZONE_ID,
64 0x0400, // UTZFMT_STYLE_ZONE_ID_SHORT,
65 0x0800 // UTZFMT_STYLE_EXEMPLAR_LOCATION
68 static const char gZoneStringsTag
[] = "zoneStrings";
69 static const char gGmtFormatTag
[]= "gmtFormat";
70 static const char gGmtZeroFormatTag
[] = "gmtZeroFormat";
71 static const char gHourFormatTag
[]= "hourFormat";
73 static const UChar TZID_GMT
[] = {0x0045, 0x0074, 0x0063, 0x002F, 0x0047, 0x004D, 0x0054, 0}; // Etc/GMT
74 static const UChar UNKNOWN_ZONE_ID
[] = {
75 0x0045, 0x0074, 0x0063, 0x002F, 0x0055, 0x006E, 0x006B, 0x006E, 0x006F, 0x0077, 0x006E, 0}; // Etc/Unknown
76 static const UChar UNKNOWN_SHORT_ZONE_ID
[] = {0x0075, 0x006E, 0x006B, 0}; // unk
77 static const UChar UNKNOWN_LOCATION
[] = {0x0055, 0x006E, 0x006B, 0x006E, 0x006F, 0x0077, 0x006E, 0}; // Unknown
79 static const UChar DEFAULT_GMT_PATTERN
[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0}; // GMT{0}
80 //static const UChar DEFAULT_GMT_ZERO[] = {0x0047, 0x004D, 0x0054, 0}; // GMT
81 static const UChar DEFAULT_GMT_POSITIVE_HM
[] = {0x002B, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // +H:mm
82 static const UChar DEFAULT_GMT_POSITIVE_HMS
[] = {0x002B, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // +H:mm:ss
83 static const UChar DEFAULT_GMT_NEGATIVE_HM
[] = {0x002D, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // -H:mm
84 static const UChar DEFAULT_GMT_NEGATIVE_HMS
[] = {0x002D, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // -H:mm:ss
85 static const UChar DEFAULT_GMT_POSITIVE_H
[] = {0x002B, 0x0048, 0}; // +H
86 static const UChar DEFAULT_GMT_NEGATIVE_H
[] = {0x002D, 0x0048, 0}; // -H
88 static const UChar32 DEFAULT_GMT_DIGITS
[] = {
89 0x0030, 0x0031, 0x0032, 0x0033, 0x0034,
90 0x0035, 0x0036, 0x0037, 0x0038, 0x0039
93 static const UChar DEFAULT_GMT_OFFSET_SEP
= 0x003A; // ':'
95 static const UChar ARG0
[] = {0x007B, 0x0030, 0x007D}; // "{0}"
96 static const int32_t ARG0_LEN
= 3;
98 static const UChar DEFAULT_GMT_OFFSET_MINUTE_PATTERN
[] = {0x006D, 0x006D, 0}; // "mm"
99 static const UChar DEFAULT_GMT_OFFSET_SECOND_PATTERN
[] = {0x0073, 0x0073, 0}; // "ss"
101 static const UChar ALT_GMT_STRINGS
[][4] = {
102 {0x0047, 0x004D, 0x0054, 0}, // GMT
103 {0x0055, 0x0054, 0x0043, 0}, // UTC
104 {0x0055, 0x0054, 0, 0}, // UT
108 // Order of GMT offset pattern parsing, *_HMS must be evaluated first
109 // because *_HM is most likely a substring of *_HMS
110 static const int32_t PARSE_GMT_OFFSET_TYPES
[] = {
111 UTZFMT_PAT_POSITIVE_HMS
,
112 UTZFMT_PAT_NEGATIVE_HMS
,
113 UTZFMT_PAT_POSITIVE_HM
,
114 UTZFMT_PAT_NEGATIVE_HM
,
115 UTZFMT_PAT_POSITIVE_H
,
116 UTZFMT_PAT_NEGATIVE_H
,
120 static const UChar SINGLEQUOTE
= 0x0027;
121 static const UChar PLUS
= 0x002B;
122 static const UChar MINUS
= 0x002D;
123 static const UChar ISO8601_UTC
= 0x005A; // 'Z'
124 static const UChar ISO8601_SEP
= 0x003A; // ':'
126 static const int32_t MILLIS_PER_HOUR
= 60 * 60 * 1000;
127 static const int32_t MILLIS_PER_MINUTE
= 60 * 1000;
128 static const int32_t MILLIS_PER_SECOND
= 1000;
130 // Maximum offset (exclusive) in millisecond supported by offset formats
131 static int32_t MAX_OFFSET
= 24 * MILLIS_PER_HOUR
;
133 // Maximum values for GMT offset fields
134 static const int32_t MAX_OFFSET_HOUR
= 23;
135 static const int32_t MAX_OFFSET_MINUTE
= 59;
136 static const int32_t MAX_OFFSET_SECOND
= 59;
138 static const int32_t UNKNOWN_OFFSET
= 0x7FFFFFFF;
140 static const int32_t ALL_SIMPLE_NAME_TYPES
= UTZNM_LONG_STANDARD
| UTZNM_LONG_DAYLIGHT
| UTZNM_SHORT_STANDARD
| UTZNM_SHORT_DAYLIGHT
| UTZNM_EXEMPLAR_LOCATION
;
141 static const int32_t ALL_GENERIC_NAME_TYPES
= UTZGNM_LOCATION
| UTZGNM_LONG
| UTZGNM_SHORT
;
143 #define DIGIT_VAL(c) (0x0030 <= (c) && (c) <= 0x0039 ? (c) - 0x0030 : -1)
144 #define MAX_OFFSET_DIGITS 6
146 // Time Zone ID/Short ID trie
147 static TextTrieMap
*gZoneIdTrie
= NULL
;
148 static icu::UInitOnce gZoneIdTrieInitOnce
= U_INITONCE_INITIALIZER
;
150 static TextTrieMap
*gShortZoneIdTrie
= NULL
;
151 static icu::UInitOnce gShortZoneIdTrieInitOnce
= U_INITONCE_INITIALIZER
;
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) {
332 CharStringByteSink
sink(&loc
);
333 ulocimp_addLikelySubtags(fLocale
.getName(), sink
, &status
);
336 regionLen
= uloc_getCountry(loc
.data(), fTargetRegion
, sizeof(fTargetRegion
), &status
);
337 if (U_SUCCESS(status
)) {
338 fTargetRegion
[regionLen
] = 0;
342 } else if (regionLen
< (int32_t)sizeof(fTargetRegion
)) {
343 uprv_strcpy(fTargetRegion
, region
);
345 fTargetRegion
[0] = 0;
348 fTimeZoneNames
= TimeZoneNames::createInstance(locale
, status
);
349 // fTimeZoneGenericNames is lazily instantiated
350 if (U_FAILURE(status
)) {
354 const UChar
* gmtPattern
= NULL
;
355 const UChar
* hourFormats
= NULL
;
357 UResourceBundle
*zoneBundle
= ures_open(U_ICUDATA_ZONE
, locale
.getName(), &status
);
358 UResourceBundle
*zoneStringsArray
= ures_getByKeyWithFallback(zoneBundle
, gZoneStringsTag
, NULL
, &status
);
359 if (U_SUCCESS(status
)) {
362 resStr
= ures_getStringByKeyWithFallback(zoneStringsArray
, gGmtFormatTag
, &len
, &status
);
366 resStr
= ures_getStringByKeyWithFallback(zoneStringsArray
, gGmtZeroFormatTag
, &len
, &status
);
368 fGMTZeroFormat
.setTo(TRUE
, resStr
, len
);
370 resStr
= ures_getStringByKeyWithFallback(zoneStringsArray
, gHourFormatTag
, &len
, &status
);
372 hourFormats
= resStr
;
374 ures_close(zoneStringsArray
);
375 ures_close(zoneBundle
);
378 if (gmtPattern
== NULL
) {
379 gmtPattern
= DEFAULT_GMT_PATTERN
;
381 initGMTPattern(UnicodeString(TRUE
, gmtPattern
, -1), status
);
383 UBool useDefaultOffsetPatterns
= TRUE
;
385 UChar
*sep
= u_strchr(hourFormats
, (UChar
)0x003B /* ';' */);
387 UErrorCode tmpStatus
= U_ZERO_ERROR
;
388 fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HM
].setTo(FALSE
, hourFormats
, (int32_t)(sep
- hourFormats
));
389 fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HM
].setTo(TRUE
, sep
+ 1, -1);
390 expandOffsetPattern(fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HM
], fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HMS
], tmpStatus
);
391 expandOffsetPattern(fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HM
], fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HMS
], tmpStatus
);
392 truncateOffsetPattern(fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HM
], fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_H
], tmpStatus
);
393 truncateOffsetPattern(fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HM
], fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_H
], tmpStatus
);
394 if (U_SUCCESS(tmpStatus
)) {
395 useDefaultOffsetPatterns
= FALSE
;
399 if (useDefaultOffsetPatterns
) {
400 fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_H
].setTo(TRUE
, DEFAULT_GMT_POSITIVE_H
, -1);
401 fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HM
].setTo(TRUE
, DEFAULT_GMT_POSITIVE_HM
, -1);
402 fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HMS
].setTo(TRUE
, DEFAULT_GMT_POSITIVE_HMS
, -1);
403 fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_H
].setTo(TRUE
, DEFAULT_GMT_NEGATIVE_H
, -1);
404 fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HM
].setTo(TRUE
, DEFAULT_GMT_NEGATIVE_HM
, -1);
405 fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HMS
].setTo(TRUE
, DEFAULT_GMT_NEGATIVE_HMS
, -1);
407 initGMTOffsetPatterns(status
);
409 NumberingSystem
* ns
= NumberingSystem::createInstance(locale
, status
);
410 UBool useDefDigits
= TRUE
;
411 if (ns
&& !ns
->isAlgorithmic()) {
412 UnicodeString digits
= ns
->getDescription();
413 useDefDigits
= !toCodePoints(digits
, fGMTOffsetDigits
, 10);
416 uprv_memcpy(fGMTOffsetDigits
, DEFAULT_GMT_DIGITS
, sizeof(UChar32
) * 10);
421 TimeZoneFormat::TimeZoneFormat(const TimeZoneFormat
& other
)
422 : Format(other
), fTimeZoneNames(NULL
), fTimeZoneGenericNames(NULL
),
423 fTZDBTimeZoneNames(NULL
) {
425 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
; i
++) {
426 fGMTOffsetPatternItems
[i
] = NULL
;
432 TimeZoneFormat::~TimeZoneFormat() {
433 delete fTimeZoneNames
;
434 delete fTimeZoneGenericNames
;
435 delete fTZDBTimeZoneNames
;
436 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
; i
++) {
437 delete fGMTOffsetPatternItems
[i
];
442 TimeZoneFormat::operator=(const TimeZoneFormat
& other
) {
443 if (this == &other
) {
447 delete fTimeZoneNames
;
448 delete fTimeZoneGenericNames
;
449 fTimeZoneGenericNames
= NULL
;
450 delete fTZDBTimeZoneNames
;
451 fTZDBTimeZoneNames
= NULL
;
453 fLocale
= other
.fLocale
;
454 uprv_memcpy(fTargetRegion
, other
.fTargetRegion
, sizeof(fTargetRegion
));
456 fTimeZoneNames
= other
.fTimeZoneNames
->clone();
457 if (other
.fTimeZoneGenericNames
) {
458 // TODO: this test has dubious thread safety.
459 fTimeZoneGenericNames
= other
.fTimeZoneGenericNames
->clone();
462 fGMTPattern
= other
.fGMTPattern
;
463 fGMTPatternPrefix
= other
.fGMTPatternPrefix
;
464 fGMTPatternSuffix
= other
.fGMTPatternSuffix
;
466 UErrorCode status
= U_ZERO_ERROR
;
467 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
; i
++) {
468 fGMTOffsetPatterns
[i
] = other
.fGMTOffsetPatterns
[i
];
469 delete fGMTOffsetPatternItems
[i
];
470 fGMTOffsetPatternItems
[i
] = NULL
;
472 initGMTOffsetPatterns(status
);
473 U_ASSERT(U_SUCCESS(status
));
475 fGMTZeroFormat
= other
.fGMTZeroFormat
;
477 uprv_memcpy(fGMTOffsetDigits
, other
.fGMTOffsetDigits
, sizeof(fGMTOffsetDigits
));
479 fDefParseOptionFlags
= other
.fDefParseOptionFlags
;
486 TimeZoneFormat::operator==(const Format
& other
) const {
487 TimeZoneFormat
* tzfmt
= (TimeZoneFormat
*)&other
;
490 fLocale
== tzfmt
->fLocale
491 && fGMTPattern
== tzfmt
->fGMTPattern
492 && fGMTZeroFormat
== tzfmt
->fGMTZeroFormat
493 && *fTimeZoneNames
== *tzfmt
->fTimeZoneNames
;
495 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
&& isEqual
; i
++) {
496 isEqual
= fGMTOffsetPatterns
[i
] == tzfmt
->fGMTOffsetPatterns
[i
];
498 for (int32_t i
= 0; i
< 10 && isEqual
; i
++) {
499 isEqual
= fGMTOffsetDigits
[i
] == tzfmt
->fGMTOffsetDigits
[i
];
502 // Check fTimeZoneGenericNames. For now,
503 // if fTimeZoneNames is same, fTimeZoneGenericNames should
504 // be also equivalent.
509 TimeZoneFormat::clone() const {
510 return new TimeZoneFormat(*this);
513 TimeZoneFormat
* U_EXPORT2
514 TimeZoneFormat::createInstance(const Locale
& locale
, UErrorCode
& status
) {
515 TimeZoneFormat
* tzfmt
= new TimeZoneFormat(locale
, status
);
516 if (U_SUCCESS(status
)) {
523 // ------------------------------------------------------------------
527 TimeZoneFormat::getTimeZoneNames() const {
528 return (const TimeZoneNames
*)fTimeZoneNames
;
532 TimeZoneFormat::adoptTimeZoneNames(TimeZoneNames
*tznames
) {
533 delete fTimeZoneNames
;
534 fTimeZoneNames
= tznames
;
536 // TODO - We should also update fTimeZoneGenericNames
540 TimeZoneFormat::setTimeZoneNames(const TimeZoneNames
&tznames
) {
541 delete fTimeZoneNames
;
542 fTimeZoneNames
= tznames
.clone();
544 // TODO - We should also update fTimeZoneGenericNames
548 TimeZoneFormat::setDefaultParseOptions(uint32_t flags
) {
549 fDefParseOptionFlags
= flags
;
553 TimeZoneFormat::getDefaultParseOptions(void) const {
554 return fDefParseOptionFlags
;
559 TimeZoneFormat::getGMTPattern(UnicodeString
& pattern
) const {
560 return pattern
.setTo(fGMTPattern
);
564 TimeZoneFormat::setGMTPattern(const UnicodeString
& pattern
, UErrorCode
& status
) {
565 initGMTPattern(pattern
, status
);
569 TimeZoneFormat::getGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type
, UnicodeString
& pattern
) const {
570 return pattern
.setTo(fGMTOffsetPatterns
[type
]);
574 TimeZoneFormat::setGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type
, const UnicodeString
& pattern
, UErrorCode
& status
) {
575 if (U_FAILURE(status
)) {
578 if (pattern
== fGMTOffsetPatterns
[type
]) {
583 OffsetFields required
= FIELDS_HM
;
585 case UTZFMT_PAT_POSITIVE_H
:
586 case UTZFMT_PAT_NEGATIVE_H
:
589 case UTZFMT_PAT_POSITIVE_HM
:
590 case UTZFMT_PAT_NEGATIVE_HM
:
591 required
= FIELDS_HM
;
593 case UTZFMT_PAT_POSITIVE_HMS
:
594 case UTZFMT_PAT_NEGATIVE_HMS
:
595 required
= FIELDS_HMS
;
601 UVector
* patternItems
= parseOffsetPattern(pattern
, required
, status
);
602 if (patternItems
== NULL
) {
606 fGMTOffsetPatterns
[type
].setTo(pattern
);
607 delete fGMTOffsetPatternItems
[type
];
608 fGMTOffsetPatternItems
[type
] = patternItems
;
609 checkAbuttingHoursAndMinutes();
613 TimeZoneFormat::getGMTOffsetDigits(UnicodeString
& digits
) const {
615 for (int32_t i
= 0; i
< 10; i
++) {
616 digits
.append(fGMTOffsetDigits
[i
]);
622 TimeZoneFormat::setGMTOffsetDigits(const UnicodeString
& digits
, UErrorCode
& status
) {
623 if (U_FAILURE(status
)) {
626 UChar32 digitArray
[10];
627 if (!toCodePoints(digits
, digitArray
, 10)) {
628 status
= U_ILLEGAL_ARGUMENT_ERROR
;
631 uprv_memcpy(fGMTOffsetDigits
, digitArray
, sizeof(UChar32
)*10);
635 TimeZoneFormat::getGMTZeroFormat(UnicodeString
& gmtZeroFormat
) const {
636 return gmtZeroFormat
.setTo(fGMTZeroFormat
);
640 TimeZoneFormat::setGMTZeroFormat(const UnicodeString
& gmtZeroFormat
, UErrorCode
& status
) {
641 if (U_SUCCESS(status
)) {
642 if (gmtZeroFormat
.isEmpty()) {
643 status
= U_ILLEGAL_ARGUMENT_ERROR
;
644 } else if (gmtZeroFormat
!= fGMTZeroFormat
) {
645 fGMTZeroFormat
.setTo(gmtZeroFormat
);
650 // ------------------------------------------------------------------
654 TimeZoneFormat::format(UTimeZoneFormatStyle style
, const TimeZone
& tz
, UDate date
,
655 UnicodeString
& name
, UTimeZoneFormatTimeType
* timeType
/* = NULL */) const {
657 *timeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
660 UBool noOffsetFormatFallback
= FALSE
;
663 case UTZFMT_STYLE_GENERIC_LOCATION
:
664 formatGeneric(tz
, UTZGNM_LOCATION
, date
, name
);
666 case UTZFMT_STYLE_GENERIC_LONG
:
667 formatGeneric(tz
, UTZGNM_LONG
, date
, name
);
669 case UTZFMT_STYLE_GENERIC_SHORT
:
670 formatGeneric(tz
, UTZGNM_SHORT
, date
, name
);
672 case UTZFMT_STYLE_SPECIFIC_LONG
:
673 formatSpecific(tz
, UTZNM_LONG_STANDARD
, UTZNM_LONG_DAYLIGHT
, date
, name
, timeType
);
675 case UTZFMT_STYLE_SPECIFIC_SHORT
:
676 formatSpecific(tz
, UTZNM_SHORT_STANDARD
, UTZNM_SHORT_DAYLIGHT
, date
, name
, timeType
);
679 case UTZFMT_STYLE_ZONE_ID
:
681 noOffsetFormatFallback
= TRUE
;
683 case UTZFMT_STYLE_ZONE_ID_SHORT
:
685 const UChar
* shortID
= ZoneMeta::getShortID(tz
);
686 if (shortID
== NULL
) {
687 shortID
= UNKNOWN_SHORT_ZONE_ID
;
689 name
.setTo(shortID
, -1);
691 noOffsetFormatFallback
= TRUE
;
694 case UTZFMT_STYLE_EXEMPLAR_LOCATION
:
695 formatExemplarLocation(tz
, name
);
696 noOffsetFormatFallback
= TRUE
;
700 // will be handled below
704 if (name
.isEmpty() && !noOffsetFormatFallback
) {
705 UErrorCode status
= U_ZERO_ERROR
;
706 int32_t rawOffset
, dstOffset
;
707 tz
.getOffset(date
, FALSE
, rawOffset
, dstOffset
, status
);
708 int32_t offset
= rawOffset
+ dstOffset
;
709 if (U_SUCCESS(status
)) {
711 case UTZFMT_STYLE_GENERIC_LOCATION
:
712 case UTZFMT_STYLE_GENERIC_LONG
:
713 case UTZFMT_STYLE_SPECIFIC_LONG
:
714 case UTZFMT_STYLE_LOCALIZED_GMT
:
715 formatOffsetLocalizedGMT(offset
, name
, status
);
718 case UTZFMT_STYLE_GENERIC_SHORT
:
719 case UTZFMT_STYLE_SPECIFIC_SHORT
:
720 case UTZFMT_STYLE_LOCALIZED_GMT_SHORT
:
721 formatOffsetShortLocalizedGMT(offset
, name
, status
);
724 case UTZFMT_STYLE_ISO_BASIC_SHORT
:
725 formatOffsetISO8601Basic(offset
, TRUE
, TRUE
, TRUE
, name
, status
);
728 case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT
:
729 formatOffsetISO8601Basic(offset
, FALSE
, TRUE
, TRUE
, name
, status
);
732 case UTZFMT_STYLE_ISO_BASIC_FIXED
:
733 formatOffsetISO8601Basic(offset
, TRUE
, FALSE
, TRUE
, name
, status
);
736 case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED
:
737 formatOffsetISO8601Basic(offset
, FALSE
, FALSE
, TRUE
, name
, status
);
740 case UTZFMT_STYLE_ISO_EXTENDED_FIXED
:
741 formatOffsetISO8601Extended(offset
, TRUE
, FALSE
, TRUE
, name
, status
);
744 case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED
:
745 formatOffsetISO8601Extended(offset
, FALSE
, FALSE
, TRUE
, name
, status
);
748 case UTZFMT_STYLE_ISO_BASIC_FULL
:
749 formatOffsetISO8601Basic(offset
, TRUE
, FALSE
, FALSE
, name
, status
);
752 case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
:
753 formatOffsetISO8601Basic(offset
, FALSE
, FALSE
, FALSE
, name
, status
);
756 case UTZFMT_STYLE_ISO_EXTENDED_FULL
:
757 formatOffsetISO8601Extended(offset
, TRUE
, FALSE
, FALSE
, name
, status
);
760 case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL
:
761 formatOffsetISO8601Extended(offset
, FALSE
, FALSE
, FALSE
, name
, status
);
765 // UTZFMT_STYLE_ZONE_ID, UTZFMT_STYLE_ZONE_ID_SHORT, UTZFMT_STYLE_EXEMPLAR_LOCATION
770 *timeType
= (dstOffset
!= 0) ? UTZFMT_TIME_TYPE_DAYLIGHT
: UTZFMT_TIME_TYPE_STANDARD
;
779 TimeZoneFormat::format(const Formattable
& obj
, UnicodeString
& appendTo
,
780 FieldPosition
& pos
, UErrorCode
& status
) const {
781 if (U_FAILURE(status
)) {
784 UDate date
= Calendar::getNow();
785 if (obj
.getType() == Formattable::kObject
) {
786 const UObject
* formatObj
= obj
.getObject();
787 const TimeZone
* tz
= dynamic_cast<const TimeZone
*>(formatObj
);
789 const Calendar
* cal
= dynamic_cast<const Calendar
*>(formatObj
);
791 tz
= &cal
->getTimeZone();
792 date
= cal
->getTime(status
);
796 int32_t rawOffset
, dstOffset
;
797 tz
->getOffset(date
, FALSE
, rawOffset
, dstOffset
, status
);
798 UChar buf
[ZONE_NAME_U16_MAX
];
799 UnicodeString
result(buf
, 0, UPRV_LENGTHOF(buf
));
800 formatOffsetLocalizedGMT(rawOffset
+ dstOffset
, result
, status
);
801 if (U_SUCCESS(status
)) {
802 appendTo
.append(result
);
803 if (pos
.getField() == UDAT_TIMEZONE_FIELD
) {
804 pos
.setBeginIndex(0);
805 pos
.setEndIndex(result
.length());
814 TimeZoneFormat::parse(UTimeZoneFormatStyle style
, const UnicodeString
& text
, ParsePosition
& pos
,
815 UTimeZoneFormatTimeType
* timeType
/*= NULL*/) const {
816 return parse(style
, text
, pos
, getDefaultParseOptions(), timeType
);
820 TimeZoneFormat::parse(UTimeZoneFormatStyle style
, const UnicodeString
& text
, ParsePosition
& pos
,
821 int32_t parseOptions
, UTimeZoneFormatTimeType
* timeType
/* = NULL */) const {
823 *timeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
826 int32_t startIdx
= pos
.getIndex();
827 int32_t maxPos
= text
.length();
830 // Styles using localized GMT format as fallback
831 UBool fallbackLocalizedGMT
=
832 (style
== UTZFMT_STYLE_SPECIFIC_LONG
|| style
== UTZFMT_STYLE_GENERIC_LONG
|| style
== UTZFMT_STYLE_GENERIC_LOCATION
);
833 UBool fallbackShortLocalizedGMT
=
834 (style
== UTZFMT_STYLE_SPECIFIC_SHORT
|| style
== UTZFMT_STYLE_GENERIC_SHORT
);
836 int32_t evaluated
= 0; // bit flags representing already evaluated styles
837 ParsePosition
tmpPos(startIdx
);
839 int32_t parsedOffset
= UNKNOWN_OFFSET
; // stores successfully parsed offset for later use
840 int32_t parsedPos
= -1; // stores successfully parsed offset position for later use
842 // Try localized GMT format first if necessary
843 if (fallbackLocalizedGMT
|| fallbackShortLocalizedGMT
) {
844 UBool hasDigitOffset
= FALSE
;
845 offset
= parseOffsetLocalizedGMT(text
, tmpPos
, fallbackShortLocalizedGMT
, &hasDigitOffset
);
846 if (tmpPos
.getErrorIndex() == -1) {
847 // Even when the input text was successfully parsed as a localized GMT format text,
848 // we may still need to evaluate the specified style if -
849 // 1) GMT zero format was used, and
850 // 2) The input text was not completely processed
851 if (tmpPos
.getIndex() == maxPos
|| hasDigitOffset
) {
852 pos
.setIndex(tmpPos
.getIndex());
853 return createTimeZoneForOffset(offset
);
855 parsedOffset
= offset
;
856 parsedPos
= tmpPos
.getIndex();
858 // Note: For now, no distinction between long/short localized GMT format in the parser.
859 // This might be changed in future.
860 // evaluated |= (fallbackLocalizedGMT ? STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT] : STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT]);
861 evaluated
|= STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT
] | STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT_SHORT
];
864 UErrorCode status
= U_ZERO_ERROR
;
866 UnicodeString
tzID(tzIDBuf
, 0, UPRV_LENGTHOF(tzIDBuf
));
868 UBool parseTZDBAbbrev
= ((parseOptions
& UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
) != 0);
870 // Try the specified style
872 case UTZFMT_STYLE_LOCALIZED_GMT
:
874 tmpPos
.setIndex(startIdx
);
875 tmpPos
.setErrorIndex(-1);
877 offset
= parseOffsetLocalizedGMT(text
, tmpPos
);
878 if (tmpPos
.getErrorIndex() == -1) {
879 pos
.setIndex(tmpPos
.getIndex());
880 return createTimeZoneForOffset(offset
);
883 // Note: For now, no distinction between long/short localized GMT format in the parser.
884 // This might be changed in future.
885 evaluated
|= STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT_SHORT
];
889 case UTZFMT_STYLE_LOCALIZED_GMT_SHORT
:
891 tmpPos
.setIndex(startIdx
);
892 tmpPos
.setErrorIndex(-1);
894 offset
= parseOffsetShortLocalizedGMT(text
, tmpPos
);
895 if (tmpPos
.getErrorIndex() == -1) {
896 pos
.setIndex(tmpPos
.getIndex());
897 return createTimeZoneForOffset(offset
);
900 // Note: For now, no distinction between long/short localized GMT format in the parser.
901 // This might be changed in future.
902 evaluated
|= STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT
];
906 case UTZFMT_STYLE_ISO_BASIC_SHORT
:
907 case UTZFMT_STYLE_ISO_BASIC_FIXED
:
908 case UTZFMT_STYLE_ISO_BASIC_FULL
:
909 case UTZFMT_STYLE_ISO_EXTENDED_FIXED
:
910 case UTZFMT_STYLE_ISO_EXTENDED_FULL
:
912 tmpPos
.setIndex(startIdx
);
913 tmpPos
.setErrorIndex(-1);
915 offset
= parseOffsetISO8601(text
, tmpPos
);
916 if (tmpPos
.getErrorIndex() == -1) {
917 pos
.setIndex(tmpPos
.getIndex());
918 return createTimeZoneForOffset(offset
);
924 case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT
:
925 case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED
:
926 case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
:
927 case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED
:
928 case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL
:
930 tmpPos
.setIndex(startIdx
);
931 tmpPos
.setErrorIndex(-1);
933 // Exclude the case of UTC Indicator "Z" here
934 UBool hasDigitOffset
= FALSE
;
935 offset
= parseOffsetISO8601(text
, tmpPos
, FALSE
, &hasDigitOffset
);
936 if (tmpPos
.getErrorIndex() == -1 && hasDigitOffset
) {
937 pos
.setIndex(tmpPos
.getIndex());
938 return createTimeZoneForOffset(offset
);
944 case UTZFMT_STYLE_SPECIFIC_LONG
:
945 case UTZFMT_STYLE_SPECIFIC_SHORT
:
948 int32_t nameTypes
= 0;
949 if (style
== UTZFMT_STYLE_SPECIFIC_LONG
) {
950 nameTypes
= (UTZNM_LONG_STANDARD
| UTZNM_LONG_DAYLIGHT
);
952 U_ASSERT(style
== UTZFMT_STYLE_SPECIFIC_SHORT
);
953 nameTypes
= (UTZNM_SHORT_STANDARD
| UTZNM_SHORT_DAYLIGHT
);
955 LocalPointer
<TimeZoneNames::MatchInfoCollection
> specificMatches(fTimeZoneNames
->find(text
, startIdx
, nameTypes
, status
));
956 if (U_FAILURE(status
)) {
957 pos
.setErrorIndex(startIdx
);
960 if (!specificMatches
.isNull()) {
961 int32_t matchIdx
= -1;
962 int32_t matchPos
= -1;
963 for (int32_t i
= 0; i
< specificMatches
->size(); i
++) {
964 matchPos
= startIdx
+ specificMatches
->getMatchLengthAt(i
);
965 if (matchPos
> parsedPos
) {
967 parsedPos
= matchPos
;
972 *timeType
= getTimeType(specificMatches
->getNameTypeAt(matchIdx
));
974 pos
.setIndex(matchPos
);
975 getTimeZoneID(specificMatches
.getAlias(), matchIdx
, tzID
);
976 U_ASSERT(!tzID
.isEmpty());
977 return TimeZone::createTimeZone(tzID
);
981 if (parseTZDBAbbrev
&& style
== UTZFMT_STYLE_SPECIFIC_SHORT
) {
982 U_ASSERT((nameTypes
& UTZNM_SHORT_STANDARD
) != 0);
983 U_ASSERT((nameTypes
& UTZNM_SHORT_DAYLIGHT
) != 0);
985 const TZDBTimeZoneNames
*tzdbTimeZoneNames
= getTZDBTimeZoneNames(status
);
986 if (U_SUCCESS(status
)) {
987 LocalPointer
<TimeZoneNames::MatchInfoCollection
> tzdbNameMatches(
988 tzdbTimeZoneNames
->find(text
, startIdx
, nameTypes
, status
));
989 if (U_FAILURE(status
)) {
990 pos
.setErrorIndex(startIdx
);
993 if (!tzdbNameMatches
.isNull()) {
994 int32_t matchIdx
= -1;
995 int32_t matchPos
= -1;
996 for (int32_t i
= 0; i
< tzdbNameMatches
->size(); i
++) {
997 matchPos
= startIdx
+ tzdbNameMatches
->getMatchLengthAt(i
);
998 if (matchPos
> parsedPos
) {
1000 parsedPos
= matchPos
;
1003 if (matchIdx
>= 0) {
1005 *timeType
= getTimeType(tzdbNameMatches
->getNameTypeAt(matchIdx
));
1007 pos
.setIndex(matchPos
);
1008 getTimeZoneID(tzdbNameMatches
.getAlias(), matchIdx
, tzID
);
1009 U_ASSERT(!tzID
.isEmpty());
1010 return TimeZone::createTimeZone(tzID
);
1017 case UTZFMT_STYLE_GENERIC_LONG
:
1018 case UTZFMT_STYLE_GENERIC_SHORT
:
1019 case UTZFMT_STYLE_GENERIC_LOCATION
:
1021 int32_t genericNameTypes
= 0;
1023 case UTZFMT_STYLE_GENERIC_LOCATION
:
1024 genericNameTypes
= UTZGNM_LOCATION
;
1027 case UTZFMT_STYLE_GENERIC_LONG
:
1028 genericNameTypes
= UTZGNM_LONG
| UTZGNM_LOCATION
;
1031 case UTZFMT_STYLE_GENERIC_SHORT
:
1032 genericNameTypes
= UTZGNM_SHORT
| UTZGNM_LOCATION
;
1040 UTimeZoneFormatTimeType tt
= UTZFMT_TIME_TYPE_UNKNOWN
;
1041 const TimeZoneGenericNames
*gnames
= getTimeZoneGenericNames(status
);
1042 if (U_SUCCESS(status
)) {
1043 len
= gnames
->findBestMatch(text
, startIdx
, genericNameTypes
, tzID
, tt
, status
);
1045 if (U_FAILURE(status
)) {
1046 pos
.setErrorIndex(startIdx
);
1054 pos
.setIndex(startIdx
+ len
);
1055 U_ASSERT(!tzID
.isEmpty());
1056 return TimeZone::createTimeZone(tzID
);
1061 case UTZFMT_STYLE_ZONE_ID
:
1063 tmpPos
.setIndex(startIdx
);
1064 tmpPos
.setErrorIndex(-1);
1066 parseZoneID(text
, tmpPos
, tzID
);
1067 if (tmpPos
.getErrorIndex() == -1) {
1068 pos
.setIndex(tmpPos
.getIndex());
1069 return TimeZone::createTimeZone(tzID
);
1073 case UTZFMT_STYLE_ZONE_ID_SHORT
:
1075 tmpPos
.setIndex(startIdx
);
1076 tmpPos
.setErrorIndex(-1);
1078 parseShortZoneID(text
, tmpPos
, tzID
);
1079 if (tmpPos
.getErrorIndex() == -1) {
1080 pos
.setIndex(tmpPos
.getIndex());
1081 return TimeZone::createTimeZone(tzID
);
1085 case UTZFMT_STYLE_EXEMPLAR_LOCATION
:
1087 tmpPos
.setIndex(startIdx
);
1088 tmpPos
.setErrorIndex(-1);
1090 parseExemplarLocation(text
, tmpPos
, tzID
);
1091 if (tmpPos
.getErrorIndex() == -1) {
1092 pos
.setIndex(tmpPos
.getIndex());
1093 return TimeZone::createTimeZone(tzID
);
1098 evaluated
|= STYLE_PARSE_FLAGS
[style
];
1101 if (parsedPos
> startIdx
) {
1102 // When the specified style is one of SPECIFIC_XXX or GENERIC_XXX, we tried to parse the input
1103 // as localized GMT format earlier. If parsedOffset is positive, it means it was successfully
1104 // parsed as localized GMT format, but offset digits were not detected (more specifically, GMT
1105 // zero format). Then, it tried to find a match within the set of display names, but could not
1106 // find a match. At this point, we can safely assume the input text contains the localized
1108 U_ASSERT(parsedOffset
!= UNKNOWN_OFFSET
);
1109 pos
.setIndex(parsedPos
);
1110 return createTimeZoneForOffset(parsedOffset
);
1113 // Failed to parse the input text as the time zone format in the specified style.
1114 // Check the longest match among other styles below.
1115 UChar parsedIDBuf
[32];
1116 UnicodeString
parsedID(parsedIDBuf
, 0, UPRV_LENGTHOF(parsedIDBuf
));
1117 UTimeZoneFormatTimeType parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1119 U_ASSERT(parsedPos
< 0);
1120 U_ASSERT(parsedOffset
== UNKNOWN_OFFSET
);
1123 if (parsedPos
< maxPos
&&
1124 ((evaluated
& ISO_Z_STYLE_FLAG
) == 0 || (evaluated
& ISO_LOCAL_STYLE_FLAG
) == 0)) {
1125 tmpPos
.setIndex(startIdx
);
1126 tmpPos
.setErrorIndex(-1);
1128 UBool hasDigitOffset
= FALSE
;
1129 offset
= parseOffsetISO8601(text
, tmpPos
, FALSE
, &hasDigitOffset
);
1130 if (tmpPos
.getErrorIndex() == -1) {
1131 if (tmpPos
.getIndex() == maxPos
|| hasDigitOffset
) {
1132 pos
.setIndex(tmpPos
.getIndex());
1133 return createTimeZoneForOffset(offset
);
1135 // Note: When ISO 8601 format contains offset digits, it should not
1136 // collide with other formats. However, ISO 8601 UTC format "Z" (single letter)
1137 // may collide with other names. In this case, we need to evaluate other names.
1138 if (parsedPos
< tmpPos
.getIndex()) {
1139 parsedOffset
= offset
;
1140 parsedID
.setToBogus();
1141 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1142 parsedPos
= tmpPos
.getIndex();
1143 U_ASSERT(parsedPos
== startIdx
+ 1); // only when "Z" is used
1148 // Localized GMT format
1149 if (parsedPos
< maxPos
&&
1150 (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT
]) == 0) {
1151 tmpPos
.setIndex(startIdx
);
1152 tmpPos
.setErrorIndex(-1);
1154 UBool hasDigitOffset
= FALSE
;
1155 offset
= parseOffsetLocalizedGMT(text
, tmpPos
, FALSE
, &hasDigitOffset
);
1156 if (tmpPos
.getErrorIndex() == -1) {
1157 if (tmpPos
.getIndex() == maxPos
|| hasDigitOffset
) {
1158 pos
.setIndex(tmpPos
.getIndex());
1159 return createTimeZoneForOffset(offset
);
1161 // Evaluate other names - see the comment earlier in this method.
1162 if (parsedPos
< tmpPos
.getIndex()) {
1163 parsedOffset
= offset
;
1164 parsedID
.setToBogus();
1165 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1166 parsedPos
= tmpPos
.getIndex();
1171 if (parsedPos
< maxPos
&&
1172 (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT_SHORT
]) == 0) {
1173 tmpPos
.setIndex(startIdx
);
1174 tmpPos
.setErrorIndex(-1);
1176 UBool hasDigitOffset
= FALSE
;
1177 offset
= parseOffsetLocalizedGMT(text
, tmpPos
, TRUE
, &hasDigitOffset
);
1178 if (tmpPos
.getErrorIndex() == -1) {
1179 if (tmpPos
.getIndex() == maxPos
|| hasDigitOffset
) {
1180 pos
.setIndex(tmpPos
.getIndex());
1181 return createTimeZoneForOffset(offset
);
1183 // Evaluate other names - see the comment earlier in this method.
1184 if (parsedPos
< tmpPos
.getIndex()) {
1185 parsedOffset
= offset
;
1186 parsedID
.setToBogus();
1187 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1188 parsedPos
= tmpPos
.getIndex();
1193 // When ParseOption.ALL_STYLES is available, we also try to look all possible display names and IDs.
1194 // For example, when style is GENERIC_LONG, "EST" (SPECIFIC_SHORT) is never
1195 // used for America/New_York. With parseAllStyles true, this code parses "EST"
1196 // as America/New_York.
1198 // Note: Adding all possible names into the trie used by the implementation is quite heavy operation,
1199 // which we want to avoid normally (note that we cache the trie, so this is applicable to the
1200 // first time only as long as the cache does not expire).
1202 if (parseOptions
& UTZFMT_PARSE_OPTION_ALL_STYLES
) {
1203 // Try all specific names and exemplar location names
1204 if (parsedPos
< maxPos
) {
1205 LocalPointer
<TimeZoneNames::MatchInfoCollection
> specificMatches(fTimeZoneNames
->find(text
, startIdx
, ALL_SIMPLE_NAME_TYPES
, status
));
1206 if (U_FAILURE(status
)) {
1207 pos
.setErrorIndex(startIdx
);
1210 int32_t specificMatchIdx
= -1;
1211 int32_t matchPos
= -1;
1212 if (!specificMatches
.isNull()) {
1213 for (int32_t i
= 0; i
< specificMatches
->size(); i
++) {
1214 if (startIdx
+ specificMatches
->getMatchLengthAt(i
) > matchPos
) {
1215 specificMatchIdx
= i
;
1216 matchPos
= startIdx
+ specificMatches
->getMatchLengthAt(i
);
1220 if (parsedPos
< matchPos
) {
1221 U_ASSERT(specificMatchIdx
>= 0);
1222 parsedPos
= matchPos
;
1223 getTimeZoneID(specificMatches
.getAlias(), specificMatchIdx
, parsedID
);
1224 parsedTimeType
= getTimeType(specificMatches
->getNameTypeAt(specificMatchIdx
));
1225 parsedOffset
= UNKNOWN_OFFSET
;
1228 if (parseTZDBAbbrev
&& parsedPos
< maxPos
&& (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_SPECIFIC_SHORT
]) == 0) {
1229 const TZDBTimeZoneNames
*tzdbTimeZoneNames
= getTZDBTimeZoneNames(status
);
1230 if (U_SUCCESS(status
)) {
1231 LocalPointer
<TimeZoneNames::MatchInfoCollection
> tzdbNameMatches(
1232 tzdbTimeZoneNames
->find(text
, startIdx
, ALL_SIMPLE_NAME_TYPES
, status
));
1233 if (U_FAILURE(status
)) {
1234 pos
.setErrorIndex(startIdx
);
1237 int32_t tzdbNameMatchIdx
= -1;
1238 int32_t matchPos
= -1;
1239 if (!tzdbNameMatches
.isNull()) {
1240 for (int32_t i
= 0; i
< tzdbNameMatches
->size(); i
++) {
1241 if (startIdx
+ tzdbNameMatches
->getMatchLengthAt(i
) > matchPos
) {
1242 tzdbNameMatchIdx
= i
;
1243 matchPos
= startIdx
+ tzdbNameMatches
->getMatchLengthAt(i
);
1247 if (parsedPos
< matchPos
) {
1248 U_ASSERT(tzdbNameMatchIdx
>= 0);
1249 parsedPos
= matchPos
;
1250 getTimeZoneID(tzdbNameMatches
.getAlias(), tzdbNameMatchIdx
, parsedID
);
1251 parsedTimeType
= getTimeType(tzdbNameMatches
->getNameTypeAt(tzdbNameMatchIdx
));
1252 parsedOffset
= UNKNOWN_OFFSET
;
1256 // Try generic names
1257 if (parsedPos
< maxPos
) {
1258 int32_t genMatchLen
= -1;
1259 UTimeZoneFormatTimeType tt
= UTZFMT_TIME_TYPE_UNKNOWN
;
1261 const TimeZoneGenericNames
*gnames
= getTimeZoneGenericNames(status
);
1262 if (U_SUCCESS(status
)) {
1263 genMatchLen
= gnames
->findBestMatch(text
, startIdx
, ALL_GENERIC_NAME_TYPES
, tzID
, tt
, status
);
1265 if (U_FAILURE(status
)) {
1266 pos
.setErrorIndex(startIdx
);
1270 if (genMatchLen
> 0 && parsedPos
< startIdx
+ genMatchLen
) {
1271 parsedPos
= startIdx
+ genMatchLen
;
1272 parsedID
.setTo(tzID
);
1273 parsedTimeType
= tt
;
1274 parsedOffset
= UNKNOWN_OFFSET
;
1279 if (parsedPos
< maxPos
&& (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_ZONE_ID
]) == 0) {
1280 tmpPos
.setIndex(startIdx
);
1281 tmpPos
.setErrorIndex(-1);
1283 parseZoneID(text
, tmpPos
, tzID
);
1284 if (tmpPos
.getErrorIndex() == -1 && parsedPos
< tmpPos
.getIndex()) {
1285 parsedPos
= tmpPos
.getIndex();
1286 parsedID
.setTo(tzID
);
1287 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1288 parsedOffset
= UNKNOWN_OFFSET
;
1291 // Try short time zone ID
1292 if (parsedPos
< maxPos
&& (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_ZONE_ID
]) == 0) {
1293 tmpPos
.setIndex(startIdx
);
1294 tmpPos
.setErrorIndex(-1);
1296 parseShortZoneID(text
, tmpPos
, tzID
);
1297 if (tmpPos
.getErrorIndex() == -1 && parsedPos
< tmpPos
.getIndex()) {
1298 parsedPos
= tmpPos
.getIndex();
1299 parsedID
.setTo(tzID
);
1300 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1301 parsedOffset
= UNKNOWN_OFFSET
;
1306 if (parsedPos
> startIdx
) {
1307 // Parsed successfully
1309 if (parsedID
.length() > 0) {
1310 parsedTZ
= TimeZone::createTimeZone(parsedID
);
1312 U_ASSERT(parsedOffset
!= UNKNOWN_OFFSET
);
1313 parsedTZ
= createTimeZoneForOffset(parsedOffset
);
1316 *timeType
= parsedTimeType
;
1318 pos
.setIndex(parsedPos
);
1322 pos
.setErrorIndex(startIdx
);
1327 TimeZoneFormat::parseObject(const UnicodeString
& source
, Formattable
& result
,
1328 ParsePosition
& parse_pos
) const {
1329 result
.adoptObject(parse(UTZFMT_STYLE_GENERIC_LOCATION
, source
, parse_pos
, UTZFMT_PARSE_OPTION_ALL_STYLES
));
1333 // ------------------------------------------------------------------
1334 // Private zone name format/parse implementation
1337 TimeZoneFormat::formatGeneric(const TimeZone
& tz
, int32_t genType
, UDate date
, UnicodeString
& name
) const {
1338 UErrorCode status
= U_ZERO_ERROR
;
1339 const TimeZoneGenericNames
* gnames
= getTimeZoneGenericNames(status
);
1340 if (U_FAILURE(status
)) {
1345 if (genType
== UTZGNM_LOCATION
) {
1346 const UChar
* canonicalID
= ZoneMeta::getCanonicalCLDRID(tz
);
1347 if (canonicalID
== NULL
) {
1351 return gnames
->getGenericLocationName(UnicodeString(TRUE
, canonicalID
, -1), name
);
1353 return gnames
->getDisplayName(tz
, (UTimeZoneGenericNameType
)genType
, date
, name
);
1357 TimeZoneFormat::formatSpecific(const TimeZone
& tz
, UTimeZoneNameType stdType
, UTimeZoneNameType dstType
,
1358 UDate date
, UnicodeString
& name
, UTimeZoneFormatTimeType
*timeType
) const {
1359 if (fTimeZoneNames
== NULL
) {
1364 UErrorCode status
= U_ZERO_ERROR
;
1365 UBool isDaylight
= tz
.inDaylightTime(date
, status
);
1366 const UChar
* canonicalID
= ZoneMeta::getCanonicalCLDRID(tz
);
1368 if (U_FAILURE(status
) || canonicalID
== NULL
) {
1374 fTimeZoneNames
->getDisplayName(UnicodeString(TRUE
, canonicalID
, -1), dstType
, date
, name
);
1376 fTimeZoneNames
->getDisplayName(UnicodeString(TRUE
, canonicalID
, -1), stdType
, date
, name
);
1379 if (timeType
&& !name
.isEmpty()) {
1380 *timeType
= isDaylight
? UTZFMT_TIME_TYPE_DAYLIGHT
: UTZFMT_TIME_TYPE_STANDARD
;
1385 const TimeZoneGenericNames
*
1386 TimeZoneFormat::getTimeZoneGenericNames(UErrorCode
& status
) const {
1387 if (U_FAILURE(status
)) {
1392 if (fTimeZoneGenericNames
== NULL
) {
1393 TimeZoneFormat
*nonConstThis
= const_cast<TimeZoneFormat
*>(this);
1394 nonConstThis
->fTimeZoneGenericNames
= TimeZoneGenericNames::createInstance(fLocale
, status
);
1396 umtx_unlock(&gLock
);
1398 return fTimeZoneGenericNames
;
1401 const TZDBTimeZoneNames
*
1402 TimeZoneFormat::getTZDBTimeZoneNames(UErrorCode
& status
) const {
1403 if (U_FAILURE(status
)) {
1408 if (fTZDBTimeZoneNames
== NULL
) {
1409 TZDBTimeZoneNames
*tzdbNames
= new TZDBTimeZoneNames(fLocale
);
1410 if (tzdbNames
== NULL
) {
1411 status
= U_MEMORY_ALLOCATION_ERROR
;
1413 TimeZoneFormat
*nonConstThis
= const_cast<TimeZoneFormat
*>(this);
1414 nonConstThis
->fTZDBTimeZoneNames
= tzdbNames
;
1417 umtx_unlock(&gLock
);
1419 return fTZDBTimeZoneNames
;
1423 TimeZoneFormat::formatExemplarLocation(const TimeZone
& tz
, UnicodeString
& name
) const {
1424 UChar locationBuf
[ZONE_NAME_U16_MAX
];
1425 UnicodeString
location(locationBuf
, 0, UPRV_LENGTHOF(locationBuf
));
1426 const UChar
* canonicalID
= ZoneMeta::getCanonicalCLDRID(tz
);
1429 fTimeZoneNames
->getExemplarLocationName(UnicodeString(TRUE
, canonicalID
, -1), location
);
1431 if (location
.length() > 0) {
1432 name
.setTo(location
);
1434 // Use "unknown" location
1435 fTimeZoneNames
->getExemplarLocationName(UnicodeString(TRUE
, UNKNOWN_ZONE_ID
, -1), location
);
1436 if (location
.length() > 0) {
1437 name
.setTo(location
);
1440 name
.setTo(UNKNOWN_LOCATION
, -1);
1447 // ------------------------------------------------------------------
1448 // Zone offset format and parse
1451 TimeZoneFormat::formatOffsetISO8601Basic(int32_t offset
, UBool useUtcIndicator
, UBool isShort
, UBool ignoreSeconds
,
1452 UnicodeString
& result
, UErrorCode
& status
) const {
1453 return formatOffsetISO8601(offset
, TRUE
, useUtcIndicator
, isShort
, ignoreSeconds
, result
, status
);
1457 TimeZoneFormat::formatOffsetISO8601Extended(int32_t offset
, UBool useUtcIndicator
, UBool isShort
, UBool ignoreSeconds
,
1458 UnicodeString
& result
, UErrorCode
& status
) const {
1459 return formatOffsetISO8601(offset
, FALSE
, useUtcIndicator
, isShort
, ignoreSeconds
, result
, status
);
1463 TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset
, UnicodeString
& result
, UErrorCode
& status
) const {
1464 return formatOffsetLocalizedGMT(offset
, FALSE
, result
, status
);
1468 TimeZoneFormat::formatOffsetShortLocalizedGMT(int32_t offset
, UnicodeString
& result
, UErrorCode
& status
) const {
1469 return formatOffsetLocalizedGMT(offset
, TRUE
, result
, status
);
1473 TimeZoneFormat::parseOffsetISO8601(const UnicodeString
& text
, ParsePosition
& pos
) const {
1474 return parseOffsetISO8601(text
, pos
, FALSE
);
1478 TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString
& text
, ParsePosition
& pos
) const {
1479 return parseOffsetLocalizedGMT(text
, pos
, FALSE
, NULL
);
1483 TimeZoneFormat::parseOffsetShortLocalizedGMT(const UnicodeString
& text
, ParsePosition
& pos
) const {
1484 return parseOffsetLocalizedGMT(text
, pos
, TRUE
, NULL
);
1487 // ------------------------------------------------------------------
1488 // Private zone offset format/parse implementation
1491 TimeZoneFormat::formatOffsetISO8601(int32_t offset
, UBool isBasic
, UBool useUtcIndicator
,
1492 UBool isShort
, UBool ignoreSeconds
, UnicodeString
& result
, UErrorCode
& status
) const {
1493 if (U_FAILURE(status
)) {
1494 result
.setToBogus();
1497 int32_t absOffset
= offset
< 0 ? -offset
: offset
;
1498 if (useUtcIndicator
&& (absOffset
< MILLIS_PER_SECOND
|| (ignoreSeconds
&& absOffset
< MILLIS_PER_MINUTE
))) {
1499 result
.setTo(ISO8601_UTC
);
1503 OffsetFields minFields
= isShort
? FIELDS_H
: FIELDS_HM
;
1504 OffsetFields maxFields
= ignoreSeconds
? FIELDS_HM
: FIELDS_HMS
;
1505 UChar sep
= isBasic
? 0 : ISO8601_SEP
;
1507 // Note: FIELDS_HMS as maxFields is a CLDR/ICU extension. ISO 8601 specification does
1508 // not support seconds field.
1510 if (absOffset
>= MAX_OFFSET
) {
1511 result
.setToBogus();
1512 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1517 fields
[0] = absOffset
/ MILLIS_PER_HOUR
;
1518 absOffset
= absOffset
% MILLIS_PER_HOUR
;
1519 fields
[1] = absOffset
/ MILLIS_PER_MINUTE
;
1520 absOffset
= absOffset
% MILLIS_PER_MINUTE
;
1521 fields
[2] = absOffset
/ MILLIS_PER_SECOND
;
1523 U_ASSERT(fields
[0] >= 0 && fields
[0] <= MAX_OFFSET_HOUR
);
1524 U_ASSERT(fields
[1] >= 0 && fields
[1] <= MAX_OFFSET_MINUTE
);
1525 U_ASSERT(fields
[2] >= 0 && fields
[2] <= MAX_OFFSET_SECOND
);
1527 int32_t lastIdx
= maxFields
;
1528 while (lastIdx
> minFields
) {
1529 if (fields
[lastIdx
] != 0) {
1537 // if all output fields are 0s, do not use negative sign
1538 for (int32_t idx
= 0; idx
<= lastIdx
; idx
++) {
1539 if (fields
[idx
] != 0) {
1547 for (int32_t idx
= 0; idx
<= lastIdx
; idx
++) {
1548 if (sep
&& idx
!= 0) {
1551 result
.append((UChar
)(0x0030 + fields
[idx
]/10));
1552 result
.append((UChar
)(0x0030 + fields
[idx
]%10
));
1559 TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset
, UBool isShort
, UnicodeString
& result
, UErrorCode
& status
) const {
1560 if (U_FAILURE(status
)) {
1561 result
.setToBogus();
1564 if (offset
<= -MAX_OFFSET
|| offset
>= MAX_OFFSET
) {
1565 result
.setToBogus();
1566 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1571 result
.setTo(fGMTZeroFormat
);
1575 UBool positive
= TRUE
;
1581 int32_t offsetH
= offset
/ MILLIS_PER_HOUR
;
1582 offset
= offset
% MILLIS_PER_HOUR
;
1583 int32_t offsetM
= offset
/ MILLIS_PER_MINUTE
;
1584 offset
= offset
% MILLIS_PER_MINUTE
;
1585 int32_t offsetS
= offset
/ MILLIS_PER_SECOND
;
1587 U_ASSERT(offsetH
<= MAX_OFFSET_HOUR
&& offsetM
<= MAX_OFFSET_MINUTE
&& offsetS
<= MAX_OFFSET_SECOND
);
1589 const UVector
* offsetPatternItems
= NULL
;
1592 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_POSITIVE_HMS
];
1593 } else if (offsetM
!= 0 || !isShort
) {
1594 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_POSITIVE_HM
];
1596 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_POSITIVE_H
];
1600 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_NEGATIVE_HMS
];
1601 } else if (offsetM
!= 0 || !isShort
) {
1602 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_NEGATIVE_HM
];
1604 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_NEGATIVE_H
];
1608 U_ASSERT(offsetPatternItems
!= NULL
);
1610 // Building the GMT format string
1611 result
.setTo(fGMTPatternPrefix
);
1613 for (int32_t i
= 0; i
< offsetPatternItems
->size(); i
++) {
1614 const GMTOffsetField
* item
= (GMTOffsetField
*)offsetPatternItems
->elementAt(i
);
1615 GMTOffsetField::FieldType type
= item
->getType();
1618 case GMTOffsetField::TEXT
:
1619 result
.append(item
->getPatternText(), -1);
1622 case GMTOffsetField::HOUR
:
1623 appendOffsetDigits(result
, offsetH
, (isShort
? 1 : 2));
1626 case GMTOffsetField::MINUTE
:
1627 appendOffsetDigits(result
, offsetM
, 2);
1630 case GMTOffsetField::SECOND
:
1631 appendOffsetDigits(result
, offsetS
, 2);
1636 result
.append(fGMTPatternSuffix
);
1641 TimeZoneFormat::parseOffsetISO8601(const UnicodeString
& text
, ParsePosition
& pos
, UBool extendedOnly
, UBool
* hasDigitOffset
/* = NULL */) const {
1642 if (hasDigitOffset
) {
1643 *hasDigitOffset
= FALSE
;
1645 int32_t start
= pos
.getIndex();
1646 if (start
>= text
.length()) {
1647 pos
.setErrorIndex(start
);
1651 UChar firstChar
= text
.charAt(start
);
1652 if (firstChar
== ISO8601_UTC
|| firstChar
== (UChar
)(ISO8601_UTC
+ 0x20)) {
1653 // "Z" (or "z") - indicates UTC
1654 pos
.setIndex(start
+ 1);
1659 if (firstChar
== PLUS
) {
1661 } else if (firstChar
== MINUS
) {
1664 // Not an ISO 8601 offset string
1665 pos
.setErrorIndex(start
);
1668 ParsePosition
posOffset(start
+ 1);
1669 int32_t offset
= parseAsciiOffsetFields(text
, posOffset
, ISO8601_SEP
, FIELDS_H
, FIELDS_HMS
);
1670 if (posOffset
.getErrorIndex() == -1 && !extendedOnly
&& (posOffset
.getIndex() - start
<= 3)) {
1671 // If the text is successfully parsed as extended format with the options above, it can be also parsed
1672 // as basic format. For example, "0230" can be parsed as offset 2:00 (only first digits are valid for
1673 // extended format), but it can be parsed as offset 2:30 with basic format. We use longer result.
1674 ParsePosition
posBasic(start
+ 1);
1675 int32_t tmpOffset
= parseAbuttingAsciiOffsetFields(text
, posBasic
, FIELDS_H
, FIELDS_HMS
, FALSE
);
1676 if (posBasic
.getErrorIndex() == -1 && posBasic
.getIndex() > posOffset
.getIndex()) {
1678 posOffset
.setIndex(posBasic
.getIndex());
1682 if (posOffset
.getErrorIndex() != -1) {
1683 pos
.setErrorIndex(start
);
1687 pos
.setIndex(posOffset
.getIndex());
1688 if (hasDigitOffset
) {
1689 *hasDigitOffset
= TRUE
;
1691 return sign
* offset
;
1695 TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString
& text
, ParsePosition
& pos
, UBool isShort
, UBool
* hasDigitOffset
) const {
1696 int32_t start
= pos
.getIndex();
1698 int32_t parsedLength
= 0;
1700 if (hasDigitOffset
) {
1701 *hasDigitOffset
= FALSE
;
1704 offset
= parseOffsetLocalizedGMTPattern(text
, start
, isShort
, parsedLength
);
1706 // For now, parseOffsetLocalizedGMTPattern handles both long and short
1707 // formats, no matter isShort is true or false. This might be changed in future
1708 // when strict parsing is necessary, or different set of patterns are used for
1709 // short/long formats.
1711 if (parsedLength
== 0) {
1712 offset
= parseOffsetLocalizedGMTPattern(text
, start
, !isShort
, parsedLength
);
1716 if (parsedLength
> 0) {
1717 if (hasDigitOffset
) {
1718 *hasDigitOffset
= TRUE
;
1720 pos
.setIndex(start
+ parsedLength
);
1724 // Try the default patterns
1725 offset
= parseOffsetDefaultLocalizedGMT(text
, start
, parsedLength
);
1726 if (parsedLength
> 0) {
1727 if (hasDigitOffset
) {
1728 *hasDigitOffset
= TRUE
;
1730 pos
.setIndex(start
+ parsedLength
);
1734 // Check if this is a GMT zero format
1735 if (text
.caseCompare(start
, fGMTZeroFormat
.length(), fGMTZeroFormat
, 0) == 0) {
1736 pos
.setIndex(start
+ fGMTZeroFormat
.length());
1740 // Check if this is a default GMT zero format
1741 for (int32_t i
= 0; ALT_GMT_STRINGS
[i
][0] != 0; i
++) {
1742 const UChar
* defGMTZero
= ALT_GMT_STRINGS
[i
];
1743 int32_t defGMTZeroLen
= u_strlen(defGMTZero
);
1744 if (text
.caseCompare(start
, defGMTZeroLen
, defGMTZero
, 0) == 0) {
1745 pos
.setIndex(start
+ defGMTZeroLen
);
1751 pos
.setErrorIndex(start
);
1756 TimeZoneFormat::parseOffsetLocalizedGMTPattern(const UnicodeString
& text
, int32_t start
, UBool
/*isShort*/, int32_t& parsedLen
) const {
1757 int32_t idx
= start
;
1759 UBool parsed
= FALSE
;
1763 int32_t len
= fGMTPatternPrefix
.length();
1764 if (len
> 0 && text
.caseCompare(idx
, len
, fGMTPatternPrefix
, 0) != 0) {
1765 // prefix match failed
1771 offset
= parseOffsetFields(text
, idx
, FALSE
, len
);
1773 // offset field match failed
1778 len
= fGMTPatternSuffix
.length();
1779 if (len
> 0 && text
.caseCompare(idx
, len
, fGMTPatternSuffix
, 0) != 0) {
1787 parsedLen
= parsed
? idx
- start
: 0;
1792 TimeZoneFormat::parseOffsetFields(const UnicodeString
& text
, int32_t start
, UBool
/*isShort*/, int32_t& parsedLen
) const {
1799 int32_t offsetH
, offsetM
, offsetS
;
1800 offsetH
= offsetM
= offsetS
= 0;
1802 for (int32_t patidx
= 0; PARSE_GMT_OFFSET_TYPES
[patidx
] >= 0; patidx
++) {
1803 int32_t gmtPatType
= PARSE_GMT_OFFSET_TYPES
[patidx
];
1804 UVector
* items
= fGMTOffsetPatternItems
[gmtPatType
];
1805 U_ASSERT(items
!= NULL
);
1807 outLen
= parseOffsetFieldsWithPattern(text
, start
, items
, FALSE
, offsetH
, offsetM
, offsetS
);
1809 sign
= (gmtPatType
== UTZFMT_PAT_POSITIVE_H
|| gmtPatType
== UTZFMT_PAT_POSITIVE_HM
|| gmtPatType
== UTZFMT_PAT_POSITIVE_HMS
) ?
1815 if (outLen
> 0 && fAbuttingOffsetHoursAndMinutes
) {
1816 // When hours field is sabutting minutes field,
1817 // the parse result above may not be appropriate.
1818 // For example, "01020" is parsed as 01:02: above,
1819 // but it should be parsed as 00:10:20.
1821 int32_t tmpSign
= 1;
1826 for (int32_t patidx
= 0; PARSE_GMT_OFFSET_TYPES
[patidx
] >= 0; patidx
++) {
1827 int32_t gmtPatType
= PARSE_GMT_OFFSET_TYPES
[patidx
];
1828 UVector
* items
= fGMTOffsetPatternItems
[gmtPatType
];
1829 U_ASSERT(items
!= NULL
);
1831 // forcing parse to use single hour digit
1832 tmpLen
= parseOffsetFieldsWithPattern(text
, start
, items
, TRUE
, tmpH
, tmpM
, tmpS
);
1834 tmpSign
= (gmtPatType
== UTZFMT_PAT_POSITIVE_H
|| gmtPatType
== UTZFMT_PAT_POSITIVE_HM
|| gmtPatType
== UTZFMT_PAT_POSITIVE_HMS
) ?
1839 if (tmpLen
> outLen
) {
1840 // Better parse result with single hour digit
1850 offset
= ((((offsetH
* 60) + offsetM
) * 60) + offsetS
) * 1000 * sign
;
1858 TimeZoneFormat::parseOffsetFieldsWithPattern(const UnicodeString
& text
, int32_t start
,
1859 UVector
* patternItems
, UBool forceSingleHourDigit
, int32_t& hour
, int32_t& min
, int32_t& sec
) const {
1860 UBool failed
= FALSE
;
1861 int32_t offsetH
, offsetM
, offsetS
;
1862 offsetH
= offsetM
= offsetS
= 0;
1863 int32_t idx
= start
;
1865 for (int32_t i
= 0; i
< patternItems
->size(); i
++) {
1867 const GMTOffsetField
* field
= (const GMTOffsetField
*)patternItems
->elementAt(i
);
1868 GMTOffsetField::FieldType fieldType
= field
->getType();
1869 if (fieldType
== GMTOffsetField::TEXT
) {
1870 const UChar
* patStr
= field
->getPatternText();
1871 len
= u_strlen(patStr
);
1873 // When TimeZoneFormat parse() is called from SimpleDateFormat,
1874 // leading space characters might be truncated. If the first pattern text
1875 // starts with such character (e.g. Bidi control), then we need to
1876 // skip the leading space charcters.
1877 if (idx
< text
.length() && !PatternProps::isWhiteSpace(text
.char32At(idx
))) {
1881 U16_GET(patStr
, 0, 0, len
, ch
);
1882 if (PatternProps::isWhiteSpace(ch
)) {
1883 chLen
= U16_LENGTH(ch
);
1893 if (text
.caseCompare(idx
, len
, patStr
, 0) != 0) {
1899 if (fieldType
== GMTOffsetField::HOUR
) {
1900 uint8_t maxDigits
= forceSingleHourDigit
? 1 : 2;
1901 offsetH
= parseOffsetFieldWithLocalizedDigits(text
, idx
, 1, maxDigits
, 0, MAX_OFFSET_HOUR
, len
);
1902 } else if (fieldType
== GMTOffsetField::MINUTE
) {
1903 offsetM
= parseOffsetFieldWithLocalizedDigits(text
, idx
, 2, 2, 0, MAX_OFFSET_MINUTE
, len
);
1904 } else if (fieldType
== GMTOffsetField::SECOND
) {
1905 offsetS
= parseOffsetFieldWithLocalizedDigits(text
, idx
, 2, 2, 0, MAX_OFFSET_SECOND
, len
);
1917 hour
= min
= sec
= 0;
1929 TimeZoneFormat::parseAbuttingOffsetFields(const UnicodeString
& text
, int32_t start
, int32_t& parsedLen
) const {
1930 int32_t digits
[MAX_OFFSET_DIGITS
];
1931 int32_t parsed
[MAX_OFFSET_DIGITS
]; // accumulative offsets
1933 // Parse digits into int[]
1934 int32_t idx
= start
;
1936 int32_t numDigits
= 0;
1937 for (int32_t i
= 0; i
< MAX_OFFSET_DIGITS
; i
++) {
1938 digits
[i
] = parseSingleLocalizedDigit(text
, idx
, len
);
1939 if (digits
[i
] < 0) {
1943 parsed
[i
] = idx
- start
;
1947 if (numDigits
== 0) {
1953 while (numDigits
> 0) {
1958 U_ASSERT(numDigits
> 0 && numDigits
<= MAX_OFFSET_DIGITS
);
1959 switch (numDigits
) {
1964 hour
= digits
[0] * 10 + digits
[1];
1968 min
= digits
[1] * 10 + digits
[2];
1971 hour
= digits
[0] * 10 + digits
[1];
1972 min
= digits
[2] * 10 + digits
[3];
1976 min
= digits
[1] * 10 + digits
[2];
1977 sec
= digits
[3] * 10 + digits
[4];
1980 hour
= digits
[0] * 10 + digits
[1];
1981 min
= digits
[2] * 10 + digits
[3];
1982 sec
= digits
[4] * 10 + digits
[5];
1985 if (hour
<= MAX_OFFSET_HOUR
&& min
<= MAX_OFFSET_MINUTE
&& sec
<= MAX_OFFSET_SECOND
) {
1986 // found a valid combination
1987 offset
= hour
* MILLIS_PER_HOUR
+ min
* MILLIS_PER_MINUTE
+ sec
* MILLIS_PER_SECOND
;
1988 parsedLen
= parsed
[numDigits
- 1];
1997 TimeZoneFormat::parseOffsetDefaultLocalizedGMT(const UnicodeString
& text
, int start
, int32_t& parsedLen
) const {
1998 int32_t idx
= start
;
2003 // check global default GMT alternatives
2006 for (int32_t i
= 0; ALT_GMT_STRINGS
[i
][0] != 0; i
++) {
2007 const UChar
* gmt
= ALT_GMT_STRINGS
[i
];
2008 int32_t len
= u_strlen(gmt
);
2009 if (text
.caseCompare(start
, len
, gmt
, 0) == 0) {
2019 // offset needs a sign char and a digit at minimum
2020 if (idx
+ 1 >= text
.length()) {
2026 UChar c
= text
.charAt(idx
);
2029 } else if (c
== MINUS
) {
2037 // try the default pattern with the separator first
2038 int32_t lenWithSep
= 0;
2039 int32_t offsetWithSep
= parseDefaultOffsetFields(text
, idx
, DEFAULT_GMT_OFFSET_SEP
, lenWithSep
);
2040 if (lenWithSep
== text
.length() - idx
) {
2042 offset
= offsetWithSep
* sign
;
2045 // try abutting field pattern
2046 int32_t lenAbut
= 0;
2047 int32_t offsetAbut
= parseAbuttingOffsetFields(text
, idx
, lenAbut
);
2049 if (lenWithSep
> lenAbut
) {
2050 offset
= offsetWithSep
* sign
;
2053 offset
= offsetAbut
* sign
;
2057 parsed
= idx
- start
;
2065 TimeZoneFormat::parseDefaultOffsetFields(const UnicodeString
& text
, int32_t start
, UChar separator
, int32_t& parsedLen
) const {
2066 int32_t max
= text
.length();
2067 int32_t idx
= start
;
2069 int32_t hour
= 0, min
= 0, sec
= 0;
2074 hour
= parseOffsetFieldWithLocalizedDigits(text
, idx
, 1, 2, 0, MAX_OFFSET_HOUR
, len
);
2080 if (idx
+ 1 < max
&& text
.charAt(idx
) == separator
) {
2081 min
= parseOffsetFieldWithLocalizedDigits(text
, idx
+ 1, 2, 2, 0, MAX_OFFSET_MINUTE
, len
);
2087 if (idx
+ 1 < max
&& text
.charAt(idx
) == separator
) {
2088 sec
= parseOffsetFieldWithLocalizedDigits(text
, idx
+ 1, 2, 2, 0, MAX_OFFSET_SECOND
, len
);
2101 parsedLen
= idx
- start
;
2102 return hour
* MILLIS_PER_HOUR
+ min
* MILLIS_PER_MINUTE
+ sec
* MILLIS_PER_SECOND
;
2106 TimeZoneFormat::parseOffsetFieldWithLocalizedDigits(const UnicodeString
& text
, int32_t start
, uint8_t minDigits
, uint8_t maxDigits
, uint16_t minVal
, uint16_t maxVal
, int32_t& parsedLen
) const {
2110 int32_t numDigits
= 0;
2111 int32_t idx
= start
;
2112 int32_t digitLen
= 0;
2114 while (idx
< text
.length() && numDigits
< maxDigits
) {
2115 int32_t digit
= parseSingleLocalizedDigit(text
, idx
, digitLen
);
2119 int32_t tmpVal
= decVal
* 10 + digit
;
2120 if (tmpVal
> maxVal
) {
2128 // Note: maxVal is checked in the while loop
2129 if (numDigits
< minDigits
|| decVal
< minVal
) {
2133 parsedLen
= idx
- start
;
2140 TimeZoneFormat::parseSingleLocalizedDigit(const UnicodeString
& text
, int32_t start
, int32_t& len
) const {
2143 if (start
< text
.length()) {
2144 UChar32 cp
= text
.char32At(start
);
2146 // First, try digits configured for this instance
2147 for (int32_t i
= 0; i
< 10; i
++) {
2148 if (cp
== fGMTOffsetDigits
[i
]) {
2153 // If failed, check if this is a Unicode digit
2155 int32_t tmp
= u_charDigitValue(cp
);
2156 digit
= (tmp
>= 0 && tmp
<= 9) ? tmp
: -1;
2160 int32_t next
= text
.moveIndex32(start
, 1);
2168 TimeZoneFormat::formatOffsetWithAsciiDigits(int32_t offset
, UChar sep
, OffsetFields minFields
, OffsetFields maxFields
, UnicodeString
& result
) {
2169 U_ASSERT(maxFields
>= minFields
);
2170 U_ASSERT(offset
> -MAX_OFFSET
&& offset
< MAX_OFFSET
);
2180 fields
[0] = offset
/ MILLIS_PER_HOUR
;
2181 offset
= offset
% MILLIS_PER_HOUR
;
2182 fields
[1] = offset
/ MILLIS_PER_MINUTE
;
2183 offset
= offset
% MILLIS_PER_MINUTE
;
2184 fields
[2] = offset
/ MILLIS_PER_SECOND
;
2186 U_ASSERT(fields
[0] >= 0 && fields
[0] <= MAX_OFFSET_HOUR
);
2187 U_ASSERT(fields
[1] >= 0 && fields
[1] <= MAX_OFFSET_MINUTE
);
2188 U_ASSERT(fields
[2] >= 0 && fields
[2] <= MAX_OFFSET_SECOND
);
2190 int32_t lastIdx
= maxFields
;
2191 while (lastIdx
> minFields
) {
2192 if (fields
[lastIdx
] != 0) {
2198 for (int32_t idx
= 0; idx
<= lastIdx
; idx
++) {
2199 if (sep
&& idx
!= 0) {
2202 result
.append((UChar
)(0x0030 + fields
[idx
]/10));
2203 result
.append((UChar
)(0x0030 + fields
[idx
]%10
));
2210 TimeZoneFormat::parseAbuttingAsciiOffsetFields(const UnicodeString
& text
, ParsePosition
& pos
, OffsetFields minFields
, OffsetFields maxFields
, UBool fixedHourWidth
) {
2211 int32_t start
= pos
.getIndex();
2213 int32_t minDigits
= 2 * (minFields
+ 1) - (fixedHourWidth
? 0 : 1);
2214 int32_t maxDigits
= 2 * (maxFields
+ 1);
2216 U_ASSERT(maxDigits
<= MAX_OFFSET_DIGITS
);
2218 int32_t digits
[MAX_OFFSET_DIGITS
] = {};
2219 int32_t numDigits
= 0;
2220 int32_t idx
= start
;
2221 while (numDigits
< maxDigits
&& idx
< text
.length()) {
2222 UChar uch
= text
.charAt(idx
);
2223 int32_t digit
= DIGIT_VAL(uch
);
2227 digits
[numDigits
] = digit
;
2232 if (fixedHourWidth
&& (numDigits
& 1)) {
2233 // Fixed digits, so the number of digits must be even number. Truncating.
2237 if (numDigits
< minDigits
) {
2238 pos
.setErrorIndex(start
);
2242 int32_t hour
= 0, min
= 0, sec
= 0;
2243 UBool bParsed
= FALSE
;
2244 while (numDigits
>= minDigits
) {
2245 switch (numDigits
) {
2250 hour
= digits
[0] * 10 + digits
[1];
2254 min
= digits
[1] * 10 + digits
[2];
2257 hour
= digits
[0] * 10 + digits
[1];
2258 min
= digits
[2] * 10 + digits
[3];
2262 min
= digits
[1] * 10 + digits
[2];
2263 sec
= digits
[3] * 10 + digits
[4];
2266 hour
= digits
[0] * 10 + digits
[1];
2267 min
= digits
[2] * 10 + digits
[3];
2268 sec
= digits
[4] * 10 + digits
[5];
2272 if (hour
<= MAX_OFFSET_HOUR
&& min
<= MAX_OFFSET_MINUTE
&& sec
<= MAX_OFFSET_SECOND
) {
2273 // Successfully parsed
2279 numDigits
-= (fixedHourWidth
? 2 : 1);
2280 hour
= min
= sec
= 0;
2284 pos
.setErrorIndex(start
);
2287 pos
.setIndex(start
+ numDigits
);
2288 return ((((hour
* 60) + min
) * 60) + sec
) * 1000;
2292 TimeZoneFormat::parseAsciiOffsetFields(const UnicodeString
& text
, ParsePosition
& pos
, UChar sep
, OffsetFields minFields
, OffsetFields maxFields
) {
2293 int32_t start
= pos
.getIndex();
2294 int32_t fieldVal
[] = {0, 0, 0};
2295 int32_t fieldLen
[] = {0, -1, -1};
2296 for (int32_t idx
= start
, fieldIdx
= 0; idx
< text
.length() && fieldIdx
<= maxFields
; idx
++) {
2297 UChar c
= text
.charAt(idx
);
2299 if (fieldIdx
== 0) {
2300 if (fieldLen
[0] == 0) {
2304 // 1 digit hour, move to next field
2306 if (fieldLen
[fieldIdx
] != -1) {
2307 // premature minute or seconds field
2310 fieldLen
[fieldIdx
] = 0;
2313 } else if (fieldLen
[fieldIdx
] == -1) {
2314 // no separator after 2 digit field
2317 int32_t digit
= DIGIT_VAL(c
);
2322 fieldVal
[fieldIdx
] = fieldVal
[fieldIdx
] * 10 + digit
;
2323 fieldLen
[fieldIdx
]++;
2324 if (fieldLen
[fieldIdx
] >= 2) {
2325 // parsed 2 digits, move to next field
2331 int32_t parsedLen
= 0;
2332 int32_t parsedFields
= -1;
2335 if (fieldLen
[0] == 0) {
2338 if (fieldVal
[0] > MAX_OFFSET_HOUR
) {
2339 offset
= (fieldVal
[0] / 10) * MILLIS_PER_HOUR
;
2340 parsedFields
= FIELDS_H
;
2344 offset
= fieldVal
[0] * MILLIS_PER_HOUR
;
2345 parsedLen
= fieldLen
[0];
2346 parsedFields
= FIELDS_H
;
2349 if (fieldLen
[1] != 2 || fieldVal
[1] > MAX_OFFSET_MINUTE
) {
2352 offset
+= fieldVal
[1] * MILLIS_PER_MINUTE
;
2353 parsedLen
+= (1 + fieldLen
[1]);
2354 parsedFields
= FIELDS_HM
;
2357 if (fieldLen
[2] != 2 || fieldVal
[2] > MAX_OFFSET_SECOND
) {
2360 offset
+= fieldVal
[2] * MILLIS_PER_SECOND
;
2361 parsedLen
+= (1 + fieldLen
[2]);
2362 parsedFields
= FIELDS_HMS
;
2365 if (parsedFields
< minFields
) {
2366 pos
.setErrorIndex(start
);
2370 pos
.setIndex(start
+ parsedLen
);
2375 TimeZoneFormat::appendOffsetDigits(UnicodeString
& buf
, int32_t n
, uint8_t minDigits
) const {
2376 U_ASSERT(n
>= 0 && n
< 60);
2377 int32_t numDigits
= n
>= 10 ? 2 : 1;
2378 for (int32_t i
= 0; i
< minDigits
- numDigits
; i
++) {
2379 buf
.append(fGMTOffsetDigits
[0]);
2381 if (numDigits
== 2) {
2382 buf
.append(fGMTOffsetDigits
[n
/ 10]);
2384 buf
.append(fGMTOffsetDigits
[n
% 10]);
2387 // ------------------------------------------------------------------
2390 TimeZoneFormat::initGMTPattern(const UnicodeString
& gmtPattern
, UErrorCode
& status
) {
2391 if (U_FAILURE(status
)) {
2394 // This implementation not perfect, but sufficient practically.
2395 int32_t idx
= gmtPattern
.indexOf(ARG0
, ARG0_LEN
, 0);
2397 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2400 fGMTPattern
.setTo(gmtPattern
);
2401 unquote(gmtPattern
.tempSubString(0, idx
), fGMTPatternPrefix
);
2402 unquote(gmtPattern
.tempSubString(idx
+ ARG0_LEN
), fGMTPatternSuffix
);
2406 TimeZoneFormat::unquote(const UnicodeString
& pattern
, UnicodeString
& result
) {
2407 if (pattern
.indexOf(SINGLEQUOTE
) < 0) {
2408 result
.setTo(pattern
);
2412 UBool isPrevQuote
= FALSE
;
2413 UBool inQuote
= FALSE
;
2414 for (int32_t i
= 0; i
< pattern
.length(); i
++) {
2415 UChar c
= pattern
.charAt(i
);
2416 if (c
== SINGLEQUOTE
) {
2419 isPrevQuote
= FALSE
;
2425 isPrevQuote
= FALSE
;
2433 TimeZoneFormat::parseOffsetPattern(const UnicodeString
& pattern
, OffsetFields required
, UErrorCode
& status
) {
2434 if (U_FAILURE(status
)) {
2437 UVector
* result
= new UVector(deleteGMTOffsetField
, NULL
, status
);
2438 if (result
== NULL
) {
2439 status
= U_MEMORY_ALLOCATION_ERROR
;
2443 int32_t checkBits
= 0;
2444 UBool isPrevQuote
= FALSE
;
2445 UBool inQuote
= FALSE
;
2447 UnicodeString
text(textBuf
, 0, UPRV_LENGTHOF(textBuf
));
2448 GMTOffsetField::FieldType itemType
= GMTOffsetField::TEXT
;
2449 int32_t itemLength
= 1;
2451 for (int32_t i
= 0; i
< pattern
.length(); i
++) {
2452 UChar ch
= pattern
.charAt(i
);
2453 if (ch
== SINGLEQUOTE
) {
2455 text
.append(SINGLEQUOTE
);
2456 isPrevQuote
= FALSE
;
2459 if (itemType
!= GMTOffsetField::TEXT
) {
2460 if (GMTOffsetField::isValid(itemType
, itemLength
)) {
2461 GMTOffsetField
* fld
= GMTOffsetField::createTimeField(itemType
, static_cast<uint8_t>(itemLength
), status
);
2462 result
->addElement(fld
, status
);
2463 if (U_FAILURE(status
)) {
2467 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2470 itemType
= GMTOffsetField::TEXT
;
2475 isPrevQuote
= FALSE
;
2479 GMTOffsetField::FieldType tmpType
= GMTOffsetField::getTypeByLetter(ch
);
2480 if (tmpType
!= GMTOffsetField::TEXT
) {
2481 // an offset time pattern character
2482 if (tmpType
== itemType
) {
2485 if (itemType
== GMTOffsetField::TEXT
) {
2486 if (text
.length() > 0) {
2487 GMTOffsetField
* textfld
= GMTOffsetField::createText(text
, status
);
2488 result
->addElement(textfld
, status
);
2489 if (U_FAILURE(status
)) {
2495 if (GMTOffsetField::isValid(itemType
, itemLength
)) {
2496 GMTOffsetField
* fld
= GMTOffsetField::createTimeField(itemType
, static_cast<uint8_t>(itemLength
), status
);
2497 result
->addElement(fld
, status
);
2498 if (U_FAILURE(status
)) {
2502 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2508 checkBits
|= tmpType
;
2512 if (itemType
!= GMTOffsetField::TEXT
) {
2513 if (GMTOffsetField::isValid(itemType
, itemLength
)) {
2514 GMTOffsetField
* fld
= GMTOffsetField::createTimeField(itemType
, static_cast<uint8_t>(itemLength
), status
);
2515 result
->addElement(fld
, status
);
2516 if (U_FAILURE(status
)) {
2520 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2523 itemType
= GMTOffsetField::TEXT
;
2531 if (U_SUCCESS(status
)) {
2532 if (itemType
== GMTOffsetField::TEXT
) {
2533 if (text
.length() > 0) {
2534 GMTOffsetField
* tfld
= GMTOffsetField::createText(text
, status
);
2535 result
->addElement(tfld
, status
);
2538 if (GMTOffsetField::isValid(itemType
, itemLength
)) {
2539 GMTOffsetField
* fld
= GMTOffsetField::createTimeField(itemType
, static_cast<uint8_t>(itemLength
), status
);
2540 result
->addElement(fld
, status
);
2542 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2546 // Check all required fields are set
2547 if (U_SUCCESS(status
)) {
2548 int32_t reqBits
= 0;
2551 reqBits
= GMTOffsetField::HOUR
;
2554 reqBits
= GMTOffsetField::HOUR
| GMTOffsetField::MINUTE
;
2557 reqBits
= GMTOffsetField::HOUR
| GMTOffsetField::MINUTE
| GMTOffsetField::SECOND
;
2560 if (checkBits
== reqBits
) {
2561 // all required fields are set, no extra fields
2573 TimeZoneFormat::expandOffsetPattern(const UnicodeString
& offsetHM
, UnicodeString
& result
, UErrorCode
& status
) {
2574 result
.setToBogus();
2575 if (U_FAILURE(status
)) {
2578 U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN
) == 2);
2580 int32_t idx_mm
= offsetHM
.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN
, 2, 0);
2582 // Bad time zone hour pattern data
2583 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2588 int32_t idx_H
= offsetHM
.tempSubString(0, idx_mm
).lastIndexOf((UChar
)0x0048 /* H */);
2590 sep
= offsetHM
.tempSubString(idx_H
+ 1, idx_mm
- (idx_H
+ 1));
2592 result
.setTo(offsetHM
.tempSubString(0, idx_mm
+ 2));
2594 result
.append(DEFAULT_GMT_OFFSET_SECOND_PATTERN
, -1);
2595 result
.append(offsetHM
.tempSubString(idx_mm
+ 2));
2600 TimeZoneFormat::truncateOffsetPattern(const UnicodeString
& offsetHM
, UnicodeString
& result
, UErrorCode
& status
) {
2601 result
.setToBogus();
2602 if (U_FAILURE(status
)) {
2605 U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN
) == 2);
2607 int32_t idx_mm
= offsetHM
.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN
, 2, 0);
2609 // Bad time zone hour pattern data
2610 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2613 UChar HH
[] = {0x0048, 0x0048};
2614 int32_t idx_HH
= offsetHM
.tempSubString(0, idx_mm
).lastIndexOf(HH
, 2, 0);
2616 return result
.setTo(offsetHM
.tempSubString(0, idx_HH
+ 2));
2618 int32_t idx_H
= offsetHM
.tempSubString(0, idx_mm
).lastIndexOf((UChar
)0x0048, 0);
2620 return result
.setTo(offsetHM
.tempSubString(0, idx_H
+ 1));
2622 // Bad time zone hour pattern data
2623 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2628 TimeZoneFormat::initGMTOffsetPatterns(UErrorCode
& status
) {
2629 for (int32_t type
= 0; type
< UTZFMT_PAT_COUNT
; type
++) {
2631 case UTZFMT_PAT_POSITIVE_H
:
2632 case UTZFMT_PAT_NEGATIVE_H
:
2633 fGMTOffsetPatternItems
[type
] = parseOffsetPattern(fGMTOffsetPatterns
[type
], FIELDS_H
, status
);
2635 case UTZFMT_PAT_POSITIVE_HM
:
2636 case UTZFMT_PAT_NEGATIVE_HM
:
2637 fGMTOffsetPatternItems
[type
] = parseOffsetPattern(fGMTOffsetPatterns
[type
], FIELDS_HM
, status
);
2639 case UTZFMT_PAT_POSITIVE_HMS
:
2640 case UTZFMT_PAT_NEGATIVE_HMS
:
2641 fGMTOffsetPatternItems
[type
] = parseOffsetPattern(fGMTOffsetPatterns
[type
], FIELDS_HMS
, status
);
2645 checkAbuttingHoursAndMinutes();
2649 TimeZoneFormat::checkAbuttingHoursAndMinutes() {
2650 fAbuttingOffsetHoursAndMinutes
= FALSE
;
2651 for (int32_t type
= 0; type
< UTZFMT_PAT_COUNT
; type
++) {
2652 UBool afterH
= FALSE
;
2653 UVector
*items
= fGMTOffsetPatternItems
[type
];
2654 for (int32_t i
= 0; i
< items
->size(); i
++) {
2655 const GMTOffsetField
* item
= (GMTOffsetField
*)items
->elementAt(i
);
2656 GMTOffsetField::FieldType fieldType
= item
->getType();
2657 if (fieldType
!= GMTOffsetField::TEXT
) {
2659 fAbuttingOffsetHoursAndMinutes
= TRUE
;
2661 } else if (fieldType
== GMTOffsetField::HOUR
) {
2664 } else if (afterH
) {
2668 if (fAbuttingOffsetHoursAndMinutes
) {
2675 TimeZoneFormat::toCodePoints(const UnicodeString
& str
, UChar32
* codeArray
, int32_t size
) {
2676 int32_t count
= str
.countChar32();
2677 if (count
!= size
) {
2681 for (int32_t idx
= 0, start
= 0; idx
< size
; idx
++) {
2682 codeArray
[idx
] = str
.char32At(start
);
2683 start
= str
.moveIndex32(start
, 1);
2690 TimeZoneFormat::createTimeZoneForOffset(int32_t offset
) const {
2692 // when offset is 0, we should use "Etc/GMT"
2693 return TimeZone::createTimeZone(UnicodeString(TRUE
, TZID_GMT
, -1));
2695 return ZoneMeta::createCustomTimeZone(offset
);
2698 UTimeZoneFormatTimeType
2699 TimeZoneFormat::getTimeType(UTimeZoneNameType nameType
) {
2701 case UTZNM_LONG_STANDARD
:
2702 case UTZNM_SHORT_STANDARD
:
2703 return UTZFMT_TIME_TYPE_STANDARD
;
2705 case UTZNM_LONG_DAYLIGHT
:
2706 case UTZNM_SHORT_DAYLIGHT
:
2707 return UTZFMT_TIME_TYPE_DAYLIGHT
;
2710 return UTZFMT_TIME_TYPE_UNKNOWN
;
2715 TimeZoneFormat::getTimeZoneID(const TimeZoneNames::MatchInfoCollection
* matches
, int32_t idx
, UnicodeString
& tzID
) const {
2716 if (!matches
->getTimeZoneIDAt(idx
, tzID
)) {
2718 UnicodeString
mzID(mzIDBuf
, 0, UPRV_LENGTHOF(mzIDBuf
));
2719 if (matches
->getMetaZoneIDAt(idx
, mzID
)) {
2720 fTimeZoneNames
->getReferenceZoneID(mzID
, fTargetRegion
, tzID
);
2727 class ZoneIdMatchHandler
: public TextTrieMapSearchResultHandler
{
2729 ZoneIdMatchHandler();
2730 virtual ~ZoneIdMatchHandler();
2732 UBool
handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
);
2733 const UChar
* getID();
2734 int32_t getMatchLen();
2740 ZoneIdMatchHandler::ZoneIdMatchHandler()
2741 : fLen(0), fID(NULL
) {
2744 ZoneIdMatchHandler::~ZoneIdMatchHandler() {
2748 ZoneIdMatchHandler::handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
) {
2749 if (U_FAILURE(status
)) {
2752 if (node
->hasValues()) {
2753 const UChar
* id
= (const UChar
*)node
->getValue(0);
2755 if (fLen
< matchLength
) {
2765 ZoneIdMatchHandler::getID() {
2770 ZoneIdMatchHandler::getMatchLen() {
2775 static void U_CALLCONV
initZoneIdTrie(UErrorCode
&status
) {
2776 U_ASSERT(gZoneIdTrie
== NULL
);
2777 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEFORMAT
, tzfmt_cleanup
);
2778 gZoneIdTrie
= new TextTrieMap(TRUE
, NULL
); // No deleter, because values are pooled by ZoneMeta
2779 if (gZoneIdTrie
== NULL
) {
2780 status
= U_MEMORY_ALLOCATION_ERROR
;
2783 StringEnumeration
*tzenum
= TimeZone::createEnumeration();
2784 const UnicodeString
*id
;
2785 while ((id
= tzenum
->snext(status
)) != NULL
) {
2786 const UChar
* uid
= ZoneMeta::findTimeZoneID(*id
);
2788 gZoneIdTrie
->put(uid
, const_cast<UChar
*>(uid
), status
);
2796 TimeZoneFormat::parseZoneID(const UnicodeString
& text
, ParsePosition
& pos
, UnicodeString
& tzID
) const {
2797 UErrorCode status
= U_ZERO_ERROR
;
2798 umtx_initOnce(gZoneIdTrieInitOnce
, &initZoneIdTrie
, status
);
2800 int32_t start
= pos
.getIndex();
2804 if (U_SUCCESS(status
)) {
2805 LocalPointer
<ZoneIdMatchHandler
> handler(new ZoneIdMatchHandler());
2806 gZoneIdTrie
->search(text
, start
, handler
.getAlias(), status
);
2807 len
= handler
->getMatchLen();
2809 tzID
.setTo(handler
->getID(), -1);
2814 pos
.setIndex(start
+ len
);
2816 pos
.setErrorIndex(start
);
2822 static void U_CALLCONV
initShortZoneIdTrie(UErrorCode
&status
) {
2823 U_ASSERT(gShortZoneIdTrie
== NULL
);
2824 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEFORMAT
, tzfmt_cleanup
);
2825 StringEnumeration
*tzenum
= TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL
, NULL
, NULL
, status
);
2826 if (U_SUCCESS(status
)) {
2827 gShortZoneIdTrie
= new TextTrieMap(TRUE
, NULL
); // No deleter, because values are pooled by ZoneMeta
2828 if (gShortZoneIdTrie
== NULL
) {
2829 status
= U_MEMORY_ALLOCATION_ERROR
;
2831 const UnicodeString
*id
;
2832 while ((id
= tzenum
->snext(status
)) != NULL
) {
2833 const UChar
* uID
= ZoneMeta::findTimeZoneID(*id
);
2834 const UChar
* shortID
= ZoneMeta::getShortID(*id
);
2835 if (shortID
&& uID
) {
2836 gShortZoneIdTrie
->put(shortID
, const_cast<UChar
*>(uID
), status
);
2846 TimeZoneFormat::parseShortZoneID(const UnicodeString
& text
, ParsePosition
& pos
, UnicodeString
& tzID
) const {
2847 UErrorCode status
= U_ZERO_ERROR
;
2848 umtx_initOnce(gShortZoneIdTrieInitOnce
, &initShortZoneIdTrie
, status
);
2850 int32_t start
= pos
.getIndex();
2854 if (U_SUCCESS(status
)) {
2855 LocalPointer
<ZoneIdMatchHandler
> handler(new ZoneIdMatchHandler());
2856 gShortZoneIdTrie
->search(text
, start
, handler
.getAlias(), status
);
2857 len
= handler
->getMatchLen();
2859 tzID
.setTo(handler
->getID(), -1);
2864 pos
.setIndex(start
+ len
);
2866 pos
.setErrorIndex(start
);
2874 TimeZoneFormat::parseExemplarLocation(const UnicodeString
& text
, ParsePosition
& pos
, UnicodeString
& tzID
) const {
2875 int32_t startIdx
= pos
.getIndex();
2876 int32_t parsedPos
= -1;
2879 UErrorCode status
= U_ZERO_ERROR
;
2880 LocalPointer
<TimeZoneNames::MatchInfoCollection
> exemplarMatches(fTimeZoneNames
->find(text
, startIdx
, UTZNM_EXEMPLAR_LOCATION
, status
));
2881 if (U_FAILURE(status
)) {
2882 pos
.setErrorIndex(startIdx
);
2885 int32_t matchIdx
= -1;
2886 if (!exemplarMatches
.isNull()) {
2887 for (int32_t i
= 0; i
< exemplarMatches
->size(); i
++) {
2888 if (startIdx
+ exemplarMatches
->getMatchLengthAt(i
) > parsedPos
) {
2890 parsedPos
= startIdx
+ exemplarMatches
->getMatchLengthAt(i
);
2893 if (parsedPos
> 0) {
2894 pos
.setIndex(parsedPos
);
2895 getTimeZoneID(exemplarMatches
.getAlias(), matchIdx
, tzID
);
2899 if (tzID
.length() == 0) {
2900 pos
.setErrorIndex(startIdx
);