intl/icu/source/i18n/tzfmt.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial