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