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