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