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