1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/i18n/tzfmt.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2777 @@ 1.4 +/* 1.5 +******************************************************************************* 1.6 +* Copyright (C) 2011-2013, International Business Machines Corporation and 1.7 +* others. All Rights Reserved. 1.8 +******************************************************************************* 1.9 +*/ 1.10 + 1.11 +#include "unicode/utypes.h" 1.12 + 1.13 +#if !UCONFIG_NO_FORMATTING 1.14 + 1.15 +#include "unicode/calendar.h" 1.16 +#include "unicode/tzfmt.h" 1.17 +#include "unicode/numsys.h" 1.18 +#include "unicode/uchar.h" 1.19 +#include "unicode/udat.h" 1.20 +#include "tzgnames.h" 1.21 +#include "cmemory.h" 1.22 +#include "cstring.h" 1.23 +#include "putilimp.h" 1.24 +#include "uassert.h" 1.25 +#include "ucln_in.h" 1.26 +#include "umutex.h" 1.27 +#include "uresimp.h" 1.28 +#include "ureslocs.h" 1.29 +#include "uvector.h" 1.30 +#include "zonemeta.h" 1.31 +#include "tznames_impl.h" // TextTrieMap 1.32 + 1.33 +U_NAMESPACE_BEGIN 1.34 + 1.35 +// Bit flags used by the parse method. 1.36 +// The order must match UTimeZoneFormatStyle enum. 1.37 +#define ISO_Z_STYLE_FLAG 0x0080 1.38 +#define ISO_LOCAL_STYLE_FLAG 0x0100 1.39 +static const int16_t STYLE_PARSE_FLAGS[] = { 1.40 + 0x0001, // UTZFMT_STYLE_GENERIC_LOCATION, 1.41 + 0x0002, // UTZFMT_STYLE_GENERIC_LONG, 1.42 + 0x0004, // UTZFMT_STYLE_GENERIC_SHORT, 1.43 + 0x0008, // UTZFMT_STYLE_SPECIFIC_LONG, 1.44 + 0x0010, // UTZFMT_STYLE_SPECIFIC_SHORT, 1.45 + 0x0020, // UTZFMT_STYLE_LOCALIZED_GMT, 1.46 + 0x0040, // UTZFMT_STYLE_LOCALIZED_GMT_SHORT, 1.47 + ISO_Z_STYLE_FLAG, // UTZFMT_STYLE_ISO_BASIC_SHORT, 1.48 + ISO_LOCAL_STYLE_FLAG, // UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT, 1.49 + ISO_Z_STYLE_FLAG, // UTZFMT_STYLE_ISO_BASIC_FIXED, 1.50 + ISO_LOCAL_STYLE_FLAG, // UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED, 1.51 + ISO_Z_STYLE_FLAG, // UTZFMT_STYLE_ISO_BASIC_FULL, 1.52 + ISO_LOCAL_STYLE_FLAG, // UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, 1.53 + ISO_Z_STYLE_FLAG, // UTZFMT_STYLE_ISO_EXTENDED_FIXED, 1.54 + ISO_LOCAL_STYLE_FLAG, // UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED, 1.55 + ISO_Z_STYLE_FLAG, // UTZFMT_STYLE_ISO_EXTENDED_FULL, 1.56 + ISO_LOCAL_STYLE_FLAG, // UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL, 1.57 + 0x0200, // UTZFMT_STYLE_ZONE_ID, 1.58 + 0x0400, // UTZFMT_STYLE_ZONE_ID_SHORT, 1.59 + 0x0800 // UTZFMT_STYLE_EXEMPLAR_LOCATION 1.60 +}; 1.61 + 1.62 +static const char gZoneStringsTag[] = "zoneStrings"; 1.63 +static const char gGmtFormatTag[]= "gmtFormat"; 1.64 +static const char gGmtZeroFormatTag[] = "gmtZeroFormat"; 1.65 +static const char gHourFormatTag[]= "hourFormat"; 1.66 + 1.67 +static const UChar TZID_GMT[] = {0x0045, 0x0074, 0x0063, 0x002F, 0x0047, 0x004D, 0x0054, 0}; // Etc/GMT 1.68 +static const UChar UNKNOWN_ZONE_ID[] = { 1.69 + 0x0045, 0x0074, 0x0063, 0x002F, 0x0055, 0x006E, 0x006B, 0x006E, 0x006F, 0x0077, 0x006E, 0}; // Etc/Unknown 1.70 +static const UChar UNKNOWN_SHORT_ZONE_ID[] = {0x0075, 0x006E, 0x006B, 0}; // unk 1.71 +static const UChar UNKNOWN_LOCATION[] = {0x0055, 0x006E, 0x006B, 0x006E, 0x006F, 0x0077, 0x006E, 0}; // Unknown 1.72 + 1.73 +static const UChar DEFAULT_GMT_PATTERN[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0}; // GMT{0} 1.74 +//static const UChar DEFAULT_GMT_ZERO[] = {0x0047, 0x004D, 0x0054, 0}; // GMT 1.75 +static const UChar DEFAULT_GMT_POSITIVE_HM[] = {0x002B, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // +H:mm 1.76 +static const UChar DEFAULT_GMT_POSITIVE_HMS[] = {0x002B, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // +H:mm:ss 1.77 +static const UChar DEFAULT_GMT_NEGATIVE_HM[] = {0x002D, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // -H:mm 1.78 +static const UChar DEFAULT_GMT_NEGATIVE_HMS[] = {0x002D, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // -H:mm:ss 1.79 +static const UChar DEFAULT_GMT_POSITIVE_H[] = {0x002B, 0x0048, 0}; // +H 1.80 +static const UChar DEFAULT_GMT_NEGATIVE_H[] = {0x002D, 0x0048, 0}; // -H 1.81 + 1.82 +static const UChar32 DEFAULT_GMT_DIGITS[] = { 1.83 + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 1.84 + 0x0035, 0x0036, 0x0037, 0x0038, 0x0039 1.85 +}; 1.86 + 1.87 +static const UChar DEFAULT_GMT_OFFSET_SEP = 0x003A; // ':' 1.88 + 1.89 +static const UChar ARG0[] = {0x007B, 0x0030, 0x007D}; // "{0}" 1.90 +static const int32_t ARG0_LEN = 3; 1.91 + 1.92 +static const UChar DEFAULT_GMT_OFFSET_MINUTE_PATTERN[] = {0x006D, 0x006D, 0}; // "mm" 1.93 +static const UChar DEFAULT_GMT_OFFSET_SECOND_PATTERN[] = {0x0073, 0x0073, 0}; // "ss" 1.94 + 1.95 +static const UChar ALT_GMT_STRINGS[][4] = { 1.96 + {0x0047, 0x004D, 0x0054, 0}, // GMT 1.97 + {0x0055, 0x0054, 0x0043, 0}, // UTC 1.98 + {0x0055, 0x0054, 0, 0}, // UT 1.99 + {0, 0, 0, 0} 1.100 +}; 1.101 + 1.102 +// Order of GMT offset pattern parsing, *_HMS must be evaluated first 1.103 +// because *_HM is most likely a substring of *_HMS 1.104 +static const int32_t PARSE_GMT_OFFSET_TYPES[] = { 1.105 + UTZFMT_PAT_POSITIVE_HMS, 1.106 + UTZFMT_PAT_NEGATIVE_HMS, 1.107 + UTZFMT_PAT_POSITIVE_HM, 1.108 + UTZFMT_PAT_NEGATIVE_HM, 1.109 + UTZFMT_PAT_POSITIVE_H, 1.110 + UTZFMT_PAT_NEGATIVE_H, 1.111 + -1 1.112 +}; 1.113 + 1.114 +static const UChar SINGLEQUOTE = 0x0027; 1.115 +static const UChar PLUS = 0x002B; 1.116 +static const UChar MINUS = 0x002D; 1.117 +static const UChar ISO8601_UTC = 0x005A; // 'Z' 1.118 +static const UChar ISO8601_SEP = 0x003A; // ':' 1.119 + 1.120 +static const int32_t MILLIS_PER_HOUR = 60 * 60 * 1000; 1.121 +static const int32_t MILLIS_PER_MINUTE = 60 * 1000; 1.122 +static const int32_t MILLIS_PER_SECOND = 1000; 1.123 + 1.124 +// Maximum offset (exclusive) in millisecond supported by offset formats 1.125 +static int32_t MAX_OFFSET = 24 * MILLIS_PER_HOUR; 1.126 + 1.127 +// Maximum values for GMT offset fields 1.128 +static const int32_t MAX_OFFSET_HOUR = 23; 1.129 +static const int32_t MAX_OFFSET_MINUTE = 59; 1.130 +static const int32_t MAX_OFFSET_SECOND = 59; 1.131 + 1.132 +static const int32_t UNKNOWN_OFFSET = 0x7FFFFFFF; 1.133 + 1.134 +static const int32_t ALL_SIMPLE_NAME_TYPES = UTZNM_LONG_STANDARD | UTZNM_LONG_DAYLIGHT | UTZNM_SHORT_STANDARD | UTZNM_SHORT_DAYLIGHT | UTZNM_EXEMPLAR_LOCATION; 1.135 +static const int32_t ALL_GENERIC_NAME_TYPES = UTZGNM_LOCATION | UTZGNM_LONG | UTZGNM_SHORT; 1.136 + 1.137 +#define DIGIT_VAL(c) (0x0030 <= (c) && (c) <= 0x0039 ? (c) - 0x0030 : -1) 1.138 +#define MAX_OFFSET_DIGITS 6 1.139 + 1.140 +// Time Zone ID/Short ID trie 1.141 +static TextTrieMap *gZoneIdTrie = NULL; 1.142 +static icu::UInitOnce gZoneIdTrieInitOnce = U_INITONCE_INITIALIZER; 1.143 + 1.144 +static TextTrieMap *gShortZoneIdTrie = NULL; 1.145 +static icu::UInitOnce gShortZoneIdTrieInitOnce = U_INITONCE_INITIALIZER; 1.146 + 1.147 +static UMutex gLock = U_MUTEX_INITIALIZER; 1.148 + 1.149 +U_CDECL_BEGIN 1.150 +/** 1.151 + * Cleanup callback func 1.152 + */ 1.153 +static UBool U_CALLCONV tzfmt_cleanup(void) 1.154 +{ 1.155 + if (gZoneIdTrie != NULL) { 1.156 + delete gZoneIdTrie; 1.157 + } 1.158 + gZoneIdTrie = NULL; 1.159 + gZoneIdTrieInitOnce.reset(); 1.160 + 1.161 + if (gShortZoneIdTrie != NULL) { 1.162 + delete gShortZoneIdTrie; 1.163 + } 1.164 + gShortZoneIdTrie = NULL; 1.165 + gShortZoneIdTrieInitOnce.reset(); 1.166 + 1.167 + return TRUE; 1.168 +} 1.169 +U_CDECL_END 1.170 + 1.171 +// ------------------------------------------------------------------ 1.172 +// GMTOffsetField 1.173 +// 1.174 +// This class represents a localized GMT offset pattern 1.175 +// item and used by TimeZoneFormat 1.176 +// ------------------------------------------------------------------ 1.177 +class GMTOffsetField : public UMemory { 1.178 +public: 1.179 + enum FieldType { 1.180 + TEXT = 0, 1.181 + HOUR = 1, 1.182 + MINUTE = 2, 1.183 + SECOND = 4 1.184 + }; 1.185 + 1.186 + virtual ~GMTOffsetField(); 1.187 + 1.188 + static GMTOffsetField* createText(const UnicodeString& text, UErrorCode& status); 1.189 + static GMTOffsetField* createTimeField(FieldType type, uint8_t width, UErrorCode& status); 1.190 + static UBool isValid(FieldType type, int32_t width); 1.191 + static FieldType getTypeByLetter(UChar ch); 1.192 + 1.193 + FieldType getType() const; 1.194 + uint8_t getWidth() const; 1.195 + const UChar* getPatternText(void) const; 1.196 + 1.197 +private: 1.198 + UChar* fText; 1.199 + FieldType fType; 1.200 + uint8_t fWidth; 1.201 + 1.202 + GMTOffsetField(); 1.203 +}; 1.204 + 1.205 +GMTOffsetField::GMTOffsetField() 1.206 +: fText(NULL), fType(TEXT), fWidth(0) { 1.207 +} 1.208 + 1.209 +GMTOffsetField::~GMTOffsetField() { 1.210 + if (fText) { 1.211 + uprv_free(fText); 1.212 + } 1.213 +} 1.214 + 1.215 +GMTOffsetField* 1.216 +GMTOffsetField::createText(const UnicodeString& text, UErrorCode& status) { 1.217 + if (U_FAILURE(status)) { 1.218 + return NULL; 1.219 + } 1.220 + GMTOffsetField* result = new GMTOffsetField(); 1.221 + if (result == NULL) { 1.222 + status = U_MEMORY_ALLOCATION_ERROR; 1.223 + return NULL; 1.224 + } 1.225 + 1.226 + int32_t len = text.length(); 1.227 + result->fText = (UChar*)uprv_malloc((len + 1) * sizeof(UChar)); 1.228 + if (result->fText == NULL) { 1.229 + status = U_MEMORY_ALLOCATION_ERROR; 1.230 + delete result; 1.231 + return NULL; 1.232 + } 1.233 + u_strncpy(result->fText, text.getBuffer(), len); 1.234 + result->fText[len] = 0; 1.235 + result->fType = TEXT; 1.236 + 1.237 + return result; 1.238 +} 1.239 + 1.240 +GMTOffsetField* 1.241 +GMTOffsetField::createTimeField(FieldType type, uint8_t width, UErrorCode& status) { 1.242 + U_ASSERT(type != TEXT); 1.243 + if (U_FAILURE(status)) { 1.244 + return NULL; 1.245 + } 1.246 + GMTOffsetField* result = new GMTOffsetField(); 1.247 + if (result == NULL) { 1.248 + status = U_MEMORY_ALLOCATION_ERROR; 1.249 + return NULL; 1.250 + } 1.251 + 1.252 + result->fType = type; 1.253 + result->fWidth = width; 1.254 + 1.255 + return result; 1.256 +} 1.257 + 1.258 +UBool 1.259 +GMTOffsetField::isValid(FieldType type, int32_t width) { 1.260 + switch (type) { 1.261 + case HOUR: 1.262 + return (width == 1 || width == 2); 1.263 + case MINUTE: 1.264 + case SECOND: 1.265 + return (width == 2); 1.266 + default: 1.267 + U_ASSERT(FALSE); 1.268 + } 1.269 + return (width > 0); 1.270 +} 1.271 + 1.272 +GMTOffsetField::FieldType 1.273 +GMTOffsetField::getTypeByLetter(UChar ch) { 1.274 + if (ch == 0x0048 /* H */) { 1.275 + return HOUR; 1.276 + } else if (ch == 0x006D /* m */) { 1.277 + return MINUTE; 1.278 + } else if (ch == 0x0073 /* s */) { 1.279 + return SECOND; 1.280 + } 1.281 + return TEXT; 1.282 +} 1.283 + 1.284 +inline GMTOffsetField::FieldType 1.285 +GMTOffsetField::getType() const { 1.286 + return fType; 1.287 + } 1.288 + 1.289 +inline uint8_t 1.290 +GMTOffsetField::getWidth() const { 1.291 + return fWidth; 1.292 +} 1.293 + 1.294 +inline const UChar* 1.295 +GMTOffsetField::getPatternText(void) const { 1.296 + return fText; 1.297 +} 1.298 + 1.299 + 1.300 +U_CDECL_BEGIN 1.301 +static void U_CALLCONV 1.302 +deleteGMTOffsetField(void *obj) { 1.303 + delete static_cast<GMTOffsetField *>(obj); 1.304 +} 1.305 +U_CDECL_END 1.306 + 1.307 + 1.308 +// ------------------------------------------------------------------ 1.309 +// TimeZoneFormat 1.310 +// ------------------------------------------------------------------ 1.311 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeZoneFormat) 1.312 + 1.313 +TimeZoneFormat::TimeZoneFormat(const Locale& locale, UErrorCode& status) 1.314 +: fLocale(locale), fTimeZoneNames(NULL), fTimeZoneGenericNames(NULL), fDefParseOptionFlags(0) { 1.315 + 1.316 + for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) { 1.317 + fGMTOffsetPatternItems[i] = NULL; 1.318 + } 1.319 + 1.320 + const char* region = fLocale.getCountry(); 1.321 + int32_t regionLen = uprv_strlen(region); 1.322 + if (regionLen == 0) { 1.323 + char loc[ULOC_FULLNAME_CAPACITY]; 1.324 + uloc_addLikelySubtags(fLocale.getName(), loc, sizeof(loc), &status); 1.325 + 1.326 + regionLen = uloc_getCountry(loc, fTargetRegion, sizeof(fTargetRegion), &status); 1.327 + if (U_SUCCESS(status)) { 1.328 + fTargetRegion[regionLen] = 0; 1.329 + } else { 1.330 + return; 1.331 + } 1.332 + } else if (regionLen < (int32_t)sizeof(fTargetRegion)) { 1.333 + uprv_strcpy(fTargetRegion, region); 1.334 + } else { 1.335 + fTargetRegion[0] = 0; 1.336 + } 1.337 + 1.338 + fTimeZoneNames = TimeZoneNames::createInstance(locale, status); 1.339 + // fTimeZoneGenericNames is lazily instantiated 1.340 + if (U_FAILURE(status)) { 1.341 + return; 1.342 + } 1.343 + 1.344 + const UChar* gmtPattern = NULL; 1.345 + const UChar* hourFormats = NULL; 1.346 + 1.347 + UResourceBundle *zoneBundle = ures_open(U_ICUDATA_ZONE, locale.getName(), &status); 1.348 + UResourceBundle *zoneStringsArray = ures_getByKeyWithFallback(zoneBundle, gZoneStringsTag, NULL, &status); 1.349 + if (U_SUCCESS(status)) { 1.350 + const UChar* resStr; 1.351 + int32_t len; 1.352 + resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gGmtFormatTag, &len, &status); 1.353 + if (len > 0) { 1.354 + gmtPattern = resStr; 1.355 + } 1.356 + resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gGmtZeroFormatTag, &len, &status); 1.357 + if (len > 0) { 1.358 + fGMTZeroFormat.setTo(TRUE, resStr, len); 1.359 + } 1.360 + resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gHourFormatTag, &len, &status); 1.361 + if (len > 0) { 1.362 + hourFormats = resStr; 1.363 + } 1.364 + ures_close(zoneStringsArray); 1.365 + ures_close(zoneBundle); 1.366 + } 1.367 + 1.368 + if (gmtPattern == NULL) { 1.369 + gmtPattern = DEFAULT_GMT_PATTERN; 1.370 + } 1.371 + initGMTPattern(UnicodeString(gmtPattern, -1), status); 1.372 + 1.373 + UBool useDefaultOffsetPatterns = TRUE; 1.374 + if (hourFormats) { 1.375 + UChar *sep = u_strchr(hourFormats, (UChar)0x003B /* ';' */); 1.376 + if (sep != NULL) { 1.377 + UErrorCode tmpStatus = U_ZERO_ERROR; 1.378 + fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM].setTo(FALSE, hourFormats, (int32_t)(sep - hourFormats)); 1.379 + fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM].setTo(TRUE, sep + 1, -1); 1.380 + expandOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HMS], tmpStatus); 1.381 + expandOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HMS], tmpStatus); 1.382 + truncateOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_H], tmpStatus); 1.383 + truncateOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_H], tmpStatus); 1.384 + if (U_SUCCESS(tmpStatus)) { 1.385 + useDefaultOffsetPatterns = FALSE; 1.386 + } 1.387 + } 1.388 + } 1.389 + if (useDefaultOffsetPatterns) { 1.390 + fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_H].setTo(TRUE, DEFAULT_GMT_POSITIVE_H, -1); 1.391 + fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM].setTo(TRUE, DEFAULT_GMT_POSITIVE_HM, -1); 1.392 + fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HMS].setTo(TRUE, DEFAULT_GMT_POSITIVE_HMS, -1); 1.393 + fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_H].setTo(TRUE, DEFAULT_GMT_NEGATIVE_H, -1); 1.394 + fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM].setTo(TRUE, DEFAULT_GMT_NEGATIVE_HM, -1); 1.395 + fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HMS].setTo(TRUE, DEFAULT_GMT_NEGATIVE_HMS, -1); 1.396 + } 1.397 + initGMTOffsetPatterns(status); 1.398 + 1.399 + NumberingSystem* ns = NumberingSystem::createInstance(locale, status); 1.400 + UBool useDefDigits = TRUE; 1.401 + if (ns && !ns->isAlgorithmic()) { 1.402 + UnicodeString digits = ns->getDescription(); 1.403 + useDefDigits = !toCodePoints(digits, fGMTOffsetDigits, 10); 1.404 + } 1.405 + if (useDefDigits) { 1.406 + uprv_memcpy(fGMTOffsetDigits, DEFAULT_GMT_DIGITS, sizeof(UChar32) * 10); 1.407 + } 1.408 + delete ns; 1.409 +} 1.410 + 1.411 +TimeZoneFormat::TimeZoneFormat(const TimeZoneFormat& other) 1.412 +: Format(other), fTimeZoneNames(NULL), fTimeZoneGenericNames(NULL) { 1.413 + 1.414 + for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) { 1.415 + fGMTOffsetPatternItems[i] = NULL; 1.416 + } 1.417 + *this = other; 1.418 +} 1.419 + 1.420 + 1.421 +TimeZoneFormat::~TimeZoneFormat() { 1.422 + delete fTimeZoneNames; 1.423 + delete fTimeZoneGenericNames; 1.424 + for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) { 1.425 + delete fGMTOffsetPatternItems[i]; 1.426 + } 1.427 +} 1.428 + 1.429 +TimeZoneFormat& 1.430 +TimeZoneFormat::operator=(const TimeZoneFormat& other) { 1.431 + if (this == &other) { 1.432 + return *this; 1.433 + } 1.434 + 1.435 + delete fTimeZoneNames; 1.436 + delete fTimeZoneGenericNames; 1.437 + fTimeZoneGenericNames = NULL; 1.438 + 1.439 + fLocale = other.fLocale; 1.440 + uprv_memcpy(fTargetRegion, other.fTargetRegion, sizeof(fTargetRegion)); 1.441 + 1.442 + fTimeZoneNames = other.fTimeZoneNames->clone(); 1.443 + if (other.fTimeZoneGenericNames) { 1.444 + // TODO: this test has dubious thread safety. 1.445 + fTimeZoneGenericNames = other.fTimeZoneGenericNames->clone(); 1.446 + } 1.447 + 1.448 + fGMTPattern = other.fGMTPattern; 1.449 + fGMTPatternPrefix = other.fGMTPatternPrefix; 1.450 + fGMTPatternSuffix = other.fGMTPatternSuffix; 1.451 + 1.452 + UErrorCode status = U_ZERO_ERROR; 1.453 + for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) { 1.454 + fGMTOffsetPatterns[i] = other.fGMTOffsetPatterns[i]; 1.455 + delete fGMTOffsetPatternItems[i]; 1.456 + } 1.457 + initGMTOffsetPatterns(status); 1.458 + U_ASSERT(U_SUCCESS(status)); 1.459 + 1.460 + fGMTZeroFormat = other.fGMTZeroFormat; 1.461 + 1.462 + uprv_memcpy(fGMTOffsetDigits, other.fGMTOffsetDigits, sizeof(fGMTOffsetDigits)); 1.463 + 1.464 + fDefParseOptionFlags = other.fDefParseOptionFlags; 1.465 + 1.466 + return *this; 1.467 +} 1.468 + 1.469 + 1.470 +UBool 1.471 +TimeZoneFormat::operator==(const Format& other) const { 1.472 + TimeZoneFormat* tzfmt = (TimeZoneFormat*)&other; 1.473 + 1.474 + UBool isEqual = 1.475 + fLocale == tzfmt->fLocale 1.476 + && fGMTPattern == tzfmt->fGMTPattern 1.477 + && fGMTZeroFormat == tzfmt->fGMTZeroFormat 1.478 + && *fTimeZoneNames == *tzfmt->fTimeZoneNames; 1.479 + 1.480 + for (int32_t i = 0; i < UTZFMT_PAT_COUNT && isEqual; i++) { 1.481 + isEqual = fGMTOffsetPatterns[i] == tzfmt->fGMTOffsetPatterns[i]; 1.482 + } 1.483 + for (int32_t i = 0; i < 10 && isEqual; i++) { 1.484 + isEqual = fGMTOffsetDigits[i] == tzfmt->fGMTOffsetDigits[i]; 1.485 + } 1.486 + // TODO 1.487 + // Check fTimeZoneGenericNames. For now, 1.488 + // if fTimeZoneNames is same, fTimeZoneGenericNames should 1.489 + // be also equivalent. 1.490 + return isEqual; 1.491 +} 1.492 + 1.493 +Format* 1.494 +TimeZoneFormat::clone() const { 1.495 + return new TimeZoneFormat(*this); 1.496 +} 1.497 + 1.498 +TimeZoneFormat* U_EXPORT2 1.499 +TimeZoneFormat::createInstance(const Locale& locale, UErrorCode& status) { 1.500 + TimeZoneFormat* tzfmt = new TimeZoneFormat(locale, status); 1.501 + if (U_SUCCESS(status)) { 1.502 + return tzfmt; 1.503 + } 1.504 + delete tzfmt; 1.505 + return NULL; 1.506 +} 1.507 + 1.508 +// ------------------------------------------------------------------ 1.509 +// Setter and Getter 1.510 + 1.511 +const TimeZoneNames* 1.512 +TimeZoneFormat::getTimeZoneNames() const { 1.513 + return (const TimeZoneNames*)fTimeZoneNames; 1.514 +} 1.515 + 1.516 +void 1.517 +TimeZoneFormat::adoptTimeZoneNames(TimeZoneNames *tznames) { 1.518 + delete fTimeZoneNames; 1.519 + fTimeZoneNames = tznames; 1.520 + 1.521 + // TODO - We should also update fTimeZoneGenericNames 1.522 +} 1.523 + 1.524 +void 1.525 +TimeZoneFormat::setTimeZoneNames(const TimeZoneNames &tznames) { 1.526 + delete fTimeZoneNames; 1.527 + fTimeZoneNames = tznames.clone(); 1.528 + 1.529 + // TODO - We should also update fTimeZoneGenericNames 1.530 +} 1.531 + 1.532 +void 1.533 +TimeZoneFormat::setDefaultParseOptions(uint32_t flags) { 1.534 + fDefParseOptionFlags = flags; 1.535 +} 1.536 + 1.537 +uint32_t 1.538 +TimeZoneFormat::getDefaultParseOptions(void) const { 1.539 + return fDefParseOptionFlags; 1.540 +} 1.541 + 1.542 + 1.543 +UnicodeString& 1.544 +TimeZoneFormat::getGMTPattern(UnicodeString& pattern) const { 1.545 + return pattern.setTo(fGMTPattern); 1.546 +} 1.547 + 1.548 +void 1.549 +TimeZoneFormat::setGMTPattern(const UnicodeString& pattern, UErrorCode& status) { 1.550 + initGMTPattern(pattern, status); 1.551 +} 1.552 + 1.553 +UnicodeString& 1.554 +TimeZoneFormat::getGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type, UnicodeString& pattern) const { 1.555 + return pattern.setTo(fGMTOffsetPatterns[type]); 1.556 +} 1.557 + 1.558 +void 1.559 +TimeZoneFormat::setGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type, const UnicodeString& pattern, UErrorCode& status) { 1.560 + if (U_FAILURE(status)) { 1.561 + return; 1.562 + } 1.563 + if (pattern == fGMTOffsetPatterns[type]) { 1.564 + // No need to reset 1.565 + return; 1.566 + } 1.567 + 1.568 + OffsetFields required = FIELDS_HM; 1.569 + switch (type) { 1.570 + case UTZFMT_PAT_POSITIVE_H: 1.571 + case UTZFMT_PAT_NEGATIVE_H: 1.572 + required = FIELDS_H; 1.573 + break; 1.574 + case UTZFMT_PAT_POSITIVE_HM: 1.575 + case UTZFMT_PAT_NEGATIVE_HM: 1.576 + required = FIELDS_HM; 1.577 + break; 1.578 + case UTZFMT_PAT_POSITIVE_HMS: 1.579 + case UTZFMT_PAT_NEGATIVE_HMS: 1.580 + required = FIELDS_HMS; 1.581 + break; 1.582 + default: 1.583 + U_ASSERT(FALSE); 1.584 + break; 1.585 + } 1.586 + 1.587 + UVector* patternItems = parseOffsetPattern(pattern, required, status); 1.588 + if (patternItems == NULL) { 1.589 + return; 1.590 + } 1.591 + 1.592 + fGMTOffsetPatterns[type].setTo(pattern); 1.593 + delete fGMTOffsetPatternItems[type]; 1.594 + fGMTOffsetPatternItems[type] = patternItems; 1.595 + checkAbuttingHoursAndMinutes(); 1.596 +} 1.597 + 1.598 +UnicodeString& 1.599 +TimeZoneFormat::getGMTOffsetDigits(UnicodeString& digits) const { 1.600 + digits.remove(); 1.601 + for (int32_t i = 0; i < 10; i++) { 1.602 + digits.append(fGMTOffsetDigits[i]); 1.603 + } 1.604 + return digits; 1.605 +} 1.606 + 1.607 +void 1.608 +TimeZoneFormat::setGMTOffsetDigits(const UnicodeString& digits, UErrorCode& status) { 1.609 + if (U_FAILURE(status)) { 1.610 + return; 1.611 + } 1.612 + UChar32 digitArray[10]; 1.613 + if (!toCodePoints(digits, digitArray, 10)) { 1.614 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.615 + return; 1.616 + } 1.617 + uprv_memcpy(fGMTOffsetDigits, digitArray, sizeof(UChar32)*10); 1.618 +} 1.619 + 1.620 +UnicodeString& 1.621 +TimeZoneFormat::getGMTZeroFormat(UnicodeString& gmtZeroFormat) const { 1.622 + return gmtZeroFormat.setTo(fGMTZeroFormat); 1.623 +} 1.624 + 1.625 +void 1.626 +TimeZoneFormat::setGMTZeroFormat(const UnicodeString& gmtZeroFormat, UErrorCode& status) { 1.627 + if (U_SUCCESS(status)) { 1.628 + if (gmtZeroFormat.isEmpty()) { 1.629 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.630 + } else if (gmtZeroFormat != fGMTZeroFormat) { 1.631 + fGMTZeroFormat.setTo(gmtZeroFormat); 1.632 + } 1.633 + } 1.634 +} 1.635 + 1.636 +// ------------------------------------------------------------------ 1.637 +// Format and Parse 1.638 + 1.639 +UnicodeString& 1.640 +TimeZoneFormat::format(UTimeZoneFormatStyle style, const TimeZone& tz, UDate date, 1.641 + UnicodeString& name, UTimeZoneFormatTimeType* timeType /* = NULL */) const { 1.642 + if (timeType) { 1.643 + *timeType = UTZFMT_TIME_TYPE_UNKNOWN; 1.644 + } 1.645 + 1.646 + UBool noOffsetFormatFallback = FALSE; 1.647 + 1.648 + switch (style) { 1.649 + case UTZFMT_STYLE_GENERIC_LOCATION: 1.650 + formatGeneric(tz, UTZGNM_LOCATION, date, name); 1.651 + break; 1.652 + case UTZFMT_STYLE_GENERIC_LONG: 1.653 + formatGeneric(tz, UTZGNM_LONG, date, name); 1.654 + break; 1.655 + case UTZFMT_STYLE_GENERIC_SHORT: 1.656 + formatGeneric(tz, UTZGNM_SHORT, date, name); 1.657 + break; 1.658 + case UTZFMT_STYLE_SPECIFIC_LONG: 1.659 + formatSpecific(tz, UTZNM_LONG_STANDARD, UTZNM_LONG_DAYLIGHT, date, name, timeType); 1.660 + break; 1.661 + case UTZFMT_STYLE_SPECIFIC_SHORT: 1.662 + formatSpecific(tz, UTZNM_SHORT_STANDARD, UTZNM_SHORT_DAYLIGHT, date, name, timeType); 1.663 + break; 1.664 + 1.665 + case UTZFMT_STYLE_ZONE_ID: 1.666 + tz.getID(name); 1.667 + noOffsetFormatFallback = TRUE; 1.668 + break; 1.669 + case UTZFMT_STYLE_ZONE_ID_SHORT: 1.670 + { 1.671 + const UChar* shortID = ZoneMeta::getShortID(tz); 1.672 + if (shortID == NULL) { 1.673 + shortID = UNKNOWN_SHORT_ZONE_ID; 1.674 + } 1.675 + name.setTo(shortID, -1); 1.676 + } 1.677 + noOffsetFormatFallback = TRUE; 1.678 + break; 1.679 + 1.680 + case UTZFMT_STYLE_EXEMPLAR_LOCATION: 1.681 + formatExemplarLocation(tz, name); 1.682 + noOffsetFormatFallback = TRUE; 1.683 + break; 1.684 + 1.685 + default: 1.686 + // will be handled below 1.687 + break; 1.688 + } 1.689 + 1.690 + if (name.isEmpty() && !noOffsetFormatFallback) { 1.691 + UErrorCode status = U_ZERO_ERROR; 1.692 + int32_t rawOffset, dstOffset; 1.693 + tz.getOffset(date, FALSE, rawOffset, dstOffset, status); 1.694 + int32_t offset = rawOffset + dstOffset; 1.695 + if (U_SUCCESS(status)) { 1.696 + switch (style) { 1.697 + case UTZFMT_STYLE_GENERIC_LOCATION: 1.698 + case UTZFMT_STYLE_GENERIC_LONG: 1.699 + case UTZFMT_STYLE_SPECIFIC_LONG: 1.700 + case UTZFMT_STYLE_LOCALIZED_GMT: 1.701 + formatOffsetLocalizedGMT(offset, name, status); 1.702 + break; 1.703 + 1.704 + case UTZFMT_STYLE_GENERIC_SHORT: 1.705 + case UTZFMT_STYLE_SPECIFIC_SHORT: 1.706 + case UTZFMT_STYLE_LOCALIZED_GMT_SHORT: 1.707 + formatOffsetShortLocalizedGMT(offset, name, status); 1.708 + break; 1.709 + 1.710 + case UTZFMT_STYLE_ISO_BASIC_SHORT: 1.711 + formatOffsetISO8601Basic(offset, TRUE, TRUE, TRUE, name, status); 1.712 + break; 1.713 + 1.714 + case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT: 1.715 + formatOffsetISO8601Basic(offset, FALSE, TRUE, TRUE, name, status); 1.716 + break; 1.717 + 1.718 + case UTZFMT_STYLE_ISO_BASIC_FIXED: 1.719 + formatOffsetISO8601Basic(offset, TRUE, FALSE, TRUE, name, status); 1.720 + break; 1.721 + 1.722 + case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED: 1.723 + formatOffsetISO8601Basic(offset, FALSE, FALSE, TRUE, name, status); 1.724 + break; 1.725 + 1.726 + case UTZFMT_STYLE_ISO_EXTENDED_FIXED: 1.727 + formatOffsetISO8601Extended(offset, TRUE, FALSE, TRUE, name, status); 1.728 + break; 1.729 + 1.730 + case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED: 1.731 + formatOffsetISO8601Extended(offset, FALSE, FALSE, TRUE, name, status); 1.732 + break; 1.733 + 1.734 + case UTZFMT_STYLE_ISO_BASIC_FULL: 1.735 + formatOffsetISO8601Basic(offset, TRUE, FALSE, FALSE, name, status); 1.736 + break; 1.737 + 1.738 + case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL: 1.739 + formatOffsetISO8601Basic(offset, FALSE, FALSE, FALSE, name, status); 1.740 + break; 1.741 + 1.742 + case UTZFMT_STYLE_ISO_EXTENDED_FULL: 1.743 + formatOffsetISO8601Extended(offset, TRUE, FALSE, FALSE, name, status); 1.744 + break; 1.745 + 1.746 + case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL: 1.747 + formatOffsetISO8601Extended(offset, FALSE, FALSE, FALSE, name, status); 1.748 + break; 1.749 + 1.750 + default: 1.751 + // UTZFMT_STYLE_ZONE_ID, UTZFMT_STYLE_ZONE_ID_SHORT, UTZFMT_STYLE_EXEMPLAR_LOCATION 1.752 + break; 1.753 + } 1.754 + 1.755 + if (timeType) { 1.756 + *timeType = (dstOffset != 0) ? UTZFMT_TIME_TYPE_DAYLIGHT : UTZFMT_TIME_TYPE_STANDARD; 1.757 + } 1.758 + } 1.759 + } 1.760 + 1.761 + return name; 1.762 +} 1.763 + 1.764 +UnicodeString& 1.765 +TimeZoneFormat::format(const Formattable& obj, UnicodeString& appendTo, 1.766 + FieldPosition& pos, UErrorCode& status) const { 1.767 + if (U_FAILURE(status)) { 1.768 + return appendTo; 1.769 + } 1.770 + UDate date = Calendar::getNow(); 1.771 + if (obj.getType() == Formattable::kObject) { 1.772 + const UObject* formatObj = obj.getObject(); 1.773 + const TimeZone* tz = dynamic_cast<const TimeZone*>(formatObj); 1.774 + if (tz == NULL) { 1.775 + const Calendar* cal = dynamic_cast<const Calendar*>(formatObj); 1.776 + if (cal != NULL) { 1.777 + tz = &cal->getTimeZone(); 1.778 + date = cal->getTime(status); 1.779 + } 1.780 + } 1.781 + if (tz != NULL) { 1.782 + int32_t rawOffset, dstOffset; 1.783 + tz->getOffset(date, FALSE, rawOffset, dstOffset, status); 1.784 + UnicodeString result; 1.785 + formatOffsetLocalizedGMT(rawOffset + dstOffset, result, status); 1.786 + if (U_SUCCESS(status)) { 1.787 + appendTo.append(result); 1.788 + if (pos.getField() == UDAT_TIMEZONE_FIELD) { 1.789 + pos.setBeginIndex(0); 1.790 + pos.setEndIndex(result.length()); 1.791 + } 1.792 + } 1.793 + } 1.794 + } 1.795 + return appendTo; 1.796 +} 1.797 + 1.798 +TimeZone* 1.799 +TimeZoneFormat::parse(UTimeZoneFormatStyle style, const UnicodeString& text, ParsePosition& pos, 1.800 + UTimeZoneFormatTimeType* timeType /*= NULL*/) const { 1.801 + return parse(style, text, pos, getDefaultParseOptions(), timeType); 1.802 +} 1.803 + 1.804 +TimeZone* 1.805 +TimeZoneFormat::parse(UTimeZoneFormatStyle style, const UnicodeString& text, ParsePosition& pos, 1.806 + int32_t parseOptions, UTimeZoneFormatTimeType* timeType /* = NULL */) const { 1.807 + if (timeType) { 1.808 + *timeType = UTZFMT_TIME_TYPE_UNKNOWN; 1.809 + } 1.810 + 1.811 + int32_t startIdx = pos.getIndex(); 1.812 + int32_t maxPos = text.length(); 1.813 + int32_t offset; 1.814 + 1.815 + // Styles using localized GMT format as fallback 1.816 + UBool fallbackLocalizedGMT = 1.817 + (style == UTZFMT_STYLE_SPECIFIC_LONG || style == UTZFMT_STYLE_GENERIC_LONG || style == UTZFMT_STYLE_GENERIC_LOCATION); 1.818 + UBool fallbackShortLocalizedGMT = 1.819 + (style == UTZFMT_STYLE_SPECIFIC_SHORT || style == UTZFMT_STYLE_GENERIC_SHORT); 1.820 + 1.821 + int32_t evaluated = 0; // bit flags representing already evaluated styles 1.822 + ParsePosition tmpPos(startIdx); 1.823 + 1.824 + int32_t parsedOffset = UNKNOWN_OFFSET; // stores successfully parsed offset for later use 1.825 + int32_t parsedPos = -1; // stores successfully parsed offset position for later use 1.826 + 1.827 + // Try localized GMT format first if necessary 1.828 + if (fallbackLocalizedGMT || fallbackShortLocalizedGMT) { 1.829 + UBool hasDigitOffset = FALSE; 1.830 + offset = parseOffsetLocalizedGMT(text, tmpPos, fallbackShortLocalizedGMT, &hasDigitOffset); 1.831 + if (tmpPos.getErrorIndex() == -1) { 1.832 + // Even when the input text was successfully parsed as a localized GMT format text, 1.833 + // we may still need to evaluate the specified style if - 1.834 + // 1) GMT zero format was used, and 1.835 + // 2) The input text was not completely processed 1.836 + if (tmpPos.getIndex() == maxPos || hasDigitOffset) { 1.837 + pos.setIndex(tmpPos.getIndex()); 1.838 + return createTimeZoneForOffset(offset); 1.839 + } 1.840 + parsedOffset = offset; 1.841 + parsedPos = tmpPos.getIndex(); 1.842 + } 1.843 + // Note: For now, no distinction between long/short localized GMT format in the parser. 1.844 + // This might be changed in future. 1.845 + // evaluated |= (fallbackLocalizedGMT ? STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT] : STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT]); 1.846 + evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT] | STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT]; 1.847 + } 1.848 + 1.849 + UErrorCode status = U_ZERO_ERROR; 1.850 + UnicodeString tzID; 1.851 + 1.852 + // Try the specified style 1.853 + switch (style) { 1.854 + case UTZFMT_STYLE_LOCALIZED_GMT: 1.855 + { 1.856 + tmpPos.setIndex(startIdx); 1.857 + tmpPos.setErrorIndex(-1); 1.858 + 1.859 + offset = parseOffsetLocalizedGMT(text, tmpPos); 1.860 + if (tmpPos.getErrorIndex() == -1) { 1.861 + pos.setIndex(tmpPos.getIndex()); 1.862 + return createTimeZoneForOffset(offset); 1.863 + } 1.864 + 1.865 + // Note: For now, no distinction between long/short localized GMT format in the parser. 1.866 + // This might be changed in future. 1.867 + evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT]; 1.868 + 1.869 + break; 1.870 + } 1.871 + case UTZFMT_STYLE_LOCALIZED_GMT_SHORT: 1.872 + { 1.873 + tmpPos.setIndex(startIdx); 1.874 + tmpPos.setErrorIndex(-1); 1.875 + 1.876 + offset = parseOffsetShortLocalizedGMT(text, tmpPos); 1.877 + if (tmpPos.getErrorIndex() == -1) { 1.878 + pos.setIndex(tmpPos.getIndex()); 1.879 + return createTimeZoneForOffset(offset); 1.880 + } 1.881 + 1.882 + // Note: For now, no distinction between long/short localized GMT format in the parser. 1.883 + // This might be changed in future. 1.884 + evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT]; 1.885 + 1.886 + break; 1.887 + } 1.888 + case UTZFMT_STYLE_ISO_BASIC_SHORT: 1.889 + case UTZFMT_STYLE_ISO_BASIC_FIXED: 1.890 + case UTZFMT_STYLE_ISO_BASIC_FULL: 1.891 + case UTZFMT_STYLE_ISO_EXTENDED_FIXED: 1.892 + case UTZFMT_STYLE_ISO_EXTENDED_FULL: 1.893 + { 1.894 + tmpPos.setIndex(startIdx); 1.895 + tmpPos.setErrorIndex(-1); 1.896 + 1.897 + offset = parseOffsetISO8601(text, tmpPos); 1.898 + if (tmpPos.getErrorIndex() == -1) { 1.899 + pos.setIndex(tmpPos.getIndex()); 1.900 + return createTimeZoneForOffset(offset); 1.901 + } 1.902 + 1.903 + break; 1.904 + } 1.905 + 1.906 + case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT: 1.907 + case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED: 1.908 + case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL: 1.909 + case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED: 1.910 + case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL: 1.911 + { 1.912 + tmpPos.setIndex(startIdx); 1.913 + tmpPos.setErrorIndex(-1); 1.914 + 1.915 + // Exclude the case of UTC Indicator "Z" here 1.916 + UBool hasDigitOffset = FALSE; 1.917 + offset = parseOffsetISO8601(text, tmpPos, FALSE, &hasDigitOffset); 1.918 + if (tmpPos.getErrorIndex() == -1 && hasDigitOffset) { 1.919 + pos.setIndex(tmpPos.getIndex()); 1.920 + return createTimeZoneForOffset(offset); 1.921 + } 1.922 + 1.923 + break; 1.924 + } 1.925 + 1.926 + case UTZFMT_STYLE_SPECIFIC_LONG: 1.927 + case UTZFMT_STYLE_SPECIFIC_SHORT: 1.928 + { 1.929 + // Specific styles 1.930 + int32_t nameTypes = 0; 1.931 + if (style == UTZFMT_STYLE_SPECIFIC_LONG) { 1.932 + nameTypes = (UTZNM_LONG_STANDARD | UTZNM_LONG_DAYLIGHT); 1.933 + } else { 1.934 + U_ASSERT(style == UTZFMT_STYLE_SPECIFIC_SHORT); 1.935 + nameTypes = (UTZNM_SHORT_STANDARD | UTZNM_SHORT_DAYLIGHT); 1.936 + } 1.937 + LocalPointer<TimeZoneNames::MatchInfoCollection> specificMatches(fTimeZoneNames->find(text, startIdx, nameTypes, status)); 1.938 + if (U_FAILURE(status)) { 1.939 + pos.setErrorIndex(startIdx); 1.940 + return NULL; 1.941 + } 1.942 + if (!specificMatches.isNull()) { 1.943 + int32_t matchIdx = -1; 1.944 + int32_t matchPos = -1; 1.945 + for (int32_t i = 0; i < specificMatches->size(); i++) { 1.946 + matchPos = startIdx + specificMatches->getMatchLengthAt(i); 1.947 + if (matchPos > parsedPos) { 1.948 + matchIdx = i; 1.949 + parsedPos = matchPos; 1.950 + } 1.951 + } 1.952 + if (matchIdx >= 0) { 1.953 + if (timeType) { 1.954 + *timeType = getTimeType(specificMatches->getNameTypeAt(matchIdx)); 1.955 + } 1.956 + pos.setIndex(matchPos); 1.957 + getTimeZoneID(specificMatches.getAlias(), matchIdx, tzID); 1.958 + U_ASSERT(!tzID.isEmpty()); 1.959 + return TimeZone::createTimeZone(tzID); 1.960 + } 1.961 + } 1.962 + break; 1.963 + } 1.964 + case UTZFMT_STYLE_GENERIC_LONG: 1.965 + case UTZFMT_STYLE_GENERIC_SHORT: 1.966 + case UTZFMT_STYLE_GENERIC_LOCATION: 1.967 + { 1.968 + int32_t genericNameTypes = 0; 1.969 + switch (style) { 1.970 + case UTZFMT_STYLE_GENERIC_LOCATION: 1.971 + genericNameTypes = UTZGNM_LOCATION; 1.972 + break; 1.973 + 1.974 + case UTZFMT_STYLE_GENERIC_LONG: 1.975 + genericNameTypes = UTZGNM_LONG | UTZGNM_LOCATION; 1.976 + break; 1.977 + 1.978 + case UTZFMT_STYLE_GENERIC_SHORT: 1.979 + genericNameTypes = UTZGNM_SHORT | UTZGNM_LOCATION; 1.980 + break; 1.981 + 1.982 + default: 1.983 + U_ASSERT(FALSE); 1.984 + } 1.985 + 1.986 + int32_t len = 0; 1.987 + UTimeZoneFormatTimeType tt = UTZFMT_TIME_TYPE_UNKNOWN; 1.988 + const TimeZoneGenericNames *gnames = getTimeZoneGenericNames(status); 1.989 + if (U_SUCCESS(status)) { 1.990 + len = gnames->findBestMatch(text, startIdx, genericNameTypes, tzID, tt, status); 1.991 + } 1.992 + if (U_FAILURE(status)) { 1.993 + pos.setErrorIndex(startIdx); 1.994 + return NULL; 1.995 + } 1.996 + if (len > 0) { 1.997 + // Found a match 1.998 + if (timeType) { 1.999 + *timeType = tt; 1.1000 + } 1.1001 + pos.setIndex(startIdx + len); 1.1002 + U_ASSERT(!tzID.isEmpty()); 1.1003 + return TimeZone::createTimeZone(tzID); 1.1004 + } 1.1005 + 1.1006 + break; 1.1007 + } 1.1008 + case UTZFMT_STYLE_ZONE_ID: 1.1009 + { 1.1010 + tmpPos.setIndex(startIdx); 1.1011 + tmpPos.setErrorIndex(-1); 1.1012 + 1.1013 + parseZoneID(text, tmpPos, tzID); 1.1014 + if (tmpPos.getErrorIndex() == -1) { 1.1015 + pos.setIndex(tmpPos.getIndex()); 1.1016 + return TimeZone::createTimeZone(tzID); 1.1017 + } 1.1018 + break; 1.1019 + } 1.1020 + case UTZFMT_STYLE_ZONE_ID_SHORT: 1.1021 + { 1.1022 + tmpPos.setIndex(startIdx); 1.1023 + tmpPos.setErrorIndex(-1); 1.1024 + 1.1025 + parseShortZoneID(text, tmpPos, tzID); 1.1026 + if (tmpPos.getErrorIndex() == -1) { 1.1027 + pos.setIndex(tmpPos.getIndex()); 1.1028 + return TimeZone::createTimeZone(tzID); 1.1029 + } 1.1030 + break; 1.1031 + } 1.1032 + case UTZFMT_STYLE_EXEMPLAR_LOCATION: 1.1033 + { 1.1034 + tmpPos.setIndex(startIdx); 1.1035 + tmpPos.setErrorIndex(-1); 1.1036 + 1.1037 + parseExemplarLocation(text, tmpPos, tzID); 1.1038 + if (tmpPos.getErrorIndex() == -1) { 1.1039 + pos.setIndex(tmpPos.getIndex()); 1.1040 + return TimeZone::createTimeZone(tzID); 1.1041 + } 1.1042 + break; 1.1043 + } 1.1044 + } 1.1045 + evaluated |= STYLE_PARSE_FLAGS[style]; 1.1046 + 1.1047 + 1.1048 + if (parsedPos > startIdx) { 1.1049 + // When the specified style is one of SPECIFIC_XXX or GENERIC_XXX, we tried to parse the input 1.1050 + // as localized GMT format earlier. If parsedOffset is positive, it means it was successfully 1.1051 + // parsed as localized GMT format, but offset digits were not detected (more specifically, GMT 1.1052 + // zero format). Then, it tried to find a match within the set of display names, but could not 1.1053 + // find a match. At this point, we can safely assume the input text contains the localized 1.1054 + // GMT format. 1.1055 + U_ASSERT(parsedOffset != UNKNOWN_OFFSET); 1.1056 + pos.setIndex(parsedPos); 1.1057 + return createTimeZoneForOffset(parsedOffset); 1.1058 + } 1.1059 + 1.1060 + // Failed to parse the input text as the time zone format in the specified style. 1.1061 + // Check the longest match among other styles below. 1.1062 + UnicodeString parsedID; 1.1063 + UTimeZoneFormatTimeType parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN; 1.1064 + 1.1065 + U_ASSERT(parsedPos < 0); 1.1066 + U_ASSERT(parsedOffset == UNKNOWN_OFFSET); 1.1067 + 1.1068 + // ISO 8601 1.1069 + if (parsedPos < maxPos && 1.1070 + ((evaluated & ISO_Z_STYLE_FLAG) == 0 || (evaluated & ISO_LOCAL_STYLE_FLAG) == 0)) { 1.1071 + tmpPos.setIndex(startIdx); 1.1072 + tmpPos.setErrorIndex(-1); 1.1073 + 1.1074 + UBool hasDigitOffset = FALSE; 1.1075 + offset = parseOffsetISO8601(text, tmpPos, FALSE, &hasDigitOffset); 1.1076 + if (tmpPos.getErrorIndex() == -1) { 1.1077 + if (tmpPos.getIndex() == maxPos || hasDigitOffset) { 1.1078 + pos.setIndex(tmpPos.getIndex()); 1.1079 + return createTimeZoneForOffset(offset); 1.1080 + } 1.1081 + // Note: When ISO 8601 format contains offset digits, it should not 1.1082 + // collide with other formats. However, ISO 8601 UTC format "Z" (single letter) 1.1083 + // may collide with other names. In this case, we need to evaluate other names. 1.1084 + if (parsedPos < tmpPos.getIndex()) { 1.1085 + parsedOffset = offset; 1.1086 + parsedID.setToBogus(); 1.1087 + parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN; 1.1088 + parsedPos = tmpPos.getIndex(); 1.1089 + U_ASSERT(parsedPos == startIdx + 1); // only when "Z" is used 1.1090 + } 1.1091 + } 1.1092 + } 1.1093 + 1.1094 + // Localized GMT format 1.1095 + if (parsedPos < maxPos && 1.1096 + (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT]) == 0) { 1.1097 + tmpPos.setIndex(startIdx); 1.1098 + tmpPos.setErrorIndex(-1); 1.1099 + 1.1100 + UBool hasDigitOffset = FALSE; 1.1101 + offset = parseOffsetLocalizedGMT(text, tmpPos, FALSE, &hasDigitOffset); 1.1102 + if (tmpPos.getErrorIndex() == -1) { 1.1103 + if (tmpPos.getIndex() == maxPos || hasDigitOffset) { 1.1104 + pos.setIndex(tmpPos.getIndex()); 1.1105 + return createTimeZoneForOffset(offset); 1.1106 + } 1.1107 + // Evaluate other names - see the comment earlier in this method. 1.1108 + if (parsedPos < tmpPos.getIndex()) { 1.1109 + parsedOffset = offset; 1.1110 + parsedID.setToBogus(); 1.1111 + parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN; 1.1112 + parsedPos = tmpPos.getIndex(); 1.1113 + } 1.1114 + } 1.1115 + } 1.1116 + 1.1117 + if (parsedPos < maxPos && 1.1118 + (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT]) == 0) { 1.1119 + tmpPos.setIndex(startIdx); 1.1120 + tmpPos.setErrorIndex(-1); 1.1121 + 1.1122 + UBool hasDigitOffset = FALSE; 1.1123 + offset = parseOffsetLocalizedGMT(text, tmpPos, TRUE, &hasDigitOffset); 1.1124 + if (tmpPos.getErrorIndex() == -1) { 1.1125 + if (tmpPos.getIndex() == maxPos || hasDigitOffset) { 1.1126 + pos.setIndex(tmpPos.getIndex()); 1.1127 + return createTimeZoneForOffset(offset); 1.1128 + } 1.1129 + // Evaluate other names - see the comment earlier in this method. 1.1130 + if (parsedPos < tmpPos.getIndex()) { 1.1131 + parsedOffset = offset; 1.1132 + parsedID.setToBogus(); 1.1133 + parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN; 1.1134 + parsedPos = tmpPos.getIndex(); 1.1135 + } 1.1136 + } 1.1137 + } 1.1138 + 1.1139 + // When ParseOption.ALL_STYLES is available, we also try to look all possible display names and IDs. 1.1140 + // For example, when style is GENERIC_LONG, "EST" (SPECIFIC_SHORT) is never 1.1141 + // used for America/New_York. With parseAllStyles true, this code parses "EST" 1.1142 + // as America/New_York. 1.1143 + 1.1144 + // Note: Adding all possible names into the trie used by the implementation is quite heavy operation, 1.1145 + // which we want to avoid normally (note that we cache the trie, so this is applicable to the 1.1146 + // first time only as long as the cache does not expire). 1.1147 + 1.1148 + if (parseOptions & UTZFMT_PARSE_OPTION_ALL_STYLES) { 1.1149 + // Try all specific names and exemplar location names 1.1150 + if (parsedPos < maxPos) { 1.1151 + LocalPointer<TimeZoneNames::MatchInfoCollection> specificMatches(fTimeZoneNames->find(text, startIdx, ALL_SIMPLE_NAME_TYPES, status)); 1.1152 + if (U_FAILURE(status)) { 1.1153 + pos.setErrorIndex(startIdx); 1.1154 + return NULL; 1.1155 + } 1.1156 + int32_t specificMatchIdx = -1; 1.1157 + int32_t matchPos = -1; 1.1158 + if (!specificMatches.isNull()) { 1.1159 + for (int32_t i = 0; i < specificMatches->size(); i++) { 1.1160 + if (startIdx + specificMatches->getMatchLengthAt(i) > matchPos) { 1.1161 + specificMatchIdx = i; 1.1162 + matchPos = startIdx + specificMatches->getMatchLengthAt(i); 1.1163 + } 1.1164 + } 1.1165 + } 1.1166 + if (parsedPos < matchPos) { 1.1167 + U_ASSERT(specificMatchIdx >= 0); 1.1168 + parsedPos = matchPos; 1.1169 + getTimeZoneID(specificMatches.getAlias(), specificMatchIdx, parsedID); 1.1170 + parsedTimeType = getTimeType(specificMatches->getNameTypeAt(specificMatchIdx)); 1.1171 + parsedOffset = UNKNOWN_OFFSET; 1.1172 + } 1.1173 + } 1.1174 + // Try generic names 1.1175 + if (parsedPos < maxPos) { 1.1176 + int32_t genMatchLen = -1; 1.1177 + UTimeZoneFormatTimeType tt = UTZFMT_TIME_TYPE_UNKNOWN; 1.1178 + 1.1179 + const TimeZoneGenericNames *gnames = getTimeZoneGenericNames(status); 1.1180 + if (U_SUCCESS(status)) { 1.1181 + genMatchLen = gnames->findBestMatch(text, startIdx, ALL_GENERIC_NAME_TYPES, tzID, tt, status); 1.1182 + } 1.1183 + if (U_FAILURE(status)) { 1.1184 + pos.setErrorIndex(startIdx); 1.1185 + return NULL; 1.1186 + } 1.1187 + 1.1188 + if (parsedPos < startIdx + genMatchLen) { 1.1189 + parsedPos = startIdx + genMatchLen; 1.1190 + parsedID.setTo(tzID); 1.1191 + parsedTimeType = tt; 1.1192 + parsedOffset = UNKNOWN_OFFSET; 1.1193 + } 1.1194 + } 1.1195 + 1.1196 + // Try time zone ID 1.1197 + if (parsedPos < maxPos && (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_ZONE_ID]) == 0) { 1.1198 + tmpPos.setIndex(startIdx); 1.1199 + tmpPos.setErrorIndex(-1); 1.1200 + 1.1201 + parseZoneID(text, tmpPos, tzID); 1.1202 + if (tmpPos.getErrorIndex() == -1 && parsedPos < tmpPos.getIndex()) { 1.1203 + parsedPos = tmpPos.getIndex(); 1.1204 + parsedID.setTo(tzID); 1.1205 + parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN; 1.1206 + parsedOffset = UNKNOWN_OFFSET; 1.1207 + } 1.1208 + } 1.1209 + // Try short time zone ID 1.1210 + if (parsedPos < maxPos && (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_ZONE_ID]) == 0) { 1.1211 + tmpPos.setIndex(startIdx); 1.1212 + tmpPos.setErrorIndex(-1); 1.1213 + 1.1214 + parseShortZoneID(text, tmpPos, tzID); 1.1215 + if (tmpPos.getErrorIndex() == -1 && parsedPos < tmpPos.getIndex()) { 1.1216 + parsedPos = tmpPos.getIndex(); 1.1217 + parsedID.setTo(tzID); 1.1218 + parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN; 1.1219 + parsedOffset = UNKNOWN_OFFSET; 1.1220 + } 1.1221 + } 1.1222 + } 1.1223 + 1.1224 + if (parsedPos > startIdx) { 1.1225 + // Parsed successfully 1.1226 + TimeZone* parsedTZ; 1.1227 + if (parsedID.length() > 0) { 1.1228 + parsedTZ = TimeZone::createTimeZone(parsedID); 1.1229 + } else { 1.1230 + U_ASSERT(parsedOffset != UNKNOWN_OFFSET); 1.1231 + parsedTZ = createTimeZoneForOffset(parsedOffset); 1.1232 + } 1.1233 + if (timeType) { 1.1234 + *timeType = parsedTimeType; 1.1235 + } 1.1236 + pos.setIndex(parsedPos); 1.1237 + return parsedTZ; 1.1238 + } 1.1239 + 1.1240 + pos.setErrorIndex(startIdx); 1.1241 + return NULL; 1.1242 +} 1.1243 + 1.1244 +void 1.1245 +TimeZoneFormat::parseObject(const UnicodeString& source, Formattable& result, 1.1246 + ParsePosition& parse_pos) const { 1.1247 + result.adoptObject(parse(UTZFMT_STYLE_GENERIC_LOCATION, source, parse_pos, UTZFMT_PARSE_OPTION_ALL_STYLES)); 1.1248 +} 1.1249 + 1.1250 + 1.1251 +// ------------------------------------------------------------------ 1.1252 +// Private zone name format/parse implementation 1.1253 + 1.1254 +UnicodeString& 1.1255 +TimeZoneFormat::formatGeneric(const TimeZone& tz, int32_t genType, UDate date, UnicodeString& name) const { 1.1256 + UErrorCode status = U_ZERO_ERROR; 1.1257 + const TimeZoneGenericNames* gnames = getTimeZoneGenericNames(status); 1.1258 + if (U_FAILURE(status)) { 1.1259 + name.setToBogus(); 1.1260 + return name; 1.1261 + } 1.1262 + 1.1263 + if (genType == UTZGNM_LOCATION) { 1.1264 + const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(tz); 1.1265 + if (canonicalID == NULL) { 1.1266 + name.setToBogus(); 1.1267 + return name; 1.1268 + } 1.1269 + return gnames->getGenericLocationName(UnicodeString(canonicalID), name); 1.1270 + } 1.1271 + return gnames->getDisplayName(tz, (UTimeZoneGenericNameType)genType, date, name); 1.1272 +} 1.1273 + 1.1274 +UnicodeString& 1.1275 +TimeZoneFormat::formatSpecific(const TimeZone& tz, UTimeZoneNameType stdType, UTimeZoneNameType dstType, 1.1276 + UDate date, UnicodeString& name, UTimeZoneFormatTimeType *timeType) const { 1.1277 + if (fTimeZoneNames == NULL) { 1.1278 + name.setToBogus(); 1.1279 + return name; 1.1280 + } 1.1281 + 1.1282 + UErrorCode status = U_ZERO_ERROR; 1.1283 + UBool isDaylight = tz.inDaylightTime(date, status); 1.1284 + const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(tz); 1.1285 + 1.1286 + if (U_FAILURE(status) || canonicalID == NULL) { 1.1287 + name.setToBogus(); 1.1288 + return name; 1.1289 + } 1.1290 + 1.1291 + if (isDaylight) { 1.1292 + fTimeZoneNames->getDisplayName(UnicodeString(canonicalID), dstType, date, name); 1.1293 + } else { 1.1294 + fTimeZoneNames->getDisplayName(UnicodeString(canonicalID), stdType, date, name); 1.1295 + } 1.1296 + 1.1297 + if (timeType && !name.isEmpty()) { 1.1298 + *timeType = isDaylight ? UTZFMT_TIME_TYPE_DAYLIGHT : UTZFMT_TIME_TYPE_STANDARD; 1.1299 + } 1.1300 + return name; 1.1301 +} 1.1302 + 1.1303 +const TimeZoneGenericNames* 1.1304 +TimeZoneFormat::getTimeZoneGenericNames(UErrorCode& status) const { 1.1305 + if (U_FAILURE(status)) { 1.1306 + return NULL; 1.1307 + } 1.1308 + 1.1309 + umtx_lock(&gLock); 1.1310 + if (fTimeZoneGenericNames == NULL) { 1.1311 + TimeZoneFormat *nonConstThis = const_cast<TimeZoneFormat *>(this); 1.1312 + nonConstThis->fTimeZoneGenericNames = TimeZoneGenericNames::createInstance(fLocale, status); 1.1313 + } 1.1314 + umtx_unlock(&gLock); 1.1315 + 1.1316 + return fTimeZoneGenericNames; 1.1317 +} 1.1318 + 1.1319 +UnicodeString& 1.1320 +TimeZoneFormat::formatExemplarLocation(const TimeZone& tz, UnicodeString& name) const { 1.1321 + UnicodeString location; 1.1322 + const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(tz); 1.1323 + 1.1324 + if (canonicalID) { 1.1325 + fTimeZoneNames->getExemplarLocationName(UnicodeString(canonicalID), location); 1.1326 + } 1.1327 + if (location.length() > 0) { 1.1328 + name.setTo(location); 1.1329 + } else { 1.1330 + // Use "unknown" location 1.1331 + fTimeZoneNames->getExemplarLocationName(UnicodeString(UNKNOWN_ZONE_ID), location); 1.1332 + if (location.length() > 0) { 1.1333 + name.setTo(location); 1.1334 + } else { 1.1335 + // last resort 1.1336 + name.setTo(UNKNOWN_LOCATION, -1); 1.1337 + } 1.1338 + } 1.1339 + return name; 1.1340 +} 1.1341 + 1.1342 + 1.1343 +// ------------------------------------------------------------------ 1.1344 +// Zone offset format and parse 1.1345 + 1.1346 +UnicodeString& 1.1347 +TimeZoneFormat::formatOffsetISO8601Basic(int32_t offset, UBool useUtcIndicator, UBool isShort, UBool ignoreSeconds, 1.1348 + UnicodeString& result, UErrorCode& status) const { 1.1349 + return formatOffsetISO8601(offset, TRUE, useUtcIndicator, isShort, ignoreSeconds, result, status); 1.1350 +} 1.1351 + 1.1352 +UnicodeString& 1.1353 +TimeZoneFormat::formatOffsetISO8601Extended(int32_t offset, UBool useUtcIndicator, UBool isShort, UBool ignoreSeconds, 1.1354 + UnicodeString& result, UErrorCode& status) const { 1.1355 + return formatOffsetISO8601(offset, FALSE, useUtcIndicator, isShort, ignoreSeconds, result, status); 1.1356 +} 1.1357 + 1.1358 +UnicodeString& 1.1359 +TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset, UnicodeString& result, UErrorCode& status) const { 1.1360 + return formatOffsetLocalizedGMT(offset, FALSE, result, status); 1.1361 +} 1.1362 + 1.1363 +UnicodeString& 1.1364 +TimeZoneFormat::formatOffsetShortLocalizedGMT(int32_t offset, UnicodeString& result, UErrorCode& status) const { 1.1365 + return formatOffsetLocalizedGMT(offset, TRUE, result, status); 1.1366 +} 1.1367 + 1.1368 +int32_t 1.1369 +TimeZoneFormat::parseOffsetISO8601(const UnicodeString& text, ParsePosition& pos) const { 1.1370 + return parseOffsetISO8601(text, pos, FALSE); 1.1371 +} 1.1372 + 1.1373 +int32_t 1.1374 +TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString& text, ParsePosition& pos) const { 1.1375 + return parseOffsetLocalizedGMT(text, pos, FALSE, NULL); 1.1376 +} 1.1377 + 1.1378 +int32_t 1.1379 +TimeZoneFormat::parseOffsetShortLocalizedGMT(const UnicodeString& text, ParsePosition& pos) const { 1.1380 + return parseOffsetLocalizedGMT(text, pos, TRUE, NULL); 1.1381 +} 1.1382 + 1.1383 +// ------------------------------------------------------------------ 1.1384 +// Private zone offset format/parse implementation 1.1385 + 1.1386 +UnicodeString& 1.1387 +TimeZoneFormat::formatOffsetISO8601(int32_t offset, UBool isBasic, UBool useUtcIndicator, 1.1388 + UBool isShort, UBool ignoreSeconds, UnicodeString& result, UErrorCode& status) const { 1.1389 + if (U_FAILURE(status)) { 1.1390 + result.setToBogus(); 1.1391 + return result; 1.1392 + } 1.1393 + int32_t absOffset = offset < 0 ? -offset : offset; 1.1394 + if (useUtcIndicator && (absOffset < MILLIS_PER_SECOND || (ignoreSeconds && absOffset < MILLIS_PER_MINUTE))) { 1.1395 + result.setTo(ISO8601_UTC); 1.1396 + return result; 1.1397 + } 1.1398 + 1.1399 + OffsetFields minFields = isShort ? FIELDS_H : FIELDS_HM; 1.1400 + OffsetFields maxFields = ignoreSeconds ? FIELDS_HM : FIELDS_HMS; 1.1401 + UChar sep = isBasic ? 0 : ISO8601_SEP; 1.1402 + 1.1403 + // Note: FIELDS_HMS as maxFields is a CLDR/ICU extension. ISO 8601 specification does 1.1404 + // not support seconds field. 1.1405 + 1.1406 + if (absOffset >= MAX_OFFSET) { 1.1407 + result.setToBogus(); 1.1408 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.1409 + return result; 1.1410 + } 1.1411 + 1.1412 + int fields[3]; 1.1413 + fields[0] = absOffset / MILLIS_PER_HOUR; 1.1414 + absOffset = absOffset % MILLIS_PER_HOUR; 1.1415 + fields[1] = absOffset / MILLIS_PER_MINUTE; 1.1416 + absOffset = absOffset % MILLIS_PER_MINUTE; 1.1417 + fields[2] = absOffset / MILLIS_PER_SECOND; 1.1418 + 1.1419 + U_ASSERT(fields[0] >= 0 && fields[0] <= MAX_OFFSET_HOUR); 1.1420 + U_ASSERT(fields[1] >= 0 && fields[1] <= MAX_OFFSET_MINUTE); 1.1421 + U_ASSERT(fields[2] >= 0 && fields[2] <= MAX_OFFSET_SECOND); 1.1422 + 1.1423 + int32_t lastIdx = maxFields; 1.1424 + while (lastIdx > minFields) { 1.1425 + if (fields[lastIdx] != 0) { 1.1426 + break; 1.1427 + } 1.1428 + lastIdx--; 1.1429 + } 1.1430 + 1.1431 + UChar sign = PLUS; 1.1432 + if (offset < 0) { 1.1433 + // if all output fields are 0s, do not use negative sign 1.1434 + for (int32_t idx = 0; idx <= lastIdx; idx++) { 1.1435 + if (fields[idx] != 0) { 1.1436 + sign = MINUS; 1.1437 + break; 1.1438 + } 1.1439 + } 1.1440 + } 1.1441 + result.setTo(sign); 1.1442 + 1.1443 + for (int32_t idx = 0; idx <= lastIdx; idx++) { 1.1444 + if (sep && idx != 0) { 1.1445 + result.append(sep); 1.1446 + } 1.1447 + result.append((UChar)(0x0030 + fields[idx]/10)); 1.1448 + result.append((UChar)(0x0030 + fields[idx]%10)); 1.1449 + } 1.1450 + 1.1451 + return result; 1.1452 +} 1.1453 + 1.1454 +UnicodeString& 1.1455 +TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset, UBool isShort, UnicodeString& result, UErrorCode& status) const { 1.1456 + if (U_FAILURE(status)) { 1.1457 + result.setToBogus(); 1.1458 + return result; 1.1459 + } 1.1460 + if (offset <= -MAX_OFFSET || offset >= MAX_OFFSET) { 1.1461 + result.setToBogus(); 1.1462 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.1463 + return result; 1.1464 + } 1.1465 + 1.1466 + if (offset == 0) { 1.1467 + result.setTo(fGMTZeroFormat); 1.1468 + return result; 1.1469 + } 1.1470 + 1.1471 + UBool positive = TRUE; 1.1472 + if (offset < 0) { 1.1473 + offset = -offset; 1.1474 + positive = FALSE; 1.1475 + } 1.1476 + 1.1477 + int32_t offsetH = offset / MILLIS_PER_HOUR; 1.1478 + offset = offset % MILLIS_PER_HOUR; 1.1479 + int32_t offsetM = offset / MILLIS_PER_MINUTE; 1.1480 + offset = offset % MILLIS_PER_MINUTE; 1.1481 + int32_t offsetS = offset / MILLIS_PER_SECOND; 1.1482 + 1.1483 + U_ASSERT(offsetH <= MAX_OFFSET_HOUR && offsetM <= MAX_OFFSET_MINUTE && offsetS <= MAX_OFFSET_SECOND); 1.1484 + 1.1485 + const UVector* offsetPatternItems = NULL; 1.1486 + if (positive) { 1.1487 + if (offsetS != 0) { 1.1488 + offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_HMS]; 1.1489 + } else if (offsetM != 0 || !isShort) { 1.1490 + offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_HM]; 1.1491 + } else { 1.1492 + offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_H]; 1.1493 + } 1.1494 + } else { 1.1495 + if (offsetS != 0) { 1.1496 + offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_HMS]; 1.1497 + } else if (offsetM != 0 || !isShort) { 1.1498 + offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_HM]; 1.1499 + } else { 1.1500 + offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_H]; 1.1501 + } 1.1502 + } 1.1503 + 1.1504 + U_ASSERT(offsetPatternItems != NULL); 1.1505 + 1.1506 + // Building the GMT format string 1.1507 + result.setTo(fGMTPatternPrefix); 1.1508 + 1.1509 + for (int32_t i = 0; i < offsetPatternItems->size(); i++) { 1.1510 + const GMTOffsetField* item = (GMTOffsetField*)offsetPatternItems->elementAt(i); 1.1511 + GMTOffsetField::FieldType type = item->getType(); 1.1512 + 1.1513 + switch (type) { 1.1514 + case GMTOffsetField::TEXT: 1.1515 + result.append(item->getPatternText(), -1); 1.1516 + break; 1.1517 + 1.1518 + case GMTOffsetField::HOUR: 1.1519 + appendOffsetDigits(result, offsetH, (isShort ? 1 : 2)); 1.1520 + break; 1.1521 + 1.1522 + case GMTOffsetField::MINUTE: 1.1523 + appendOffsetDigits(result, offsetM, 2); 1.1524 + break; 1.1525 + 1.1526 + case GMTOffsetField::SECOND: 1.1527 + appendOffsetDigits(result, offsetS, 2); 1.1528 + break; 1.1529 + } 1.1530 + } 1.1531 + 1.1532 + result.append(fGMTPatternSuffix); 1.1533 + return result; 1.1534 +} 1.1535 + 1.1536 +int32_t 1.1537 +TimeZoneFormat::parseOffsetISO8601(const UnicodeString& text, ParsePosition& pos, UBool extendedOnly, UBool* hasDigitOffset /* = NULL */) const { 1.1538 + if (hasDigitOffset) { 1.1539 + *hasDigitOffset = FALSE; 1.1540 + } 1.1541 + int32_t start = pos.getIndex(); 1.1542 + if (start >= text.length()) { 1.1543 + pos.setErrorIndex(start); 1.1544 + return 0; 1.1545 + } 1.1546 + 1.1547 + UChar firstChar = text.charAt(start); 1.1548 + if (firstChar == ISO8601_UTC || firstChar == (UChar)(ISO8601_UTC + 0x20)) { 1.1549 + // "Z" (or "z") - indicates UTC 1.1550 + pos.setIndex(start + 1); 1.1551 + return 0; 1.1552 + } 1.1553 + 1.1554 + int32_t sign = 1; 1.1555 + if (firstChar == PLUS) { 1.1556 + sign = 1; 1.1557 + } else if (firstChar == MINUS) { 1.1558 + sign = -1; 1.1559 + } else { 1.1560 + // Not an ISO 8601 offset string 1.1561 + pos.setErrorIndex(start); 1.1562 + return 0; 1.1563 + } 1.1564 + ParsePosition posOffset(start + 1); 1.1565 + int32_t offset = parseAsciiOffsetFields(text, posOffset, ISO8601_SEP, FIELDS_H, FIELDS_HMS); 1.1566 + if (posOffset.getErrorIndex() == -1 && !extendedOnly && (posOffset.getIndex() - start <= 3)) { 1.1567 + // If the text is successfully parsed as extended format with the options above, it can be also parsed 1.1568 + // as basic format. For example, "0230" can be parsed as offset 2:00 (only first digits are valid for 1.1569 + // extended format), but it can be parsed as offset 2:30 with basic format. We use longer result. 1.1570 + ParsePosition posBasic(start + 1); 1.1571 + int32_t tmpOffset = parseAbuttingAsciiOffsetFields(text, posBasic, FIELDS_H, FIELDS_HMS, FALSE); 1.1572 + if (posBasic.getErrorIndex() == -1 && posBasic.getIndex() > posOffset.getIndex()) { 1.1573 + offset = tmpOffset; 1.1574 + posOffset.setIndex(posBasic.getIndex()); 1.1575 + } 1.1576 + } 1.1577 + 1.1578 + if (posOffset.getErrorIndex() != -1) { 1.1579 + pos.setErrorIndex(start); 1.1580 + return 0; 1.1581 + } 1.1582 + 1.1583 + pos.setIndex(posOffset.getIndex()); 1.1584 + if (hasDigitOffset) { 1.1585 + *hasDigitOffset = TRUE; 1.1586 + } 1.1587 + return sign * offset; 1.1588 +} 1.1589 + 1.1590 +int32_t 1.1591 +TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString& text, ParsePosition& pos, UBool isShort, UBool* hasDigitOffset) const { 1.1592 + int32_t start = pos.getIndex(); 1.1593 + int32_t offset = 0; 1.1594 + int32_t parsedLength = 0; 1.1595 + 1.1596 + if (hasDigitOffset) { 1.1597 + *hasDigitOffset = FALSE; 1.1598 + } 1.1599 + 1.1600 + offset = parseOffsetLocalizedGMTPattern(text, start, isShort, parsedLength); 1.1601 + 1.1602 + // For now, parseOffsetLocalizedGMTPattern handles both long and short 1.1603 + // formats, no matter isShort is true or false. This might be changed in future 1.1604 + // when strict parsing is necessary, or different set of patterns are used for 1.1605 + // short/long formats. 1.1606 +#if 0 1.1607 + if (parsedLength == 0) { 1.1608 + offset = parseOffsetLocalizedGMTPattern(text, start, !isShort, parsedLength); 1.1609 + } 1.1610 +#endif 1.1611 + 1.1612 + if (parsedLength > 0) { 1.1613 + if (hasDigitOffset) { 1.1614 + *hasDigitOffset = TRUE; 1.1615 + } 1.1616 + pos.setIndex(start + parsedLength); 1.1617 + return offset; 1.1618 + } 1.1619 + 1.1620 + // Try the default patterns 1.1621 + offset = parseOffsetDefaultLocalizedGMT(text, start, parsedLength); 1.1622 + if (parsedLength > 0) { 1.1623 + if (hasDigitOffset) { 1.1624 + *hasDigitOffset = TRUE; 1.1625 + } 1.1626 + pos.setIndex(start + parsedLength); 1.1627 + return offset; 1.1628 + } 1.1629 + 1.1630 + // Check if this is a GMT zero format 1.1631 + if (text.caseCompare(start, fGMTZeroFormat.length(), fGMTZeroFormat, 0) == 0) { 1.1632 + pos.setIndex(start + fGMTZeroFormat.length()); 1.1633 + return 0; 1.1634 + } 1.1635 + 1.1636 + // Check if this is a default GMT zero format 1.1637 + for (int32_t i = 0; ALT_GMT_STRINGS[i][0] != 0; i++) { 1.1638 + const UChar* defGMTZero = ALT_GMT_STRINGS[i]; 1.1639 + int32_t defGMTZeroLen = u_strlen(defGMTZero); 1.1640 + if (text.caseCompare(start, defGMTZeroLen, defGMTZero, 0) == 0) { 1.1641 + pos.setIndex(start + defGMTZeroLen); 1.1642 + return 0; 1.1643 + } 1.1644 + } 1.1645 + 1.1646 + // Nothing matched 1.1647 + pos.setErrorIndex(start); 1.1648 + return 0; 1.1649 +} 1.1650 + 1.1651 +int32_t 1.1652 +TimeZoneFormat::parseOffsetLocalizedGMTPattern(const UnicodeString& text, int32_t start, UBool /*isShort*/, int32_t& parsedLen) const { 1.1653 + int32_t idx = start; 1.1654 + int32_t offset = 0; 1.1655 + UBool parsed = FALSE; 1.1656 + 1.1657 + do { 1.1658 + // Prefix part 1.1659 + int32_t len = fGMTPatternPrefix.length(); 1.1660 + if (len > 0 && text.caseCompare(idx, len, fGMTPatternPrefix, 0) != 0) { 1.1661 + // prefix match failed 1.1662 + break; 1.1663 + } 1.1664 + idx += len; 1.1665 + 1.1666 + // Offset part 1.1667 + offset = parseOffsetFields(text, idx, FALSE, len); 1.1668 + if (len == 0) { 1.1669 + // offset field match failed 1.1670 + break; 1.1671 + } 1.1672 + idx += len; 1.1673 + 1.1674 + len = fGMTPatternSuffix.length(); 1.1675 + if (len > 0 && text.caseCompare(idx, len, fGMTPatternSuffix, 0) != 0) { 1.1676 + // no suffix match 1.1677 + break; 1.1678 + } 1.1679 + idx += len; 1.1680 + parsed = TRUE; 1.1681 + } while (FALSE); 1.1682 + 1.1683 + parsedLen = parsed ? idx - start : 0; 1.1684 + return offset; 1.1685 +} 1.1686 + 1.1687 +int32_t 1.1688 +TimeZoneFormat::parseOffsetFields(const UnicodeString& text, int32_t start, UBool /*isShort*/, int32_t& parsedLen) const { 1.1689 + int32_t outLen = 0; 1.1690 + int32_t offset = 0; 1.1691 + int32_t sign = 1; 1.1692 + 1.1693 + parsedLen = 0; 1.1694 + 1.1695 + int32_t offsetH, offsetM, offsetS; 1.1696 + offsetH = offsetM = offsetS = 0; 1.1697 + 1.1698 + for (int32_t patidx = 0; PARSE_GMT_OFFSET_TYPES[patidx] >= 0; patidx++) { 1.1699 + int32_t gmtPatType = PARSE_GMT_OFFSET_TYPES[patidx]; 1.1700 + UVector* items = fGMTOffsetPatternItems[gmtPatType]; 1.1701 + U_ASSERT(items != NULL); 1.1702 + 1.1703 + outLen = parseOffsetFieldsWithPattern(text, start, items, FALSE, offsetH, offsetM, offsetS); 1.1704 + if (outLen > 0) { 1.1705 + sign = (gmtPatType == UTZFMT_PAT_POSITIVE_H || gmtPatType == UTZFMT_PAT_POSITIVE_HM || gmtPatType == UTZFMT_PAT_POSITIVE_HMS) ? 1.1706 + 1 : -1; 1.1707 + break; 1.1708 + } 1.1709 + } 1.1710 + 1.1711 + if (outLen > 0 && fAbuttingOffsetHoursAndMinutes) { 1.1712 + // When hours field is sabutting minutes field, 1.1713 + // the parse result above may not be appropriate. 1.1714 + // For example, "01020" is parsed as 01:02: above, 1.1715 + // but it should be parsed as 00:10:20. 1.1716 + int32_t tmpLen = 0; 1.1717 + int32_t tmpSign = 1; 1.1718 + int32_t tmpH, tmpM, tmpS; 1.1719 + 1.1720 + for (int32_t patidx = 0; PARSE_GMT_OFFSET_TYPES[patidx] >= 0; patidx++) { 1.1721 + int32_t gmtPatType = PARSE_GMT_OFFSET_TYPES[patidx]; 1.1722 + UVector* items = fGMTOffsetPatternItems[gmtPatType]; 1.1723 + U_ASSERT(items != NULL); 1.1724 + 1.1725 + // forcing parse to use single hour digit 1.1726 + tmpLen = parseOffsetFieldsWithPattern(text, start, items, TRUE, tmpH, tmpM, tmpS); 1.1727 + if (tmpLen > 0) { 1.1728 + tmpSign = (gmtPatType == UTZFMT_PAT_POSITIVE_H || gmtPatType == UTZFMT_PAT_POSITIVE_HM || gmtPatType == UTZFMT_PAT_POSITIVE_HMS) ? 1.1729 + 1 : -1; 1.1730 + break; 1.1731 + } 1.1732 + } 1.1733 + if (tmpLen > outLen) { 1.1734 + // Better parse result with single hour digit 1.1735 + outLen = tmpLen; 1.1736 + sign = tmpSign; 1.1737 + offsetH = tmpH; 1.1738 + offsetM = tmpM; 1.1739 + offsetS = tmpS; 1.1740 + } 1.1741 + } 1.1742 + 1.1743 + if (outLen > 0) { 1.1744 + offset = ((((offsetH * 60) + offsetM) * 60) + offsetS) * 1000 * sign; 1.1745 + parsedLen = outLen; 1.1746 + } 1.1747 + 1.1748 + return offset; 1.1749 +} 1.1750 + 1.1751 +int32_t 1.1752 +TimeZoneFormat::parseOffsetFieldsWithPattern(const UnicodeString& text, int32_t start, 1.1753 + UVector* patternItems, UBool forceSingleHourDigit, int32_t& hour, int32_t& min, int32_t& sec) const { 1.1754 + UBool failed = FALSE; 1.1755 + int32_t offsetH, offsetM, offsetS; 1.1756 + offsetH = offsetM = offsetS = 0; 1.1757 + int32_t idx = start; 1.1758 + 1.1759 + for (int32_t i = 0; i < patternItems->size(); i++) { 1.1760 + int32_t len = 0; 1.1761 + const GMTOffsetField* field = (const GMTOffsetField*)patternItems->elementAt(i); 1.1762 + GMTOffsetField::FieldType fieldType = field->getType(); 1.1763 + if (fieldType == GMTOffsetField::TEXT) { 1.1764 + const UChar* patStr = field->getPatternText(); 1.1765 + len = u_strlen(patStr); 1.1766 + if (text.caseCompare(idx, len, patStr, 0) != 0) { 1.1767 + failed = TRUE; 1.1768 + break; 1.1769 + } 1.1770 + idx += len; 1.1771 + } else { 1.1772 + if (fieldType == GMTOffsetField::HOUR) { 1.1773 + uint8_t maxDigits = forceSingleHourDigit ? 1 : 2; 1.1774 + offsetH = parseOffsetFieldWithLocalizedDigits(text, idx, 1, maxDigits, 0, MAX_OFFSET_HOUR, len); 1.1775 + } else if (fieldType == GMTOffsetField::MINUTE) { 1.1776 + offsetM = parseOffsetFieldWithLocalizedDigits(text, idx, 2, 2, 0, MAX_OFFSET_MINUTE, len); 1.1777 + } else if (fieldType == GMTOffsetField::SECOND) { 1.1778 + offsetS = parseOffsetFieldWithLocalizedDigits(text, idx, 2, 2, 0, MAX_OFFSET_SECOND, len); 1.1779 + } 1.1780 + 1.1781 + if (len == 0) { 1.1782 + failed = TRUE; 1.1783 + break; 1.1784 + } 1.1785 + idx += len; 1.1786 + } 1.1787 + } 1.1788 + 1.1789 + if (failed) { 1.1790 + hour = min = sec = 0; 1.1791 + return 0; 1.1792 + } 1.1793 + 1.1794 + hour = offsetH; 1.1795 + min = offsetM; 1.1796 + sec = offsetS; 1.1797 + 1.1798 + return idx - start; 1.1799 +} 1.1800 + 1.1801 +int32_t 1.1802 +TimeZoneFormat::parseAbuttingOffsetFields(const UnicodeString& text, int32_t start, int32_t& parsedLen) const { 1.1803 + int32_t digits[MAX_OFFSET_DIGITS]; 1.1804 + int32_t parsed[MAX_OFFSET_DIGITS]; // accumulative offsets 1.1805 + 1.1806 + // Parse digits into int[] 1.1807 + int32_t idx = start; 1.1808 + int32_t len = 0; 1.1809 + int32_t numDigits = 0; 1.1810 + for (int32_t i = 0; i < MAX_OFFSET_DIGITS; i++) { 1.1811 + digits[i] = parseSingleLocalizedDigit(text, idx, len); 1.1812 + if (digits[i] < 0) { 1.1813 + break; 1.1814 + } 1.1815 + idx += len; 1.1816 + parsed[i] = idx - start; 1.1817 + numDigits++; 1.1818 + } 1.1819 + 1.1820 + if (numDigits == 0) { 1.1821 + parsedLen = 0; 1.1822 + return 0; 1.1823 + } 1.1824 + 1.1825 + int32_t offset = 0; 1.1826 + while (numDigits > 0) { 1.1827 + int32_t hour = 0; 1.1828 + int32_t min = 0; 1.1829 + int32_t sec = 0; 1.1830 + 1.1831 + U_ASSERT(numDigits > 0 && numDigits <= MAX_OFFSET_DIGITS); 1.1832 + switch (numDigits) { 1.1833 + case 1: // H 1.1834 + hour = digits[0]; 1.1835 + break; 1.1836 + case 2: // HH 1.1837 + hour = digits[0] * 10 + digits[1]; 1.1838 + break; 1.1839 + case 3: // Hmm 1.1840 + hour = digits[0]; 1.1841 + min = digits[1] * 10 + digits[2]; 1.1842 + break; 1.1843 + case 4: // HHmm 1.1844 + hour = digits[0] * 10 + digits[1]; 1.1845 + min = digits[2] * 10 + digits[3]; 1.1846 + break; 1.1847 + case 5: // Hmmss 1.1848 + hour = digits[0]; 1.1849 + min = digits[1] * 10 + digits[2]; 1.1850 + sec = digits[3] * 10 + digits[4]; 1.1851 + break; 1.1852 + case 6: // HHmmss 1.1853 + hour = digits[0] * 10 + digits[1]; 1.1854 + min = digits[2] * 10 + digits[3]; 1.1855 + sec = digits[4] * 10 + digits[5]; 1.1856 + break; 1.1857 + } 1.1858 + if (hour <= MAX_OFFSET_HOUR && min <= MAX_OFFSET_MINUTE && sec <= MAX_OFFSET_SECOND) { 1.1859 + // found a valid combination 1.1860 + offset = hour * MILLIS_PER_HOUR + min * MILLIS_PER_MINUTE + sec * MILLIS_PER_SECOND; 1.1861 + parsedLen = parsed[numDigits - 1]; 1.1862 + break; 1.1863 + } 1.1864 + numDigits--; 1.1865 + } 1.1866 + return offset; 1.1867 +} 1.1868 + 1.1869 +int32_t 1.1870 +TimeZoneFormat::parseOffsetDefaultLocalizedGMT(const UnicodeString& text, int start, int32_t& parsedLen) const { 1.1871 + int32_t idx = start; 1.1872 + int32_t offset = 0; 1.1873 + int32_t parsed = 0; 1.1874 + 1.1875 + do { 1.1876 + // check global default GMT alternatives 1.1877 + int32_t gmtLen = 0; 1.1878 + 1.1879 + for (int32_t i = 0; ALT_GMT_STRINGS[i][0] != 0; i++) { 1.1880 + const UChar* gmt = ALT_GMT_STRINGS[i]; 1.1881 + int32_t len = u_strlen(gmt); 1.1882 + if (text.caseCompare(start, len, gmt, 0) == 0) { 1.1883 + gmtLen = len; 1.1884 + break; 1.1885 + } 1.1886 + } 1.1887 + if (gmtLen == 0) { 1.1888 + break; 1.1889 + } 1.1890 + idx += gmtLen; 1.1891 + 1.1892 + // offset needs a sign char and a digit at minimum 1.1893 + if (idx + 1 >= text.length()) { 1.1894 + break; 1.1895 + } 1.1896 + 1.1897 + // parse sign 1.1898 + int32_t sign = 1; 1.1899 + UChar c = text.charAt(idx); 1.1900 + if (c == PLUS) { 1.1901 + sign = 1; 1.1902 + } else if (c == MINUS) { 1.1903 + sign = -1; 1.1904 + } else { 1.1905 + break; 1.1906 + } 1.1907 + idx++; 1.1908 + 1.1909 + // offset part 1.1910 + // try the default pattern with the separator first 1.1911 + int32_t lenWithSep = 0; 1.1912 + int32_t offsetWithSep = parseDefaultOffsetFields(text, idx, DEFAULT_GMT_OFFSET_SEP, lenWithSep); 1.1913 + if (lenWithSep == text.length() - idx) { 1.1914 + // maximum match 1.1915 + offset = offsetWithSep * sign; 1.1916 + idx += lenWithSep; 1.1917 + } else { 1.1918 + // try abutting field pattern 1.1919 + int32_t lenAbut = 0; 1.1920 + int32_t offsetAbut = parseAbuttingOffsetFields(text, idx, lenAbut); 1.1921 + 1.1922 + if (lenWithSep > lenAbut) { 1.1923 + offset = offsetWithSep * sign; 1.1924 + idx += lenWithSep; 1.1925 + } else { 1.1926 + offset = offsetAbut * sign; 1.1927 + idx += lenAbut; 1.1928 + } 1.1929 + } 1.1930 + parsed = idx - start; 1.1931 + } while (false); 1.1932 + 1.1933 + parsedLen = parsed; 1.1934 + return offset; 1.1935 +} 1.1936 + 1.1937 +int32_t 1.1938 +TimeZoneFormat::parseDefaultOffsetFields(const UnicodeString& text, int32_t start, UChar separator, int32_t& parsedLen) const { 1.1939 + int32_t max = text.length(); 1.1940 + int32_t idx = start; 1.1941 + int32_t len = 0; 1.1942 + int32_t hour = 0, min = 0, sec = 0; 1.1943 + 1.1944 + parsedLen = 0; 1.1945 + 1.1946 + do { 1.1947 + hour = parseOffsetFieldWithLocalizedDigits(text, idx, 1, 2, 0, MAX_OFFSET_HOUR, len); 1.1948 + if (len == 0) { 1.1949 + break; 1.1950 + } 1.1951 + idx += len; 1.1952 + 1.1953 + if (idx + 1 < max && text.charAt(idx) == separator) { 1.1954 + min = parseOffsetFieldWithLocalizedDigits(text, idx + 1, 2, 2, 0, MAX_OFFSET_MINUTE, len); 1.1955 + if (len == 0) { 1.1956 + break; 1.1957 + } 1.1958 + idx += (1 + len); 1.1959 + 1.1960 + if (idx + 1 < max && text.charAt(idx) == separator) { 1.1961 + sec = parseOffsetFieldWithLocalizedDigits(text, idx + 1, 2, 2, 0, MAX_OFFSET_SECOND, len); 1.1962 + if (len == 0) { 1.1963 + break; 1.1964 + } 1.1965 + idx += (1 + len); 1.1966 + } 1.1967 + } 1.1968 + } while (FALSE); 1.1969 + 1.1970 + if (idx == start) { 1.1971 + return 0; 1.1972 + } 1.1973 + 1.1974 + parsedLen = idx - start; 1.1975 + return hour * MILLIS_PER_HOUR + min * MILLIS_PER_MINUTE + sec * MILLIS_PER_SECOND; 1.1976 +} 1.1977 + 1.1978 +int32_t 1.1979 +TimeZoneFormat::parseOffsetFieldWithLocalizedDigits(const UnicodeString& text, int32_t start, uint8_t minDigits, uint8_t maxDigits, uint16_t minVal, uint16_t maxVal, int32_t& parsedLen) const { 1.1980 + parsedLen = 0; 1.1981 + 1.1982 + int32_t decVal = 0; 1.1983 + int32_t numDigits = 0; 1.1984 + int32_t idx = start; 1.1985 + int32_t digitLen = 0; 1.1986 + 1.1987 + while (idx < text.length() && numDigits < maxDigits) { 1.1988 + int32_t digit = parseSingleLocalizedDigit(text, idx, digitLen); 1.1989 + if (digit < 0) { 1.1990 + break; 1.1991 + } 1.1992 + int32_t tmpVal = decVal * 10 + digit; 1.1993 + if (tmpVal > maxVal) { 1.1994 + break; 1.1995 + } 1.1996 + decVal = tmpVal; 1.1997 + numDigits++; 1.1998 + idx += digitLen; 1.1999 + } 1.2000 + 1.2001 + // Note: maxVal is checked in the while loop 1.2002 + if (numDigits < minDigits || decVal < minVal) { 1.2003 + decVal = -1; 1.2004 + numDigits = 0; 1.2005 + } else { 1.2006 + parsedLen = idx - start; 1.2007 + } 1.2008 + 1.2009 + return decVal; 1.2010 +} 1.2011 + 1.2012 +int32_t 1.2013 +TimeZoneFormat::parseSingleLocalizedDigit(const UnicodeString& text, int32_t start, int32_t& len) const { 1.2014 + int32_t digit = -1; 1.2015 + len = 0; 1.2016 + if (start < text.length()) { 1.2017 + UChar32 cp = text.char32At(start); 1.2018 + 1.2019 + // First, try digits configured for this instance 1.2020 + for (int32_t i = 0; i < 10; i++) { 1.2021 + if (cp == fGMTOffsetDigits[i]) { 1.2022 + digit = i; 1.2023 + break; 1.2024 + } 1.2025 + } 1.2026 + // If failed, check if this is a Unicode digit 1.2027 + if (digit < 0) { 1.2028 + int32_t tmp = u_charDigitValue(cp); 1.2029 + digit = (tmp >= 0 && tmp <= 9) ? tmp : -1; 1.2030 + } 1.2031 + 1.2032 + if (digit >= 0) { 1.2033 + int32_t next = text.moveIndex32(start, 1); 1.2034 + len = next - start; 1.2035 + } 1.2036 + } 1.2037 + return digit; 1.2038 +} 1.2039 + 1.2040 +UnicodeString& 1.2041 +TimeZoneFormat::formatOffsetWithAsciiDigits(int32_t offset, UChar sep, OffsetFields minFields, OffsetFields maxFields, UnicodeString& result) { 1.2042 + U_ASSERT(maxFields >= minFields); 1.2043 + U_ASSERT(offset > -MAX_OFFSET && offset < MAX_OFFSET); 1.2044 + 1.2045 + UChar sign = PLUS; 1.2046 + if (offset < 0) { 1.2047 + sign = MINUS; 1.2048 + offset = -offset; 1.2049 + } 1.2050 + result.setTo(sign); 1.2051 + 1.2052 + int fields[3]; 1.2053 + fields[0] = offset / MILLIS_PER_HOUR; 1.2054 + offset = offset % MILLIS_PER_HOUR; 1.2055 + fields[1] = offset / MILLIS_PER_MINUTE; 1.2056 + offset = offset % MILLIS_PER_MINUTE; 1.2057 + fields[2] = offset / MILLIS_PER_SECOND; 1.2058 + 1.2059 + U_ASSERT(fields[0] >= 0 && fields[0] <= MAX_OFFSET_HOUR); 1.2060 + U_ASSERT(fields[1] >= 0 && fields[1] <= MAX_OFFSET_MINUTE); 1.2061 + U_ASSERT(fields[2] >= 0 && fields[2] <= MAX_OFFSET_SECOND); 1.2062 + 1.2063 + int32_t lastIdx = maxFields; 1.2064 + while (lastIdx > minFields) { 1.2065 + if (fields[lastIdx] != 0) { 1.2066 + break; 1.2067 + } 1.2068 + lastIdx--; 1.2069 + } 1.2070 + 1.2071 + for (int32_t idx = 0; idx <= lastIdx; idx++) { 1.2072 + if (sep && idx != 0) { 1.2073 + result.append(sep); 1.2074 + } 1.2075 + result.append((UChar)(0x0030 + fields[idx]/10)); 1.2076 + result.append((UChar)(0x0030 + fields[idx]%10)); 1.2077 + } 1.2078 + 1.2079 + return result; 1.2080 +} 1.2081 + 1.2082 +int32_t 1.2083 +TimeZoneFormat::parseAbuttingAsciiOffsetFields(const UnicodeString& text, ParsePosition& pos, OffsetFields minFields, OffsetFields maxFields, UBool fixedHourWidth) { 1.2084 + int32_t start = pos.getIndex(); 1.2085 + 1.2086 + int32_t minDigits = 2 * (minFields + 1) - (fixedHourWidth ? 0 : 1); 1.2087 + int32_t maxDigits = 2 * (maxFields + 1); 1.2088 + 1.2089 + U_ASSERT(maxDigits <= MAX_OFFSET_DIGITS); 1.2090 + 1.2091 + int32_t digits[MAX_OFFSET_DIGITS] = {}; 1.2092 + int32_t numDigits = 0; 1.2093 + int32_t idx = start; 1.2094 + while (numDigits < maxDigits && idx < text.length()) { 1.2095 + UChar uch = text.charAt(idx); 1.2096 + int32_t digit = DIGIT_VAL(uch); 1.2097 + if (digit < 0) { 1.2098 + break; 1.2099 + } 1.2100 + digits[numDigits] = digit; 1.2101 + numDigits++; 1.2102 + idx++; 1.2103 + } 1.2104 + 1.2105 + if (fixedHourWidth && (numDigits & 1)) { 1.2106 + // Fixed digits, so the number of digits must be even number. Truncating. 1.2107 + numDigits--; 1.2108 + } 1.2109 + 1.2110 + if (numDigits < minDigits) { 1.2111 + pos.setErrorIndex(start); 1.2112 + return 0; 1.2113 + } 1.2114 + 1.2115 + int32_t hour = 0, min = 0, sec = 0; 1.2116 + UBool bParsed = FALSE; 1.2117 + while (numDigits >= minDigits) { 1.2118 + switch (numDigits) { 1.2119 + case 1: //H 1.2120 + hour = digits[0]; 1.2121 + break; 1.2122 + case 2: //HH 1.2123 + hour = digits[0] * 10 + digits[1]; 1.2124 + break; 1.2125 + case 3: //Hmm 1.2126 + hour = digits[0]; 1.2127 + min = digits[1] * 10 + digits[2]; 1.2128 + break; 1.2129 + case 4: //HHmm 1.2130 + hour = digits[0] * 10 + digits[1]; 1.2131 + min = digits[2] * 10 + digits[3]; 1.2132 + break; 1.2133 + case 5: //Hmmss 1.2134 + hour = digits[0]; 1.2135 + min = digits[1] * 10 + digits[2]; 1.2136 + sec = digits[3] * 10 + digits[4]; 1.2137 + break; 1.2138 + case 6: //HHmmss 1.2139 + hour = digits[0] * 10 + digits[1]; 1.2140 + min = digits[2] * 10 + digits[3]; 1.2141 + sec = digits[4] * 10 + digits[5]; 1.2142 + break; 1.2143 + } 1.2144 + 1.2145 + if (hour <= MAX_OFFSET_HOUR && min <= MAX_OFFSET_MINUTE && sec <= MAX_OFFSET_SECOND) { 1.2146 + // Successfully parsed 1.2147 + bParsed = true; 1.2148 + break; 1.2149 + } 1.2150 + 1.2151 + // Truncating 1.2152 + numDigits -= (fixedHourWidth ? 2 : 1); 1.2153 + hour = min = sec = 0; 1.2154 + } 1.2155 + 1.2156 + if (!bParsed) { 1.2157 + pos.setErrorIndex(start); 1.2158 + return 0; 1.2159 + } 1.2160 + pos.setIndex(start + numDigits); 1.2161 + return ((((hour * 60) + min) * 60) + sec) * 1000; 1.2162 +} 1.2163 + 1.2164 +int32_t 1.2165 +TimeZoneFormat::parseAsciiOffsetFields(const UnicodeString& text, ParsePosition& pos, UChar sep, OffsetFields minFields, OffsetFields maxFields) { 1.2166 + int32_t start = pos.getIndex(); 1.2167 + int32_t fieldVal[] = {0, 0, 0}; 1.2168 + int32_t fieldLen[] = {0, -1, -1}; 1.2169 + for (int32_t idx = start, fieldIdx = 0; idx < text.length() && fieldIdx <= maxFields; idx++) { 1.2170 + UChar c = text.charAt(idx); 1.2171 + if (c == sep) { 1.2172 + if (fieldIdx == 0) { 1.2173 + if (fieldLen[0] == 0) { 1.2174 + // no hours field 1.2175 + break; 1.2176 + } 1.2177 + // 1 digit hour, move to next field 1.2178 + } else { 1.2179 + if (fieldLen[fieldIdx] != -1) { 1.2180 + // premature minute or seconds field 1.2181 + break; 1.2182 + } 1.2183 + fieldLen[fieldIdx] = 0; 1.2184 + } 1.2185 + continue; 1.2186 + } else if (fieldLen[fieldIdx] == -1) { 1.2187 + // no separator after 2 digit field 1.2188 + break; 1.2189 + } 1.2190 + int32_t digit = DIGIT_VAL(c); 1.2191 + if (digit < 0) { 1.2192 + // not a digit 1.2193 + break; 1.2194 + } 1.2195 + fieldVal[fieldIdx] = fieldVal[fieldIdx] * 10 + digit; 1.2196 + fieldLen[fieldIdx]++; 1.2197 + if (fieldLen[fieldIdx] >= 2) { 1.2198 + // parsed 2 digits, move to next field 1.2199 + fieldIdx++; 1.2200 + } 1.2201 + } 1.2202 + 1.2203 + int32_t offset = 0; 1.2204 + int32_t parsedLen = 0; 1.2205 + int32_t parsedFields = -1; 1.2206 + do { 1.2207 + // hour 1.2208 + if (fieldLen[0] == 0) { 1.2209 + break; 1.2210 + } 1.2211 + if (fieldVal[0] > MAX_OFFSET_HOUR) { 1.2212 + offset = (fieldVal[0] / 10) * MILLIS_PER_HOUR; 1.2213 + parsedFields = FIELDS_H; 1.2214 + parsedLen = 1; 1.2215 + break; 1.2216 + } 1.2217 + offset = fieldVal[0] * MILLIS_PER_HOUR; 1.2218 + parsedLen = fieldLen[0]; 1.2219 + parsedFields = FIELDS_H; 1.2220 + 1.2221 + // minute 1.2222 + if (fieldLen[1] != 2 || fieldVal[1] > MAX_OFFSET_MINUTE) { 1.2223 + break; 1.2224 + } 1.2225 + offset += fieldVal[1] * MILLIS_PER_MINUTE; 1.2226 + parsedLen += (1 + fieldLen[1]); 1.2227 + parsedFields = FIELDS_HM; 1.2228 + 1.2229 + // second 1.2230 + if (fieldLen[2] != 2 || fieldVal[2] > MAX_OFFSET_SECOND) { 1.2231 + break; 1.2232 + } 1.2233 + offset += fieldVal[2] * MILLIS_PER_SECOND; 1.2234 + parsedLen += (1 + fieldLen[2]); 1.2235 + parsedFields = FIELDS_HMS; 1.2236 + } while (false); 1.2237 + 1.2238 + if (parsedFields < minFields) { 1.2239 + pos.setErrorIndex(start); 1.2240 + return 0; 1.2241 + } 1.2242 + 1.2243 + pos.setIndex(start + parsedLen); 1.2244 + return offset; 1.2245 +} 1.2246 + 1.2247 +void 1.2248 +TimeZoneFormat::appendOffsetDigits(UnicodeString& buf, int32_t n, uint8_t minDigits) const { 1.2249 + U_ASSERT(n >= 0 && n < 60); 1.2250 + int32_t numDigits = n >= 10 ? 2 : 1; 1.2251 + for (int32_t i = 0; i < minDigits - numDigits; i++) { 1.2252 + buf.append(fGMTOffsetDigits[0]); 1.2253 + } 1.2254 + if (numDigits == 2) { 1.2255 + buf.append(fGMTOffsetDigits[n / 10]); 1.2256 + } 1.2257 + buf.append(fGMTOffsetDigits[n % 10]); 1.2258 +} 1.2259 + 1.2260 +// ------------------------------------------------------------------ 1.2261 +// Private misc 1.2262 +void 1.2263 +TimeZoneFormat::initGMTPattern(const UnicodeString& gmtPattern, UErrorCode& status) { 1.2264 + if (U_FAILURE(status)) { 1.2265 + return; 1.2266 + } 1.2267 + // This implementation not perfect, but sufficient practically. 1.2268 + int32_t idx = gmtPattern.indexOf(ARG0, ARG0_LEN, 0); 1.2269 + if (idx < 0) { 1.2270 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.2271 + return; 1.2272 + } 1.2273 + fGMTPattern.setTo(gmtPattern); 1.2274 + unquote(gmtPattern.tempSubString(0, idx), fGMTPatternPrefix); 1.2275 + unquote(gmtPattern.tempSubString(idx + ARG0_LEN), fGMTPatternSuffix); 1.2276 +} 1.2277 + 1.2278 +UnicodeString& 1.2279 +TimeZoneFormat::unquote(const UnicodeString& pattern, UnicodeString& result) { 1.2280 + if (pattern.indexOf(SINGLEQUOTE) < 0) { 1.2281 + result.setTo(pattern); 1.2282 + return result; 1.2283 + } 1.2284 + result.remove(); 1.2285 + UBool isPrevQuote = FALSE; 1.2286 + UBool inQuote = FALSE; 1.2287 + for (int32_t i = 0; i < pattern.length(); i++) { 1.2288 + UChar c = pattern.charAt(i); 1.2289 + if (c == SINGLEQUOTE) { 1.2290 + if (isPrevQuote) { 1.2291 + result.append(c); 1.2292 + isPrevQuote = FALSE; 1.2293 + } else { 1.2294 + isPrevQuote = TRUE; 1.2295 + } 1.2296 + inQuote = !inQuote; 1.2297 + } else { 1.2298 + isPrevQuote = FALSE; 1.2299 + result.append(c); 1.2300 + } 1.2301 + } 1.2302 + return result; 1.2303 +} 1.2304 + 1.2305 +UVector* 1.2306 +TimeZoneFormat::parseOffsetPattern(const UnicodeString& pattern, OffsetFields required, UErrorCode& status) { 1.2307 + if (U_FAILURE(status)) { 1.2308 + return NULL; 1.2309 + } 1.2310 + UVector* result = new UVector(deleteGMTOffsetField, NULL, status); 1.2311 + if (result == NULL) { 1.2312 + status = U_MEMORY_ALLOCATION_ERROR; 1.2313 + return NULL; 1.2314 + } 1.2315 + 1.2316 + int32_t checkBits = 0; 1.2317 + UBool isPrevQuote = FALSE; 1.2318 + UBool inQuote = FALSE; 1.2319 + UnicodeString text; 1.2320 + GMTOffsetField::FieldType itemType = GMTOffsetField::TEXT; 1.2321 + int32_t itemLength = 1; 1.2322 + 1.2323 + for (int32_t i = 0; i < pattern.length(); i++) { 1.2324 + UChar ch = pattern.charAt(i); 1.2325 + if (ch == SINGLEQUOTE) { 1.2326 + if (isPrevQuote) { 1.2327 + text.append(SINGLEQUOTE); 1.2328 + isPrevQuote = FALSE; 1.2329 + } else { 1.2330 + isPrevQuote = TRUE; 1.2331 + if (itemType != GMTOffsetField::TEXT) { 1.2332 + if (GMTOffsetField::isValid(itemType, itemLength)) { 1.2333 + GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, (uint8_t)itemLength, status); 1.2334 + result->addElement(fld, status); 1.2335 + if (U_FAILURE(status)) { 1.2336 + break; 1.2337 + } 1.2338 + } else { 1.2339 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.2340 + break; 1.2341 + } 1.2342 + itemType = GMTOffsetField::TEXT; 1.2343 + } 1.2344 + } 1.2345 + inQuote = !inQuote; 1.2346 + } else { 1.2347 + isPrevQuote = FALSE; 1.2348 + if (inQuote) { 1.2349 + text.append(ch); 1.2350 + } else { 1.2351 + GMTOffsetField::FieldType tmpType = GMTOffsetField::getTypeByLetter(ch); 1.2352 + if (tmpType != GMTOffsetField::TEXT) { 1.2353 + // an offset time pattern character 1.2354 + if (tmpType == itemType) { 1.2355 + itemLength++; 1.2356 + } else { 1.2357 + if (itemType == GMTOffsetField::TEXT) { 1.2358 + if (text.length() > 0) { 1.2359 + GMTOffsetField* textfld = GMTOffsetField::createText(text, status); 1.2360 + result->addElement(textfld, status); 1.2361 + if (U_FAILURE(status)) { 1.2362 + break; 1.2363 + } 1.2364 + text.remove(); 1.2365 + } 1.2366 + } else { 1.2367 + if (GMTOffsetField::isValid(itemType, itemLength)) { 1.2368 + GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status); 1.2369 + result->addElement(fld, status); 1.2370 + if (U_FAILURE(status)) { 1.2371 + break; 1.2372 + } 1.2373 + } else { 1.2374 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.2375 + break; 1.2376 + } 1.2377 + } 1.2378 + itemType = tmpType; 1.2379 + itemLength = 1; 1.2380 + checkBits |= tmpType; 1.2381 + } 1.2382 + } else { 1.2383 + // a string literal 1.2384 + if (itemType != GMTOffsetField::TEXT) { 1.2385 + if (GMTOffsetField::isValid(itemType, itemLength)) { 1.2386 + GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status); 1.2387 + result->addElement(fld, status); 1.2388 + if (U_FAILURE(status)) { 1.2389 + break; 1.2390 + } 1.2391 + } else { 1.2392 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.2393 + break; 1.2394 + } 1.2395 + itemType = GMTOffsetField::TEXT; 1.2396 + } 1.2397 + text.append(ch); 1.2398 + } 1.2399 + } 1.2400 + } 1.2401 + } 1.2402 + // handle last item 1.2403 + if (U_SUCCESS(status)) { 1.2404 + if (itemType == GMTOffsetField::TEXT) { 1.2405 + if (text.length() > 0) { 1.2406 + GMTOffsetField* tfld = GMTOffsetField::createText(text, status); 1.2407 + result->addElement(tfld, status); 1.2408 + } 1.2409 + } else { 1.2410 + if (GMTOffsetField::isValid(itemType, itemLength)) { 1.2411 + GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status); 1.2412 + result->addElement(fld, status); 1.2413 + } else { 1.2414 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.2415 + } 1.2416 + } 1.2417 + 1.2418 + // Check all required fields are set 1.2419 + if (U_SUCCESS(status)) { 1.2420 + int32_t reqBits = 0; 1.2421 + switch (required) { 1.2422 + case FIELDS_H: 1.2423 + reqBits = GMTOffsetField::HOUR; 1.2424 + break; 1.2425 + case FIELDS_HM: 1.2426 + reqBits = GMTOffsetField::HOUR | GMTOffsetField::MINUTE; 1.2427 + break; 1.2428 + case FIELDS_HMS: 1.2429 + reqBits = GMTOffsetField::HOUR | GMTOffsetField::MINUTE | GMTOffsetField::SECOND; 1.2430 + break; 1.2431 + } 1.2432 + if (checkBits == reqBits) { 1.2433 + // all required fields are set, no extra fields 1.2434 + return result; 1.2435 + } 1.2436 + } 1.2437 + } 1.2438 + 1.2439 + // error 1.2440 + delete result; 1.2441 + return NULL; 1.2442 +} 1.2443 + 1.2444 +UnicodeString& 1.2445 +TimeZoneFormat::expandOffsetPattern(const UnicodeString& offsetHM, UnicodeString& result, UErrorCode& status) { 1.2446 + result.setToBogus(); 1.2447 + if (U_FAILURE(status)) { 1.2448 + return result; 1.2449 + } 1.2450 + U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN) == 2); 1.2451 + 1.2452 + int32_t idx_mm = offsetHM.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN, 2, 0); 1.2453 + if (idx_mm < 0) { 1.2454 + // Bad time zone hour pattern data 1.2455 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.2456 + return result; 1.2457 + } 1.2458 + 1.2459 + UnicodeString sep; 1.2460 + int32_t idx_H = offsetHM.tempSubString(0, idx_mm).lastIndexOf((UChar)0x0048 /* H */); 1.2461 + if (idx_H >= 0) { 1.2462 + sep = offsetHM.tempSubString(idx_H + 1, idx_mm - (idx_H + 1)); 1.2463 + } 1.2464 + result.setTo(offsetHM.tempSubString(0, idx_mm + 2)); 1.2465 + result.append(sep); 1.2466 + result.append(DEFAULT_GMT_OFFSET_SECOND_PATTERN, -1); 1.2467 + result.append(offsetHM.tempSubString(idx_mm + 2)); 1.2468 + return result; 1.2469 +} 1.2470 + 1.2471 +UnicodeString& 1.2472 +TimeZoneFormat::truncateOffsetPattern(const UnicodeString& offsetHM, UnicodeString& result, UErrorCode& status) { 1.2473 + result.setToBogus(); 1.2474 + if (U_FAILURE(status)) { 1.2475 + return result; 1.2476 + } 1.2477 + U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN) == 2); 1.2478 + 1.2479 + int32_t idx_mm = offsetHM.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN, 2, 0); 1.2480 + if (idx_mm < 0) { 1.2481 + // Bad time zone hour pattern data 1.2482 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.2483 + return result; 1.2484 + } 1.2485 + UChar HH[] = {0x0048, 0x0048}; 1.2486 + int32_t idx_HH = offsetHM.tempSubString(0, idx_mm).lastIndexOf(HH, 2, 0); 1.2487 + if (idx_HH >= 0) { 1.2488 + return result.setTo(offsetHM.tempSubString(0, idx_HH + 2)); 1.2489 + } 1.2490 + int32_t idx_H = offsetHM.tempSubString(0, idx_mm).lastIndexOf((UChar)0x0048, 0); 1.2491 + if (idx_H >= 0) { 1.2492 + return result.setTo(offsetHM.tempSubString(0, idx_H + 1)); 1.2493 + } 1.2494 + // Bad time zone hour pattern data 1.2495 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.2496 + return result; 1.2497 +} 1.2498 + 1.2499 +void 1.2500 +TimeZoneFormat::initGMTOffsetPatterns(UErrorCode& status) { 1.2501 + for (int32_t type = 0; type < UTZFMT_PAT_COUNT; type++) { 1.2502 + switch (type) { 1.2503 + case UTZFMT_PAT_POSITIVE_H: 1.2504 + case UTZFMT_PAT_NEGATIVE_H: 1.2505 + fGMTOffsetPatternItems[type] = parseOffsetPattern(fGMTOffsetPatterns[type], FIELDS_H, status); 1.2506 + break; 1.2507 + case UTZFMT_PAT_POSITIVE_HM: 1.2508 + case UTZFMT_PAT_NEGATIVE_HM: 1.2509 + fGMTOffsetPatternItems[type] = parseOffsetPattern(fGMTOffsetPatterns[type], FIELDS_HM, status); 1.2510 + break; 1.2511 + case UTZFMT_PAT_POSITIVE_HMS: 1.2512 + case UTZFMT_PAT_NEGATIVE_HMS: 1.2513 + fGMTOffsetPatternItems[type] = parseOffsetPattern(fGMTOffsetPatterns[type], FIELDS_HMS, status); 1.2514 + break; 1.2515 + } 1.2516 + } 1.2517 + checkAbuttingHoursAndMinutes(); 1.2518 +} 1.2519 + 1.2520 +void 1.2521 +TimeZoneFormat::checkAbuttingHoursAndMinutes() { 1.2522 + fAbuttingOffsetHoursAndMinutes= FALSE; 1.2523 + for (int32_t type = 0; type < UTZFMT_PAT_COUNT; type++) { 1.2524 + UBool afterH = FALSE; 1.2525 + UVector *items = fGMTOffsetPatternItems[type]; 1.2526 + for (int32_t i = 0; i < items->size(); i++) { 1.2527 + const GMTOffsetField* item = (GMTOffsetField*)items->elementAt(i); 1.2528 + GMTOffsetField::FieldType type = item->getType(); 1.2529 + if (type != GMTOffsetField::TEXT) { 1.2530 + if (afterH) { 1.2531 + fAbuttingOffsetHoursAndMinutes = TRUE; 1.2532 + break; 1.2533 + } else if (type == GMTOffsetField::HOUR) { 1.2534 + afterH = TRUE; 1.2535 + } 1.2536 + } else if (afterH) { 1.2537 + break; 1.2538 + } 1.2539 + } 1.2540 + if (fAbuttingOffsetHoursAndMinutes) { 1.2541 + break; 1.2542 + } 1.2543 + } 1.2544 +} 1.2545 + 1.2546 +UBool 1.2547 +TimeZoneFormat::toCodePoints(const UnicodeString& str, UChar32* codeArray, int32_t size) { 1.2548 + int32_t count = str.countChar32(); 1.2549 + if (count != size) { 1.2550 + return FALSE; 1.2551 + } 1.2552 + 1.2553 + for (int32_t idx = 0, start = 0; idx < size; idx++) { 1.2554 + codeArray[idx] = str.char32At(start); 1.2555 + start = str.moveIndex32(start, 1); 1.2556 + } 1.2557 + 1.2558 + return TRUE; 1.2559 +} 1.2560 + 1.2561 +TimeZone* 1.2562 +TimeZoneFormat::createTimeZoneForOffset(int32_t offset) const { 1.2563 + if (offset == 0) { 1.2564 + // when offset is 0, we should use "Etc/GMT" 1.2565 + return TimeZone::createTimeZone(UnicodeString(TZID_GMT)); 1.2566 + } 1.2567 + return ZoneMeta::createCustomTimeZone(offset); 1.2568 +} 1.2569 + 1.2570 +UTimeZoneFormatTimeType 1.2571 +TimeZoneFormat::getTimeType(UTimeZoneNameType nameType) { 1.2572 + switch (nameType) { 1.2573 + case UTZNM_LONG_STANDARD: 1.2574 + case UTZNM_SHORT_STANDARD: 1.2575 + return UTZFMT_TIME_TYPE_STANDARD; 1.2576 + 1.2577 + case UTZNM_LONG_DAYLIGHT: 1.2578 + case UTZNM_SHORT_DAYLIGHT: 1.2579 + return UTZFMT_TIME_TYPE_DAYLIGHT; 1.2580 + 1.2581 + default: 1.2582 + U_ASSERT(FALSE); 1.2583 + } 1.2584 + return UTZFMT_TIME_TYPE_UNKNOWN; 1.2585 +} 1.2586 + 1.2587 +UnicodeString& 1.2588 +TimeZoneFormat::getTimeZoneID(const TimeZoneNames::MatchInfoCollection* matches, int32_t idx, UnicodeString& tzID) const { 1.2589 + if (!matches->getTimeZoneIDAt(idx, tzID)) { 1.2590 + UnicodeString mzID; 1.2591 + if (matches->getMetaZoneIDAt(idx, mzID)) { 1.2592 + fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, tzID); 1.2593 + } 1.2594 + } 1.2595 + return tzID; 1.2596 +} 1.2597 + 1.2598 + 1.2599 +class ZoneIdMatchHandler : public TextTrieMapSearchResultHandler { 1.2600 +public: 1.2601 + ZoneIdMatchHandler(); 1.2602 + virtual ~ZoneIdMatchHandler(); 1.2603 + 1.2604 + UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status); 1.2605 + const UChar* getID(); 1.2606 + int32_t getMatchLen(); 1.2607 +private: 1.2608 + int32_t fLen; 1.2609 + const UChar* fID; 1.2610 +}; 1.2611 + 1.2612 +ZoneIdMatchHandler::ZoneIdMatchHandler() 1.2613 +: fLen(0), fID(NULL) { 1.2614 +} 1.2615 + 1.2616 +ZoneIdMatchHandler::~ZoneIdMatchHandler() { 1.2617 +} 1.2618 + 1.2619 +UBool 1.2620 +ZoneIdMatchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) { 1.2621 + if (U_FAILURE(status)) { 1.2622 + return FALSE; 1.2623 + } 1.2624 + if (node->hasValues()) { 1.2625 + const UChar* id = (const UChar*)node->getValue(0); 1.2626 + if (id != NULL) { 1.2627 + if (fLen < matchLength) { 1.2628 + fID = id; 1.2629 + fLen = matchLength; 1.2630 + } 1.2631 + } 1.2632 + } 1.2633 + return TRUE; 1.2634 +} 1.2635 + 1.2636 +const UChar* 1.2637 +ZoneIdMatchHandler::getID() { 1.2638 + return fID; 1.2639 +} 1.2640 + 1.2641 +int32_t 1.2642 +ZoneIdMatchHandler::getMatchLen() { 1.2643 + return fLen; 1.2644 +} 1.2645 + 1.2646 + 1.2647 +static void U_CALLCONV initZoneIdTrie(UErrorCode &status) { 1.2648 + U_ASSERT(gZoneIdTrie == NULL); 1.2649 + ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEFORMAT, tzfmt_cleanup); 1.2650 + gZoneIdTrie = new TextTrieMap(TRUE, NULL); // No deleter, because values are pooled by ZoneMeta 1.2651 + if (gZoneIdTrie == NULL) { 1.2652 + status = U_MEMORY_ALLOCATION_ERROR; 1.2653 + return; 1.2654 + } 1.2655 + StringEnumeration *tzenum = TimeZone::createEnumeration(); 1.2656 + const UnicodeString *id; 1.2657 + while ((id = tzenum->snext(status))) { 1.2658 + const UChar* uid = ZoneMeta::findTimeZoneID(*id); 1.2659 + if (uid) { 1.2660 + gZoneIdTrie->put(uid, const_cast<UChar *>(uid), status); 1.2661 + } 1.2662 + } 1.2663 + delete tzenum; 1.2664 +} 1.2665 + 1.2666 + 1.2667 +UnicodeString& 1.2668 +TimeZoneFormat::parseZoneID(const UnicodeString& text, ParsePosition& pos, UnicodeString& tzID) const { 1.2669 + UErrorCode status = U_ZERO_ERROR; 1.2670 + umtx_initOnce(gZoneIdTrieInitOnce, &initZoneIdTrie, status); 1.2671 + 1.2672 + int32_t start = pos.getIndex(); 1.2673 + int32_t len = 0; 1.2674 + tzID.setToBogus(); 1.2675 + 1.2676 + if (U_SUCCESS(status)) { 1.2677 + LocalPointer<ZoneIdMatchHandler> handler(new ZoneIdMatchHandler()); 1.2678 + gZoneIdTrie->search(text, start, handler.getAlias(), status); 1.2679 + len = handler->getMatchLen(); 1.2680 + if (len > 0) { 1.2681 + tzID.setTo(handler->getID(), -1); 1.2682 + } 1.2683 + } 1.2684 + 1.2685 + if (len > 0) { 1.2686 + pos.setIndex(start + len); 1.2687 + } else { 1.2688 + pos.setErrorIndex(start); 1.2689 + } 1.2690 + 1.2691 + return tzID; 1.2692 +} 1.2693 + 1.2694 +static void U_CALLCONV initShortZoneIdTrie(UErrorCode &status) { 1.2695 + U_ASSERT(gShortZoneIdTrie == NULL); 1.2696 + ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEFORMAT, tzfmt_cleanup); 1.2697 + StringEnumeration *tzenum = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status); 1.2698 + if (U_SUCCESS(status)) { 1.2699 + gShortZoneIdTrie = new TextTrieMap(TRUE, NULL); // No deleter, because values are pooled by ZoneMeta 1.2700 + if (gShortZoneIdTrie == NULL) { 1.2701 + status = U_MEMORY_ALLOCATION_ERROR; 1.2702 + } else { 1.2703 + const UnicodeString *id; 1.2704 + while ((id = tzenum->snext(status))) { 1.2705 + const UChar* uID = ZoneMeta::findTimeZoneID(*id); 1.2706 + const UChar* shortID = ZoneMeta::getShortID(*id); 1.2707 + if (shortID && uID) { 1.2708 + gShortZoneIdTrie->put(shortID, const_cast<UChar *>(uID), status); 1.2709 + } 1.2710 + } 1.2711 + } 1.2712 + } 1.2713 + delete tzenum; 1.2714 +} 1.2715 + 1.2716 + 1.2717 +UnicodeString& 1.2718 +TimeZoneFormat::parseShortZoneID(const UnicodeString& text, ParsePosition& pos, UnicodeString& tzID) const { 1.2719 + UErrorCode status = U_ZERO_ERROR; 1.2720 + umtx_initOnce(gShortZoneIdTrieInitOnce, &initShortZoneIdTrie, status); 1.2721 + 1.2722 + int32_t start = pos.getIndex(); 1.2723 + int32_t len = 0; 1.2724 + tzID.setToBogus(); 1.2725 + 1.2726 + if (U_SUCCESS(status)) { 1.2727 + LocalPointer<ZoneIdMatchHandler> handler(new ZoneIdMatchHandler()); 1.2728 + gShortZoneIdTrie->search(text, start, handler.getAlias(), status); 1.2729 + len = handler->getMatchLen(); 1.2730 + if (len > 0) { 1.2731 + tzID.setTo(handler->getID(), -1); 1.2732 + } 1.2733 + } 1.2734 + 1.2735 + if (len > 0) { 1.2736 + pos.setIndex(start + len); 1.2737 + } else { 1.2738 + pos.setErrorIndex(start); 1.2739 + } 1.2740 + 1.2741 + return tzID; 1.2742 +} 1.2743 + 1.2744 + 1.2745 +UnicodeString& 1.2746 +TimeZoneFormat::parseExemplarLocation(const UnicodeString& text, ParsePosition& pos, UnicodeString& tzID) const { 1.2747 + int32_t startIdx = pos.getIndex(); 1.2748 + int32_t parsedPos = -1; 1.2749 + tzID.setToBogus(); 1.2750 + 1.2751 + UErrorCode status = U_ZERO_ERROR; 1.2752 + LocalPointer<TimeZoneNames::MatchInfoCollection> exemplarMatches(fTimeZoneNames->find(text, startIdx, UTZNM_EXEMPLAR_LOCATION, status)); 1.2753 + if (U_FAILURE(status)) { 1.2754 + pos.setErrorIndex(startIdx); 1.2755 + return tzID; 1.2756 + } 1.2757 + int32_t matchIdx = -1; 1.2758 + if (!exemplarMatches.isNull()) { 1.2759 + for (int32_t i = 0; i < exemplarMatches->size(); i++) { 1.2760 + if (startIdx + exemplarMatches->getMatchLengthAt(i) > parsedPos) { 1.2761 + matchIdx = i; 1.2762 + parsedPos = startIdx + exemplarMatches->getMatchLengthAt(i); 1.2763 + } 1.2764 + } 1.2765 + if (parsedPos > 0) { 1.2766 + pos.setIndex(parsedPos); 1.2767 + getTimeZoneID(exemplarMatches.getAlias(), matchIdx, tzID); 1.2768 + } 1.2769 + } 1.2770 + 1.2771 + if (tzID.length() == 0) { 1.2772 + pos.setErrorIndex(startIdx); 1.2773 + } 1.2774 + 1.2775 + return tzID; 1.2776 +} 1.2777 + 1.2778 +U_NAMESPACE_END 1.2779 + 1.2780 +#endif