From ae3f6f13a421eaa7ed72c7ad052ec09a7f7c352f Mon Sep 17 00:00:00 2001 From: Yoshito Umaoka Date: Wed, 23 Jul 2014 21:51:09 +0000 Subject: [PATCH] ICU-10934 The tz database abbreviation support in ICU4C X-SVN-Rev: 36080 --- .gitattributes | 1 + icu4c/source/data/Makefile.in | 2 +- icu4c/source/data/zone/resfiles.mk | 3 +- icu4c/source/data/zone/tzdbNames.txt | 709 ++++++++++++++++++++++++ icu4c/source/i18n/tzfmt.cpp | 97 +++- icu4c/source/i18n/tznames.cpp | 25 +- icu4c/source/i18n/tznames_impl.cpp | 644 ++++++++++++++++++++- icu4c/source/i18n/tznames_impl.h | 35 +- icu4c/source/i18n/ucln_in.h | 1 + icu4c/source/i18n/unicode/tzfmt.h | 21 +- icu4c/source/i18n/unicode/tznames.h | 17 +- icu4c/source/test/intltest/tzfmttst.cpp | 214 ++++++- icu4c/source/test/intltest/tzfmttst.h | 3 +- 13 files changed, 1732 insertions(+), 40 deletions(-) create mode 100644 icu4c/source/data/zone/tzdbNames.txt diff --git a/.gitattributes b/.gitattributes index e41fb11e9ce..a0ecae30e43 100644 --- a/.gitattributes +++ b/.gitattributes @@ -70,6 +70,7 @@ icu4c/source/data/makedata.vcxproj -text icu4c/source/data/makedata.vcxproj.filters -text icu4c/source/data/region/pool.res -text icu4c/source/data/zone/pool.res -text +icu4c/source/data/zone/tzdbNames.txt -text icu4c/source/extra/uconv/uconv.vcxproj -text icu4c/source/extra/uconv/uconv.vcxproj.filters -text icu4c/source/i18n/i18n.vcxproj -text diff --git a/icu4c/source/data/Makefile.in b/icu4c/source/data/Makefile.in index df97376c20f..97b1a48093c 100644 --- a/icu4c/source/data/Makefile.in +++ b/icu4c/source/data/Makefile.in @@ -322,7 +322,7 @@ REGION_SRC_FILES = $(REGION_SRC:%=$(REGIONSRCDIR)/%) INSTALLED_REGION_FILES = $(REGION_SOURCE:%.txt=%) $(REGION_SOURCE_LOCAL:%.txt=%) endif ifdef ZONE_SOURCE -ZONE_SRC= root.txt $(ZONE_SOURCE) $(ZONE_ALIAS_SOURCE) $(ZONE_SOURCE_LOCAL) +ZONE_SRC= root.txt $(ZONE_SOURCE) $(ZONE_ALIAS_SOURCE) $(ZONE_SOURCE_LOCAL) tzdbNames.txt ZONE_SRC_FILES = $(ZONE_SRC:%=$(ZONESRCDIR)/%) INSTALLED_ZONE_FILES = $(ZONE_SOURCE:%.txt=%) $(ZONE_SOURCE_LOCAL:%.txt=%) endif diff --git a/icu4c/source/data/zone/resfiles.mk b/icu4c/source/data/zone/resfiles.mk index 2dd77f50372..6b0835ca08e 100644 --- a/icu4c/source/data/zone/resfiles.mk +++ b/icu4c/source/data/zone/resfiles.mk @@ -116,5 +116,6 @@ ZONE_SOURCE = af.txt agq.txt ak.txt am.txt\ vai.txt vai_Latn.txt vai_Vaii.txt vi.txt vun.txt\ xog.txt yav.txt yo.txt zgh.txt zh.txt\ zh_Hans.txt zh_Hans_HK.txt zh_Hans_MO.txt zh_Hans_SG.txt zh_Hant.txt\ - zh_Hant_HK.txt zh_Hant_MO.txt zu.txt + zh_Hant_HK.txt zh_Hant_MO.txt zu.txt\ + tzdbNames.txt diff --git a/icu4c/source/data/zone/tzdbNames.txt b/icu4c/source/data/zone/tzdbNames.txt new file mode 100644 index 00000000000..e603fa56fe3 --- /dev/null +++ b/icu4c/source/data/zone/tzdbNames.txt @@ -0,0 +1,709 @@ +// *************************************************************************** +// * +// * Copyright (C) 2014 International Business Machines +// * Corporation and others. All Rights Reserved. +// * +// * This file is manually edited for supporting the tz database name +// * compatibility. +// * +// ***************************************************************************# +tzdbNames{ + zoneStrings{ + "meta:Acre"{ + sd{"ACST"} + ss{"ACT"} + } + "meta:Afghanistan"{ + ss{"AFT"} + } + "meta:Africa_Central"{ + sd{"CAST"} + ss{"CAT"} + } + "meta:Africa_Eastern"{ + sd{"EAST"} + ss{"EAT"} + } + "meta:Africa_FarWestern"{ + ss{"WAT"} + parseRegions{""} // this metazone is never used for parsing + } + "meta:Africa_Southern"{ + ss{"SAST"} + } + "meta:Africa_Western"{ + sd{"WAST"} + ss{"WAT"} + } + "meta:Aktyubinsk"{ + sd{"AKTST"} + ss{"AKTT"} + } + "meta:Alaska"{ + sd{"AKDT"} + ss{"AKST"} + } + "meta:Alaska_Hawaii"{ + sd{"AHDT"} + ss{"AHST"} + } + "meta:Almaty"{ + sd{"ALMST"} + ss{"ALMT"} + parseRegions{""} // this metazone is never used for parsing + } + "meta:Amazon"{ + sd{"AMST"} + ss{"AMT"} + } + "meta:America_Central"{ + sd{"CDT"} + ss{"CST"} + } + "meta:America_Eastern"{ + sd{"EDT"} + ss{"EST"} + } + "meta:America_Mountain"{ + sd{"MDT"} + ss{"MST"} + } + "meta:America_Pacific"{ + sd{"PDT"} + ss{"PST"} + } + "meta:Anadyr"{ + sd{"ANAST"} + ss{"ANAT"} + } + "meta:Aqtau"{ + sd{"AQTST"} + ss{"AQTT"} + parseRegions{""} // this metazone is never used for parsing + } + "meta:Aqtobe"{ + sd{"AQTST"} + ss{"AQTT"} + parseRegions{""} // this metazone is never used for parsing + } + "meta:Arabian"{ + sd{"ADT"} + ss{"AST"} + parseRegions{"BH", "IQ", "KW", "QA", "SA", "YE"} + } + "meta:Argentina"{ + sd{"ARST"} + ss{"ART"} + } + "meta:Argentina_Western"{ + sd{"WARST"} + ss{"WART"} + } + "meta:Armenia"{ + sd{"AMST"} + ss{"AMT"} + parseRegions{"AM"} + } + "meta:Ashkhabad"{ + sd{"ASHST"} + ss{"ASHT"} + } + "meta:Atlantic"{ + sd{"ADT"} + ss{"AST"} + } + "meta:Australia_Central"{ + sd{"CST"} + ss{"CST"} + parseRegions{"AU"} + } + "meta:Australia_CentralWestern"{ + sd{"CWST"} + ss{"CWST"} + } + "meta:Australia_Eastern"{ + sd{"EST"} + ss{"EST"} + parseRegions{"AU"} + } + "meta:Australia_Western"{ + sd{"WST"} + ss{"WST"} + } + "meta:Azerbaijan"{ + sd{"AZT"} + ss{"AZST"} + } + "meta:Azores"{ + sd{"AZOST"} + ss{"AZOT"} + } + "meta:Baku"{ + sd{"BAKST"} + ss{"BAKT"} + } + "meta:Bangladesh"{ + sd{"BDST"} + ss{"BDT"} + } + "meta:Bering"{ + sd{"BDT"} + ss{"BST"} + parseRegions{""} // this metazone is never used for parsing + } + "meta:Bhutan"{ + ss{"BTT"} + } + "meta:Bolivia"{ + ss{"BOT"} + } + "meta:Borneo"{ + ss{"BORT"} + } + "meta:Brasilia"{ + sd{"BRST"} + ss{"BRT"} + } + "meta:British"{ + sd{"BST"} + parseRegions{""} // this metazone is never used for parsing + } + "meta:Brunei"{ + ss{"BNT"} + } + "meta:Cape_Verde"{ + sd{"CVST"} + ss{"CVT"} + } + "meta:Casey"{ + ss{"CAST"} + parseRegions{"AQ"} + } + "meta:Chamorro"{ + ss{"ChST"} + } + "meta:Changbai"{ + ss{"CHAT"} + } + "meta:Chatham"{ + sd{"CHAST"} + ss{"CHADT"} + } + "meta:Chile"{ + sd{"CLST"} + ss{"CLT"} + } + "meta:China"{ + sd{"CDT"} + ss{"CST"} + parseRegions{"CN", "MO", "TW"} + } + "meta:Choibalsan"{ + sd{"CHOST"} + ss{"CHOT"} + } + "meta:Christmas"{ + ss{"CXT"} + } + "meta:Cocos"{ + ss{"CCT"} + } + "meta:Colombia"{ + sd{"COST"} + ss{"COT"} + } + "meta:Cook"{ + sd{"CKHST"} + ss{"CKT"} + } + "meta:Cuba"{ + sd{"CDT"} + ss{"CST"} + parseRegions{"CU"} + } + "meta:Dacca"{ + ss{"DACT"} + } + "meta:Davis"{ + ss{"DAVT"} + } + "meta:Dominican"{ + sd{"EHDT"} + ss{"EST"} + } + "meta:DumontDUrville"{ + ss{"DDUT"} + } + "meta:Dushanbe"{ + sd{"DUSST"} + ss{"DUST"} + } + "meta:Dutch_Guiana"{ + ss{"NEGT"} + } + "meta:East_Timor"{ + ss{"TLT"} + } + "meta:Easter"{ + sd{"EASST"} + ss{"EAST"} + parseRegions{"CL"} + } + "meta:Ecuador"{ + ss{"ECT"} + } + "meta:Europe_Central"{ + sd{"CEST"} + ss{"CET"} + } + "meta:Europe_Eastern"{ + sd{"EEST"} + ss{"EET"} + } + "meta:Europe_Western"{ + sd{"WEST"} + ss{"WET"} + } + "meta:Falkland"{ + ss{"FKST"} + } + "meta:Fiji"{ + sd{"FJST"} + ss{"FJT"} + } + "meta:French_Guiana"{ + ss{"GFT"} + } + "meta:French_Southern"{ + ss{"TFT"} + } + "meta:Frunze"{ + sd{"FRUST"} + ss{"FRUT"} + } + "meta:GMT"{ + ss{"GMT"} + } + "meta:Galapagos"{ + ss{"GALT"} + } + "meta:Gambier"{ + ss{"GAMT"} + } + "meta:Georgia"{ + sd{"GEST"} + ss{"GET"} + } + "meta:Gilbert_Islands"{ + ss{"GILT"} + } + "meta:Goose_Bay"{ + sd{"ADT"} + ss{"AST"} + parseRegions{"CA"} + } + "meta:Greenland_Central"{ + sd{"CGST"} + ss{"CGT"} + } + "meta:Greenland_Eastern"{ + sd{"EGST"} + ss{"EGT"} + } + "meta:Greenland_Western"{ + sd{"WGST"} + ss{"WGT"} + } + "meta:Guam"{ + ss{"GST"} + parseRegions{""} // this metazone is never used for parsing + } + "meta:Gulf"{ + ss{"GST"} + } + "meta:Guyana"{ + ss{"GYT"} + } + "meta:Hawaii_Aleutian"{ + sd{"HDT"} + ss{"HST"} + } + "meta:Hong_Kong"{ + sd{"HKST"} + ss{"HKT"} + } + "meta:Hovd"{ + sd{"HOVST"} + ss{"HOVT"} + } + "meta:India"{ + ss{"IST"} + } + "meta:Indian_Ocean"{ + ss{"IOT"} + } + "meta:Indochina"{ + ss{"ICT"} + } + "meta:Indonesia_Central"{ + ss{"WITA"} + } + "meta:Indonesia_Eastern"{ + ss{"WIT"} + } + "meta:Indonesia_Western"{ + ss{"WIB"} + } + "meta:Iran"{ + sd{"IRDT"} + ss{"IRST"} + } + "meta:Irkutsk"{ + sd{"IRKST"} + ss{"IRKT"} + } + "meta:Israel"{ + sd{"IDT"} + ss{"IST"} + parseRegions{"IL", "PS"} + } + "meta:Japan"{ + sd{"JDT"} + ss{"JST"} + } + "meta:Kamchatka"{ + sd{"PETST"} + ss{"PETT"} + } + "meta:Karachi"{ + ss{"KART"} + } + "meta:Kashgar"{ + ss{"KAST"} + } + "meta:Kazakhstan_Eastern"{ + sd{"ALMST"} + ss{"ALMT"} + } + "meta:Kazakhstan_Western"{ + sd{"AQTST"} + ss{"AQTT"} + } + "meta:Kizilorda"{ + sd{"KIZST"} + ss{"KIZT"} + } + "meta:Korea"{ + sd{"KDT"} + ss{"KST"} + } + "meta:Kosrae"{ + ss{"KOST"} + } + "meta:Krasnoyarsk"{ + sd{"KRAST"} + ss{"KRAT"} + } + "meta:Kuybyshev"{ + sd{"KUYST"} + ss{"KUYT"} + } + "meta:Kwajalein"{ + ss{"KWAT"} + } + "meta:Kyrgystan"{ + sd{"KGST"} + ss{"KGT"} + } + "meta:Lanka"{ + ss{"LKT"} + } + "meta:Liberia"{ + ss{"LRT"} + } + "meta:Line_Islands"{ + ss{"LINT"} + } + "meta:Long_Shu"{ + ss{"LONT"} + } + "meta:Lord_Howe"{ + ss{"LHST"} + } + "meta:Macau"{ + sd{"MOST"} + ss{"MOT"} + } + "meta:Macquarie"{ + ss{"MIST"} + } + "meta:Magadan"{ + sd{"MAGST"} + ss{"MAGT"} + } + "meta:Malaya"{ + ss{"MALT"} + } + "meta:Malaysia"{ + ss{"MYT"} + } + "meta:Maldives"{ + ss{"MVT"} + } + "meta:Marquesas"{ + ss{"MART"} + } + "meta:Marshall_Islands"{ + ss{"MHT"} + } + "meta:Mauritius"{ + sd{"MUST"} + ss{"MUT"} + } + "meta:Mawson"{ + ss{"MAWT"} + } + "meta:Mexico_Northwest"{ + sd{"PDT"} + ss{"PST"} + } + "meta:Mexico_Pacific"{ + sd{"MDT"} + ss{"MST"} + parseRegions{"MX"} + } + "meta:Mongolia"{ + sd{"ULAST"} + ss{"ULAT"} + } + "meta:Moscow"{ + sd{"MSD"} + ss{"MSK"} + } + "meta:Myanmar"{ + ss{"MMT"} + } + "meta:Nauru"{ + ss{"NRT"} + } + "meta:Nepal"{ + ss{"NPT"} + } + "meta:New_Caledonia"{ + sd{"NCST"} + ss{"NCT"} + } + "meta:New_Zealand"{ + sd{"NZDT"} + ss{"NZST"} + } + "meta:Newfoundland"{ + sd{"NDT"} + ss{"NST"} + } + "meta:Niue"{ + ss{"NUT"} + } + "meta:Norfolk"{ + ss{"NFT"} + } + "meta:Noronha"{ + sd{"FNST"} + ss{"FNT"} + } + "meta:North_Mariana"{ + ss{"MPT"} + } + "meta:Novosibirsk"{ + sd{"NOVST"} + ss{"NOVT"} + } + "meta:Omsk"{ + sd{"OMSST"} + ss{"OMST"} + } + "meta:Oral"{ + sd{"ORAST"} + ss{"ORAT"} + } + "meta:Pakistan"{ + sd{"PKST"} + ss{"PKT"} + } + "meta:Palau"{ + ss{"PWT"} + } + "meta:Papua_New_Guinea"{ + ss{"PGT"} + } + "meta:Paraguay"{ + sd{"PYST"} + ss{"PYT"} + } + "meta:Peru"{ + sd{"PEST"} + ss{"PET"} + } + "meta:Philippines"{ + sd{"PHST"} + ss{"PHT"} + } + "meta:Phoenix_Islands"{ + ss{"PHOT"} + } + "meta:Pierre_Miquelon"{ + sd{"PMDT"} + ss{"PMST"} + } + "meta:Pitcairn"{ + ss{"PST"} + parseRegions{"PN"} + } + "meta:Ponape"{ + ss{"PONT"} + } + "meta:Qyzylorda"{ + sd{"QYZST"} + ss{"QYZT"} + } + "meta:Reunion"{ + ss{"RET"} + } + "meta:Rothera"{ + ss{"ROTT"} + } + "meta:Sakhalin"{ + sd{"SAKST"} + ss{"SAKT"} + } + "meta:Samara"{ + sd{"SAMST"} + ss{"SAMT"} + } + "meta:Samarkand"{ + sd{"SAMST"} + ss{"SAMT"} + parseRegions{"UZ"} + } + "meta:Samoa"{ + ss{"SST"} + } + "meta:Seychelles"{ + ss{"SCT"} + } + "meta:Shevchenko"{ + sd{"SHEST"} + ss{"SHET"} + } + "meta:Singapore"{ + ss{"SGT"} + } + "meta:Solomon"{ + ss{"SBT"} + } + "meta:South_Georgia"{ + ss{"GST"} + parseRegions{"GS"} + } + "meta:Suriname"{ + ss{"SRT"} + } + "meta:Syowa"{ + ss{"SYOT"} + } + "meta:Tahiti"{ + ss{"TAHT"} + } + "meta:Taipei"{ + sd{"CDT"} + ss{"CST"} + } + "meta:Tajikistan"{ + ss{"TJT"} + } + "meta:Tashkent"{ + sd{"TASST"} + ss{"TAST"} + } + "meta:Tbilisi"{ + sd{"TBIST"} + ss{"TBIT"} + } + "meta:Tokelau"{ + ss{"TKT"} + } + "meta:Tonga"{ + sd{"TOST"} + ss{"TOT"} + } + "meta:Truk"{ + ss{"CHUT"} + } + "meta:Turkey"{ + sd{"TRST"} + ss{"TRT"} + } + "meta:Turkmenistan"{ + sd{"TMST"} + ss{"TMT"} + } + "meta:Tuvalu"{ + ss{"TVT"} + } + "meta:Uralsk"{ + sd{"URAST"} + ss{"URAT"} + } + "meta:Uruguay"{ + sd{"UYST"} + ss{"UYT"} + } + "meta:Urumqi"{ + ss{"URUT"} + } + "meta:Uzbekistan"{ + sd{"UZST"} + ss{"UZT"} + } + "meta:Vanuatu"{ + sd{"VUST"} + ss{"VUT"} + } + "meta:Venezuela"{ + ss{"VET"} + } + "meta:Vladivostok"{ + sd{"VLAST"} + ss{"VLAT"} + } + "meta:Volgograd"{ + sd{"VOLST"} + ss{"VOLT"} + } + "meta:Vostok"{ + ss{"VOST"} + } + "meta:Wake"{ + ss{"WAKT"} + } + "meta:Wallis"{ + ss{"WFT"} + } + "meta:Yakutsk"{ + sd{"YAKST"} + ss{"YAKT"} + } + "meta:Yekaterinburg"{ + sd{"YEKST"} + ss{"YEKT"} + } + "meta:Yerevan"{ + sd{"YERST"} + ss{"YERT"} + } + "meta:Yukon"{ + sd{"YDT"} + ss{"YST"} + } + } +} diff --git a/icu4c/source/i18n/tzfmt.cpp b/icu4c/source/i18n/tzfmt.cpp index a2e8a5936a6..6900fdf94ed 100644 --- a/icu4c/source/i18n/tzfmt.cpp +++ b/icu4c/source/i18n/tzfmt.cpp @@ -1,6 +1,6 @@ /* ******************************************************************************* -* Copyright (C) 2011-2013, International Business Machines Corporation and +* Copyright (C) 2011-2014, International Business Machines Corporation and * others. All Rights Reserved. ******************************************************************************* */ @@ -308,7 +308,8 @@ U_CDECL_END UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeZoneFormat) TimeZoneFormat::TimeZoneFormat(const Locale& locale, UErrorCode& status) -: fLocale(locale), fTimeZoneNames(NULL), fTimeZoneGenericNames(NULL), fDefParseOptionFlags(0) { +: fLocale(locale), fTimeZoneNames(NULL), fTimeZoneGenericNames(NULL), + fDefParseOptionFlags(0), fTZDBTimeZoneNames(NULL) { for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) { fGMTOffsetPatternItems[i] = NULL; @@ -418,6 +419,7 @@ TimeZoneFormat::TimeZoneFormat(const TimeZoneFormat& other) TimeZoneFormat::~TimeZoneFormat() { delete fTimeZoneNames; delete fTimeZoneGenericNames; + delete fTZDBTimeZoneNames; for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) { delete fGMTOffsetPatternItems[i]; } @@ -846,6 +848,8 @@ TimeZoneFormat::parse(UTimeZoneFormatStyle style, const UnicodeString& text, Par UErrorCode status = U_ZERO_ERROR; UnicodeString tzID; + UBool parseTZDBAbbrev = ((parseOptions & UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS) != 0); + // Try the specified style switch (style) { case UTZFMT_STYLE_LOCALIZED_GMT: @@ -956,6 +960,41 @@ TimeZoneFormat::parse(UTimeZoneFormatStyle style, const UnicodeString& text, Par return TimeZone::createTimeZone(tzID); } } + + if (parseTZDBAbbrev && style == UTZFMT_STYLE_SPECIFIC_SHORT) { + U_ASSERT((nameTypes & UTZNM_SHORT_STANDARD) != 0); + U_ASSERT((nameTypes & UTZNM_SHORT_DAYLIGHT) != 0); + + const TZDBTimeZoneNames *tzdbTimeZoneNames = getTZDBTimeZoneNames(status); + if (U_SUCCESS(status)) { + LocalPointer tzdbNameMatches( + tzdbTimeZoneNames->find(text, startIdx, nameTypes, status)); + if (U_FAILURE(status)) { + pos.setErrorIndex(startIdx); + return NULL; + } + if (!tzdbNameMatches.isNull()) { + int32_t matchIdx = -1; + int32_t matchPos = -1; + for (int32_t i = 0; i < tzdbNameMatches->size(); i++) { + matchPos = startIdx + tzdbNameMatches->getMatchLengthAt(i); + if (matchPos > parsedPos) { + matchIdx = i; + parsedPos = matchPos; + } + } + if (matchIdx >= 0) { + if (timeType) { + *timeType = getTimeType(tzdbNameMatches->getNameTypeAt(matchIdx)); + } + pos.setIndex(matchPos); + getTimeZoneID(tzdbNameMatches.getAlias(), matchIdx, tzID); + U_ASSERT(!tzID.isEmpty()); + return TimeZone::createTimeZone(tzID); + } + } + } + } break; } case UTZFMT_STYLE_GENERIC_LONG: @@ -1168,6 +1207,34 @@ TimeZoneFormat::parse(UTimeZoneFormatStyle style, const UnicodeString& text, Par parsedOffset = UNKNOWN_OFFSET; } } + if (parseTZDBAbbrev && parsedPos < maxPos && (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_SPECIFIC_SHORT]) == 0) { + const TZDBTimeZoneNames *tzdbTimeZoneNames = getTZDBTimeZoneNames(status); + if (U_SUCCESS(status)) { + LocalPointer tzdbNameMatches( + tzdbTimeZoneNames->find(text, startIdx, ALL_SIMPLE_NAME_TYPES, status)); + if (U_FAILURE(status)) { + pos.setErrorIndex(startIdx); + return NULL; + } + int32_t tzdbNameMatchIdx = -1; + int32_t matchPos = -1; + if (!tzdbNameMatches.isNull()) { + for (int32_t i = 0; i < tzdbNameMatches->size(); i++) { + if (startIdx + tzdbNameMatches->getMatchLengthAt(i) > matchPos) { + tzdbNameMatchIdx = i; + matchPos = startIdx + tzdbNameMatches->getMatchLengthAt(i); + } + } + } + if (parsedPos < matchPos) { + U_ASSERT(tzdbNameMatchIdx >= 0); + parsedPos = matchPos; + getTimeZoneID(tzdbNameMatches.getAlias(), tzdbNameMatchIdx, parsedID); + parsedTimeType = getTimeType(tzdbNameMatches->getNameTypeAt(tzdbNameMatchIdx)); + parsedOffset = UNKNOWN_OFFSET; + } + } + } // Try generic names if (parsedPos < maxPos) { int32_t genMatchLen = -1; @@ -1182,7 +1249,7 @@ TimeZoneFormat::parse(UTimeZoneFormatStyle style, const UnicodeString& text, Par return NULL; } - if (parsedPos < startIdx + genMatchLen) { + if (genMatchLen > 0 && parsedPos < startIdx + genMatchLen) { parsedPos = startIdx + genMatchLen; parsedID.setTo(tzID); parsedTimeType = tt; @@ -1313,6 +1380,27 @@ TimeZoneFormat::getTimeZoneGenericNames(UErrorCode& status) const { return fTimeZoneGenericNames; } +const TZDBTimeZoneNames* +TimeZoneFormat::getTZDBTimeZoneNames(UErrorCode& status) const { + if (U_FAILURE(status)) { + return NULL; + } + + umtx_lock(&gLock); + if (fTZDBTimeZoneNames == NULL) { + TZDBTimeZoneNames *tzdbNames = new TZDBTimeZoneNames(fLocale); + if (tzdbNames == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + } else { + TimeZoneFormat *nonConstThis = const_cast(this); + nonConstThis->fTZDBTimeZoneNames = tzdbNames; + } + } + umtx_unlock(&gLock); + + return fTZDBTimeZoneNames; +} + UnicodeString& TimeZoneFormat::formatExemplarLocation(const TimeZone& tz, UnicodeString& name) const { UnicodeString location; @@ -2576,9 +2664,8 @@ TimeZoneFormat::getTimeType(UTimeZoneNameType nameType) { return UTZFMT_TIME_TYPE_DAYLIGHT; default: - U_ASSERT(FALSE); + return UTZFMT_TIME_TYPE_UNKNOWN; } - return UTZFMT_TIME_TYPE_UNKNOWN; } UnicodeString& diff --git a/icu4c/source/i18n/tznames.cpp b/icu4c/source/i18n/tznames.cpp index bb1e863cd8b..57bda37fc66 100644 --- a/icu4c/source/i18n/tznames.cpp +++ b/icu4c/source/i18n/tznames.cpp @@ -1,6 +1,6 @@ /* ******************************************************************************* -* Copyright (C) 2011-2013, International Business Machines Corporation and * +* Copyright (C) 2011-2014, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* */ @@ -291,9 +291,30 @@ TimeZoneNames::~TimeZoneNames() { TimeZoneNames* TimeZoneNames::createInstance(const Locale& locale, UErrorCode& status) { - return new TimeZoneNamesDelegate(locale, status); + TimeZoneNames *instance = NULL; + if (U_SUCCESS(status)) { + instance = new TimeZoneNamesDelegate(locale, status); + if (instance == NULL && U_SUCCESS(status)) { + status = U_MEMORY_ALLOCATION_ERROR; + } + } + return instance; } +#ifndef U_HIDE_DRAFT_API +TimeZoneNames* +TimeZoneNames::createTZDBInstance(const Locale& locale, UErrorCode& status) { + TimeZoneNames *instance = NULL; + if (U_SUCCESS(status)) { + instance = new TZDBTimeZoneNames(locale); + if (instance == NULL && U_SUCCESS(status)) { + status = U_MEMORY_ALLOCATION_ERROR; + } + } + return instance; +} +#endif /* U_HIDE_DRAFT_API */ + UnicodeString& TimeZoneNames::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const { return TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, name); diff --git a/icu4c/source/i18n/tznames_impl.cpp b/icu4c/source/i18n/tznames_impl.cpp index d11d787cfc7..1595d89c549 100644 --- a/icu4c/source/i18n/tznames_impl.cpp +++ b/icu4c/source/i18n/tznames_impl.cpp @@ -1,6 +1,6 @@ /* ******************************************************************************* -* Copyright (C) 2011-2013, International Business Machines Corporation and +* Copyright (C) 2011-2014, International Business Machines Corporation and * others. All Rights Reserved. ******************************************************************************* * @@ -51,6 +51,36 @@ static const UTimeZoneNameType ALL_NAME_TYPES[] = { UTZNM_UNKNOWN // unknown as the last one }; +// stuff for TZDBTimeZoneNames +static const char* TZDBNAMES_KEYS[] = {"ss", "sd"}; +static const int32_t TZDBNAMES_KEYS_SIZE = (sizeof TZDBNAMES_KEYS / sizeof TZDBNAMES_KEYS[0]); + +static UMutex gTZDBNamesMapLock = U_MUTEX_INITIALIZER; + +static UHashtable* gTZDBNamesMap = NULL; +static icu::UInitOnce gTZDBNamesMapInitOnce = U_INITONCE_INITIALIZER; + +static TextTrieMap* gTZDBNamesTrie = NULL; +static icu::UInitOnce gTZDBNamesTrieInitOnce = U_INITONCE_INITIALIZER; + +U_CDECL_BEGIN +static UBool U_CALLCONV tzdbTimeZoneNames_cleanup(void) { + if (gTZDBNamesMap != NULL) { + uhash_close(gTZDBNamesMap); + gTZDBNamesMap = NULL; + } + gTZDBNamesMapInitOnce.reset(); + + if (gTZDBNamesTrie != NULL) { + delete gTZDBNamesTrie; + gTZDBNamesTrie = NULL; + } + gTZDBNamesTrieInitOnce.reset(); + + return TRUE; +} +U_CDECL_END + #define DEFAULT_CHARACTERNODE_CAPACITY 1 // --------------------------------------------------- @@ -799,7 +829,7 @@ ZNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, for (int32_t i = 0; i < valuesCount; i++) { ZNameInfo *nameinfo = (ZNameInfo *)node->getValue(i); if (nameinfo == NULL) { - break; + continue; } if ((nameinfo->type & fTypes) != 0) { // matches a requested type @@ -986,6 +1016,12 @@ TimeZoneNamesImpl::clone() const { StringEnumeration* TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode& status) const { + return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status); +} + +// static implementation of getAvailableMetaZoneIDs(UErrorCode&) +StringEnumeration* +TimeZoneNamesImpl::_getAvailableMetaZoneIDs(UErrorCode& status) { if (U_FAILURE(status)) { return NULL; } @@ -998,6 +1034,12 @@ TimeZoneNamesImpl::getAvailableMetaZoneIDs(UErrorCode& status) const { StringEnumeration* TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const { + return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID, status); +} + +// static implementation of getAvailableMetaZoneIDs(const UnicodeString&, UErrorCode&) +StringEnumeration* +TimeZoneNamesImpl::_getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) { if (U_FAILURE(status)) { return NULL; } @@ -1032,16 +1074,29 @@ TimeZoneNamesImpl::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode UnicodeString& TimeZoneNamesImpl::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const { + return TimeZoneNamesImpl::_getMetaZoneID(tzID, date, mzID); +} + +// static implementation of getMetaZoneID +UnicodeString& +TimeZoneNamesImpl::_getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) { ZoneMeta::getMetazoneID(tzID, date, mzID); return mzID; } UnicodeString& TimeZoneNamesImpl::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const { + return TimeZoneNamesImpl::_getReferenceZoneID(mzID, region, tzID); +} + +// static implementaion of getReferenceZoneID +UnicodeString& +TimeZoneNamesImpl::_getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) { ZoneMeta::getZoneIdByMetazone(mzID, UnicodeString(region, -1, US_INV), tzID); return tzID; } + UnicodeString& TimeZoneNamesImpl::getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, @@ -1170,6 +1225,7 @@ TimeZoneNamesImpl::loadMetaZoneNames(const UnicodeString& mzID) { if (U_FAILURE(status)) { if (znames != NULL) { delete znames; + znames = NULL; } } else if (znames != NULL) { // put the name info into the trie @@ -1247,6 +1303,7 @@ TimeZoneNamesImpl::loadTimeZoneNames(const UnicodeString& tzID) { if (U_FAILURE(status)) { if (tznames != NULL) { delete tznames; + tznames = NULL; } } else if (tznames != NULL) { // put the name info into the trie @@ -1371,6 +1428,589 @@ TimeZoneNamesImpl::getDefaultExemplarLocationName(const UnicodeString& tzID, Uni return name; } +// --------------------------------------------------- +// TZDBTimeZoneNames and its supporting classes +// +// TZDBTimeZoneNames is an implementation class of +// TimeZoneNames holding the IANA tz database abbreviations. +// --------------------------------------------------- + +class TZDBNames : public UMemory { +public: + virtual ~TZDBNames(); + + static TZDBNames* createInstance(UResourceBundle* rb, const char* key); + const UChar* getName(UTimeZoneNameType type) const; + const char** getParseRegions(int32_t& numRegions) const; + +protected: + TZDBNames(const UChar** names, char** regions, int32_t numRegions); + +private: + const UChar** fNames; + char** fRegions; + int32_t fNumRegions; +}; + +TZDBNames::TZDBNames(const UChar** names, char** regions, int32_t numRegions) + : fNames(names), + fRegions(regions), + fNumRegions(numRegions) { +} + +TZDBNames::~TZDBNames() { + if (fNames != NULL) { + uprv_free(fNames); + } + if (fRegions != NULL) { + char **p = fRegions; + for (int32_t i = 0; i < fNumRegions; p++, i++) { + uprv_free(*p); + } + uprv_free(fRegions); + } +} + +TZDBNames* +TZDBNames::createInstance(UResourceBundle* rb, const char* key) { + if (rb == NULL || key == NULL || *key == 0) { + return NULL; + } + + UErrorCode status = U_ZERO_ERROR; + + const UChar **names = NULL; + char** regions = NULL; + int32_t numRegions = 0; + + int32_t len = 0; + + UResourceBundle* rbTable = NULL; + rbTable = ures_getByKey(rb, key, rbTable, &status); + if (U_FAILURE(status)) { + return NULL; + } + + names = (const UChar **)uprv_malloc(sizeof(const UChar*) * TZDBNAMES_KEYS_SIZE); + UBool isEmpty = TRUE; + if (names != NULL) { + for (int32_t i = 0; i < TZDBNAMES_KEYS_SIZE; i++) { + status = U_ZERO_ERROR; + const UChar *value = ures_getStringByKey(rbTable, TZDBNAMES_KEYS[i], &len, &status); + if (U_FAILURE(status) || len == 0) { + names[i] = NULL; + } else { + names[i] = value; + isEmpty = FALSE; + } + } + } + + if (isEmpty) { + if (names != NULL) { + uprv_free(names); + } + return NULL; + } + + UResourceBundle *regionsRes = ures_getByKey(rbTable, "parseRegions", NULL, &status); + UBool regionError = FALSE; + if (U_SUCCESS(status)) { + numRegions = ures_getSize(regionsRes); + if (numRegions > 0) { + regions = (char**)uprv_malloc(sizeof(char*) * numRegions); + if (regions != NULL) { + char **pRegion = regions; + for (int32_t i = 0; i < numRegions; i++, pRegion++) { + *pRegion = NULL; + } + // filling regions + pRegion = regions; + for (int32_t i = 0; i < numRegions; i++, pRegion++) { + status = U_ZERO_ERROR; + const UChar *uregion = ures_getStringByIndex(regionsRes, i, &len, &status); + if (U_FAILURE(status)) { + regionError = TRUE; + break; + } + *pRegion = (char*)uprv_malloc(sizeof(char) * (len + 1)); + if (*pRegion == NULL) { + regionError = TRUE; + break; + } + u_UCharsToChars(uregion, *pRegion, len); + (*pRegion)[len] = 0; + } + } + } + } + ures_close(regionsRes); + ures_close(rbTable); + + if (regionError) { + if (names != NULL) { + uprv_free(names); + } + if (regions != NULL) { + char **p = regions; + for (int32_t i = 0; i < numRegions; p++, i++) { + uprv_free(p); + } + uprv_free(regions); + } + return NULL; + } + + return new TZDBNames(names, regions, numRegions); +} + +const UChar* +TZDBNames::getName(UTimeZoneNameType type) const { + if (fNames == NULL) { + return NULL; + } + const UChar *name = NULL; + switch(type) { + case UTZNM_SHORT_STANDARD: + name = fNames[0]; + break; + case UTZNM_SHORT_DAYLIGHT: + name = fNames[1]; + break; + default: + name = NULL; + } + return name; +} + +const char** +TZDBNames::getParseRegions(int32_t& numRegions) const { + if (fRegions == NULL) { + numRegions = 0; + } else { + numRegions = fNumRegions; + } + return (const char**)fRegions; +} + +U_CDECL_BEGIN +/** + * TZDBNameInfo stores metazone name information for the IANA abbreviations + * in the trie + */ +typedef struct TZDBNameInfo { + const UChar* mzID; + UTimeZoneNameType type; + UBool ambiguousType; + const char** parseRegions; + int32_t nRegions; +} TZDBNameInfo; +U_CDECL_END + + +class TZDBNameSearchHandler : public TextTrieMapSearchResultHandler { +public: + TZDBNameSearchHandler(uint32_t types, const char* region); + virtual ~TZDBNameSearchHandler(); + + UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status); + TimeZoneNames::MatchInfoCollection* getMatches(int32_t& maxMatchLen); + +private: + uint32_t fTypes; + int32_t fMaxMatchLen; + TimeZoneNames::MatchInfoCollection* fResults; + const char* fRegion; +}; + +TZDBNameSearchHandler::TZDBNameSearchHandler(uint32_t types, const char* region) +: fTypes(types), fMaxMatchLen(0), fResults(NULL), fRegion(region) { +} + +TZDBNameSearchHandler::~TZDBNameSearchHandler() { + if (fResults != NULL) { + delete fResults; + } +} + +UBool +TZDBNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) { + if (U_FAILURE(status)) { + return FALSE; + } + + TZDBNameInfo *match = NULL; + TZDBNameInfo *defaultRegionMatch = NULL; + + if (node->hasValues()) { + int32_t valuesCount = node->countValues(); + for (int32_t i = 0; i < valuesCount; i++) { + TZDBNameInfo *ninfo = (TZDBNameInfo *)node->getValue(i); + if (ninfo == NULL) { + continue; + } + if ((ninfo->type & fTypes) != 0) { + // Some tz database abbreviations are ambiguous. For example, + // CST means either Central Standard Time or China Standard Time. + // Unlike CLDR time zone display names, this implementation + // does not use unique names. And TimeZoneFormat does not expect + // multiple results returned for the same time zone type. + // For this reason, this implementation resolve one among same + // zone type with a same name at this level. + if (ninfo->parseRegions == NULL) { + // parseRegions == null means this is the default metazone + // mapping for the abbreviation. + if (defaultRegionMatch == NULL) { + match = defaultRegionMatch = ninfo; + } + } else { + UBool matchRegion = FALSE; + // non-default metazone mapping for an abbreviation + // comes with applicable regions. For example, the default + // metazone mapping for "CST" is America_Central, + // but if region is one of CN/MO/TW, "CST" is parsed + // as metazone China (China Standard Time). + for (int32_t i = 0; i < ninfo->nRegions; i++) { + const char *region = ninfo->parseRegions[i]; + if (uprv_strcmp(fRegion, region) == 0) { + match = ninfo; + matchRegion = TRUE; + break; + } + } + if (matchRegion) { + break; + } + if (match == NULL) { + match = ninfo; + } + } + } + } + + if (match != NULL) { + UTimeZoneNameType ntype = match->type; + // Note: Workaround for duplicated standard/daylight names + // The tz database contains a few zones sharing a + // same name for both standard time and daylight saving + // time. For example, Australia/Sydney observes DST, + // but "EST" is used for both standard and daylight. + // When both SHORT_STANDARD and SHORT_DAYLIGHT are included + // in the find operation, we cannot tell which one was + // actually matched. + // TimeZoneFormat#parse returns a matched name type (standard + // or daylight) and DateFormat implementation uses the info to + // to adjust actual time. To avoid false type information, + // this implementation replaces the name type with SHORT_GENERIC. + if (match->ambiguousType + && (ntype == UTZNM_SHORT_STANDARD || ntype == UTZNM_SHORT_DAYLIGHT) + && (fTypes & UTZNM_SHORT_STANDARD) != 0 + && (fTypes & UTZNM_SHORT_DAYLIGHT) != 0) { + ntype = UTZNM_SHORT_GENERIC; + } + + if (fResults == NULL) { + fResults = new TimeZoneNames::MatchInfoCollection(); + if (fResults == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + } + } + if (U_SUCCESS(status)) { + U_ASSERT(fResults != NULL); + U_ASSERT(match->mzID != NULL); + fResults->addMetaZone(ntype, matchLength, UnicodeString(match->mzID, -1), status); + if (U_SUCCESS(status) && matchLength > fMaxMatchLen) { + fMaxMatchLen = matchLength; + } + } + } + } + return TRUE; +} + +TimeZoneNames::MatchInfoCollection* +TZDBNameSearchHandler::getMatches(int32_t& maxMatchLen) { + // give the ownership to the caller + TimeZoneNames::MatchInfoCollection* results = fResults; + maxMatchLen = fMaxMatchLen; + + // reset + fResults = NULL; + fMaxMatchLen = 0; + return results; +} + +U_CDECL_BEGIN +/** + * Deleter for TZDBNames + */ +static void U_CALLCONV +deleteTZDBNames(void *obj) { + if (obj != EMPTY) { + delete (TZDBNames *)obj; + } +} + +static void U_CALLCONV initTZDBNamesMap(UErrorCode &status) { + gTZDBNamesMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); + if (U_FAILURE(status)) { + gTZDBNamesMap = NULL; + return; + } + // no key deleters for tzdb name maps + uhash_setValueDeleter(gTZDBNamesMap, deleteTZDBNames); + ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES, tzdbTimeZoneNames_cleanup); +} + +/** + * Deleter for TZDBNameInfo + */ +static void U_CALLCONV +deleteTZDBNameInfo(void *obj) { + if (obj != NULL) { + uprv_free(obj); + } +} + +static void U_CALLCONV prepareFind(UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + gTZDBNamesTrie = new TextTrieMap(TRUE, deleteTZDBNameInfo); + if (gTZDBNamesTrie == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + + const UnicodeString *mzID; + StringEnumeration *mzIDs = TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status); + if (U_SUCCESS(status)) { + while ((mzID = mzIDs->snext(status)) && U_SUCCESS(status)) { + const TZDBNames *names = TZDBTimeZoneNames::getMetaZoneNames(*mzID, status); + if (names == NULL) { + continue; + } + const UChar *std = names->getName(UTZNM_SHORT_STANDARD); + const UChar *dst = names->getName(UTZNM_SHORT_DAYLIGHT); + if (std == NULL && dst == NULL) { + continue; + } + int32_t numRegions = 0; + const char **parseRegions = names->getParseRegions(numRegions); + + // The tz database contains a few zones sharing a + // same name for both standard time and daylight saving + // time. For example, Australia/Sydney observes DST, + // but "EST" is used for both standard and daylight. + // we need to store the information for later processing. + UBool ambiguousType = (std != NULL && dst != NULL && u_strcmp(std, dst) == 0); + + const UChar *uMzID = ZoneMeta::findMetaZoneID(*mzID); + if (std != NULL) { + TZDBNameInfo *stdInf = (TZDBNameInfo *)uprv_malloc(sizeof(TZDBNameInfo)); + if (stdInf == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + break; + } + stdInf->mzID = uMzID; + stdInf->type = UTZNM_SHORT_STANDARD; + stdInf->ambiguousType = ambiguousType; + stdInf->parseRegions = parseRegions; + stdInf->nRegions = numRegions; + gTZDBNamesTrie->put(std, stdInf, status); + } + if (U_SUCCESS(status) && dst != NULL) { + TZDBNameInfo *dstInf = (TZDBNameInfo *)uprv_malloc(sizeof(TZDBNameInfo)); + if (dstInf == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + break; + } + dstInf->mzID = uMzID; + dstInf->type = UTZNM_SHORT_DAYLIGHT; + dstInf->ambiguousType = ambiguousType; + dstInf->parseRegions = parseRegions; + dstInf->nRegions = numRegions; + gTZDBNamesTrie->put(dst, dstInf, status); + } + } + } + delete mzIDs; + + if (U_FAILURE(status)) { + delete gTZDBNamesTrie; + gTZDBNamesTrie = NULL; + return; + } + + ucln_i18n_registerCleanup(UCLN_I18N_TZDBTIMEZONENAMES, tzdbTimeZoneNames_cleanup); +} + +U_CDECL_END + +TZDBTimeZoneNames::TZDBTimeZoneNames(const Locale& locale) +: fLocale(locale) { + UBool useWorld = TRUE; + const char* region = fLocale.getCountry(); + int32_t regionLen = uprv_strlen(region); + if (regionLen == 0) { + UErrorCode status = U_ZERO_ERROR; + char loc[ULOC_FULLNAME_CAPACITY]; + uloc_addLikelySubtags(fLocale.getName(), loc, sizeof(loc), &status); + regionLen = uloc_getCountry(loc, fRegion, sizeof(fRegion), &status); + if (U_SUCCESS(status) && regionLen < (int32_t)sizeof(fRegion)) { + useWorld = FALSE; + } + } else if (regionLen < (int32_t)sizeof(fRegion)) { + uprv_strcpy(fRegion, region); + useWorld = FALSE; + } + if (useWorld) { + uprv_strcpy(fRegion, "001"); + } +} + +TZDBTimeZoneNames::~TZDBTimeZoneNames() { +} + +UBool +TZDBTimeZoneNames::operator==(const TimeZoneNames& other) const { + if (this == &other) { + return TRUE; + } + // No implementation for now + return FALSE; +} + +TimeZoneNames* +TZDBTimeZoneNames::clone() const { + return new TZDBTimeZoneNames(fLocale); +} + +StringEnumeration* +TZDBTimeZoneNames::getAvailableMetaZoneIDs(UErrorCode& status) const { + return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(status); +} + +StringEnumeration* +TZDBTimeZoneNames::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const { + return TimeZoneNamesImpl::_getAvailableMetaZoneIDs(tzID, status); +} + +UnicodeString& +TZDBTimeZoneNames::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const { + return TimeZoneNamesImpl::_getMetaZoneID(tzID, date, mzID); +} + +UnicodeString& +TZDBTimeZoneNames::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const { + return TimeZoneNamesImpl::_getReferenceZoneID(mzID, region, tzID); +} + +UnicodeString& +TZDBTimeZoneNames::getMetaZoneDisplayName(const UnicodeString& mzID, + UTimeZoneNameType type, + UnicodeString& name) const { + name.setToBogus(); + if (mzID.isEmpty()) { + return name; + } + + UErrorCode status = U_ZERO_ERROR; + const TZDBNames *tzdbNames = TZDBTimeZoneNames::getMetaZoneNames(mzID, status); + if (U_SUCCESS(status)) { + const UChar *s = tzdbNames->getName(type); + if (s != NULL) { + name.setTo(TRUE, s, -1); + } + } + + return name; +} + +UnicodeString& +TZDBTimeZoneNames::getTimeZoneDisplayName(const UnicodeString& /* tzID */, UTimeZoneNameType /* type */, UnicodeString& name) const { + // No abbreviations associated a zone directly for now. + name.setToBogus(); + return name; +} + +TZDBTimeZoneNames::MatchInfoCollection* +TZDBTimeZoneNames::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { + umtx_initOnce(gTZDBNamesTrieInitOnce, &prepareFind, status); + if (U_FAILURE(status)) { + return NULL; + } + + TZDBNameSearchHandler handler(types, fRegion); + gTZDBNamesTrie->search(text, start, (TextTrieMapSearchResultHandler *)&handler, status); + if (U_FAILURE(status)) { + return NULL; + } + int32_t maxLen = 0; + return handler.getMatches(maxLen); +} + +const TZDBNames* +TZDBTimeZoneNames::getMetaZoneNames(const UnicodeString& mzID, UErrorCode& status) { + umtx_initOnce(gTZDBNamesMapInitOnce, &initTZDBNamesMap, status); + if (U_FAILURE(status)) { + return NULL; + } + + TZDBNames* tzdbNames = NULL; + + UChar mzIDKey[ZID_KEY_MAX + 1]; + mzID.extract(mzIDKey, ZID_KEY_MAX + 1, status); + U_ASSERT(status == U_ZERO_ERROR); // already checked length above + mzIDKey[mzID.length()] = 0; + + umtx_lock(&gTZDBNamesMapLock); + { + void *cacheVal = uhash_get(gTZDBNamesMap, mzIDKey); + if (cacheVal == NULL) { + UResourceBundle *zoneStringsRes = ures_openDirect(U_ICUDATA_ZONE, "tzdbNames", &status); + zoneStringsRes = ures_getByKey(zoneStringsRes, gZoneStrings, zoneStringsRes, &status); + if (U_SUCCESS(status)) { + char key[ZID_KEY_MAX + 1]; + mergeTimeZoneKey(mzID, key); + tzdbNames = TZDBNames::createInstance(zoneStringsRes, key); + + if (tzdbNames == NULL) { + cacheVal = (void *)EMPTY; + } else { + cacheVal = tzdbNames; + } + // Use the persistent ID as the resource key, so we can + // avoid duplications. + const UChar* newKey = ZoneMeta::findMetaZoneID(mzID); + if (newKey != NULL) { + uhash_put(gTZDBNamesMap, (void *)newKey, cacheVal, &status); + if (U_FAILURE(status)) { + if (tzdbNames != NULL) { + delete tzdbNames; + tzdbNames = NULL; + } + } + } else { + // Should never happen with a valid input + if (tzdbNames != NULL) { + // It's not possible that we get a valid tzdbNames with unknown ID. + // But just in case.. + delete tzdbNames; + tzdbNames = NULL; + } + } + } + ures_close(zoneStringsRes); + } else if (cacheVal != EMPTY) { + tzdbNames = (TZDBNames *)cacheVal; + } + } + umtx_unlock(&gTZDBNamesMapLock); + + return tzdbNames; +} + U_NAMESPACE_END diff --git a/icu4c/source/i18n/tznames_impl.h b/icu4c/source/i18n/tznames_impl.h index c677a8b9537..5e5d96829e7 100644 --- a/icu4c/source/i18n/tznames_impl.h +++ b/icu4c/source/i18n/tznames_impl.h @@ -1,6 +1,6 @@ /* ******************************************************************************* - * Copyright (C) 2011-2013, International Business Machines Corporation and * + * Copyright (C) 2011-2014, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* */ @@ -186,6 +186,11 @@ public: static UnicodeString& getDefaultExemplarLocationName(const UnicodeString& tzID, UnicodeString& name); + static StringEnumeration* _getAvailableMetaZoneIDs(UErrorCode& status); + static StringEnumeration* _getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status); + static UnicodeString& _getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID); + static UnicodeString& _getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID); + private: Locale fLocale; @@ -207,6 +212,34 @@ private: TZNames* loadTimeZoneNames(const UnicodeString& mzId); }; +class TZDBNames; + +class TZDBTimeZoneNames : public TimeZoneNames { +public: + TZDBTimeZoneNames(const Locale& locale); + virtual ~TZDBTimeZoneNames(); + + virtual UBool operator==(const TimeZoneNames& other) const; + virtual TimeZoneNames* clone() const; + + StringEnumeration* getAvailableMetaZoneIDs(UErrorCode& status) const; + StringEnumeration* getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const; + + UnicodeString& getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const; + UnicodeString& getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const; + + UnicodeString& getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const; + UnicodeString& getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const; + + TimeZoneNames::MatchInfoCollection* find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const; + + static const TZDBNames* getMetaZoneNames(const UnicodeString& mzId, UErrorCode& status); + +private: + Locale fLocale; + char fRegion[ULOC_COUNTRY_CAPACITY]; +}; + U_NAMESPACE_END #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/i18n/ucln_in.h b/icu4c/source/i18n/ucln_in.h index a2d9ffdcc4f..f63137659aa 100644 --- a/icu4c/source/i18n/ucln_in.h +++ b/icu4c/source/i18n/ucln_in.h @@ -35,6 +35,7 @@ typedef enum ECleanupI18NType { UCLN_I18N_DANGI_CALENDAR, UCLN_I18N_CALENDAR, UCLN_I18N_TIMEZONEFORMAT, + UCLN_I18N_TZDBTIMEZONENAMES, UCLN_I18N_TIMEZONEGENERICNAMES, UCLN_I18N_TIMEZONENAMES, UCLN_I18N_ZONEMETA, diff --git a/icu4c/source/i18n/unicode/tzfmt.h b/icu4c/source/i18n/unicode/tzfmt.h index 9551f854380..53cb7f64f34 100644 --- a/icu4c/source/i18n/unicode/tzfmt.h +++ b/icu4c/source/i18n/unicode/tzfmt.h @@ -233,7 +233,15 @@ typedef enum UTimeZoneFormatParseOption { * by other styles. * @stable ICU 50 */ - UTZFMT_PARSE_OPTION_ALL_STYLES = 0x01 + UTZFMT_PARSE_OPTION_ALL_STYLES = 0x01, + /** + * When parsing a time zone display name in UTZFMT_STYLE_SPECIFIC_SHORT, + * look for the IANA tz database compatible zone abbreviations in addition + * to the localized names coming from the {@link TimeZoneNames} currently + * used by the {@link TimeZoneFormat}. + * @draft ICU 54 + */ + UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS = 0x02 } UTimeZoneFormatParseOption; U_CDECL_END @@ -241,6 +249,7 @@ U_CDECL_END U_NAMESPACE_BEGIN class TimeZoneGenericNames; +class TZDBTimeZoneNames; class UVector; /** @@ -697,6 +706,9 @@ private: UBool fAbuttingOffsetHoursAndMinutes; + /* TZDBTimeZoneNames object used for parsing */ + TZDBTimeZoneNames* fTZDBTimeZoneNames; + /** * Returns the time zone's specific format string. * @param tz the time zone @@ -727,6 +739,13 @@ private: */ const TimeZoneGenericNames* getTimeZoneGenericNames(UErrorCode& status) const; + /** + * Lazily create a TZDBTimeZoneNames instance + * @param status receives the status + * @return the cached TZDBTimeZoneNames. + */ + const TZDBTimeZoneNames* getTZDBTimeZoneNames(UErrorCode& status) const; + /** * Private method returning the time zone's exemplar location string. * This method will never return empty. diff --git a/icu4c/source/i18n/unicode/tznames.h b/icu4c/source/i18n/unicode/tznames.h index 084dec1bf8f..5ae16ef4e2e 100644 --- a/icu4c/source/i18n/unicode/tznames.h +++ b/icu4c/source/i18n/unicode/tznames.h @@ -158,15 +158,28 @@ public: virtual TimeZoneNames* clone() const = 0; /** - * Returns an instance of TimeZoneDisplayNames for the specified locale. + * Returns an instance of TimeZoneNames for the specified locale. * * @param locale The locale. * @param status Receives the status. - * @return An instance of TimeZoneDisplayNames + * @return An instance of TimeZoneNames * @stable ICU 50 */ static TimeZoneNames* U_EXPORT2 createInstance(const Locale& locale, UErrorCode& status); +#ifndef U_HIDE_DRAFT_API + /** + * Returns an instance of TimeZoneNames containing only short specific + * zone names (SHORT_STANDARD and SHORT_DAYLIGHT), + * compatible with the IANA tz database's zone abbreviations (not localized). + *
+ * Note: The input locale is used for resolving ambiguous names (e.g. "IST" is parsed + * as Israel Standard Time for Israel, while it is parsed as India Standard Time for + * all other regions). The zone names returned by this instance are not localized. + */ + static TimeZoneNames* U_EXPORT2 createTZDBInstance(const Locale& locale, UErrorCode& status); +#endif /* U_HIDE_DRAFT_API */ + /** * Returns an enumeration of all available meta zone IDs. * @param status Receives the status. diff --git a/icu4c/source/test/intltest/tzfmttst.cpp b/icu4c/source/test/intltest/tzfmttst.cpp index ffdaeae279c..3e4ece8c37f 100644 --- a/icu4c/source/test/intltest/tzfmttst.cpp +++ b/icu4c/source/test/intltest/tzfmttst.cpp @@ -1,6 +1,6 @@ /* ******************************************************************************* -* Copyright (C) 2007-2013, International Business Machines Corporation and * +* Copyright (C) 2007-2014, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* */ @@ -76,6 +76,7 @@ TimeZoneFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &name TESTCASE(2, TestParse); TESTCASE(3, TestISOFormat); TESTCASE(4, TestFormat); + TESTCASE(5, TestFormatTZDBNames); default: name = ""; break; } } @@ -685,7 +686,7 @@ typedef struct { int32_t inPos; const char* locale; UTimeZoneFormatStyle style; - UBool parseAll; + uint32_t parseOptions; const char* expected; int32_t outPos; UTimeZoneFormatTimeType timeType; @@ -694,26 +695,94 @@ typedef struct { void TimeZoneFormatTest::TestParse(void) { const ParseTestData DATA[] = { - // text inPos locale style parseAll expected outPos timeType - {"Z", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL, false, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN}, - {"Z", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG, false, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN}, - {"Zambia time", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL, true, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN}, - {"Zambia time", 0, "en_US", UTZFMT_STYLE_GENERIC_LOCATION, false, "Africa/Lusaka", 11, UTZFMT_TIME_TYPE_UNKNOWN}, - {"Zambia time", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, true, "Africa/Lusaka", 11, UTZFMT_TIME_TYPE_UNKNOWN}, - {"+00:00", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL, false, "Etc/GMT", 6, UTZFMT_TIME_TYPE_UNKNOWN}, - {"-01:30:45", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL, false, "GMT-01:30:45", 9, UTZFMT_TIME_TYPE_UNKNOWN}, - {"-7", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, false, "GMT-07:00", 2, UTZFMT_TIME_TYPE_UNKNOWN}, - {"-2222", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, false, "GMT-22:22", 5, UTZFMT_TIME_TYPE_UNKNOWN}, - {"-3333", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, false, "GMT-03:33", 4, UTZFMT_TIME_TYPE_UNKNOWN}, - {"XXX+01:30YYY", 3, "en_US", UTZFMT_STYLE_LOCALIZED_GMT, false, "GMT+01:30", 9, UTZFMT_TIME_TYPE_UNKNOWN}, - {"GMT0", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT, false, "Etc/GMT", 3, UTZFMT_TIME_TYPE_UNKNOWN}, - {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT, false, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD}, - {"ESTx", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT, false, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD}, - {"EDTx", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT, false, "America/New_York", 3, UTZFMT_TIME_TYPE_DAYLIGHT}, - {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG, false, NULL, 0, UTZFMT_TIME_TYPE_UNKNOWN}, - {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG, true, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD}, - {"EST", 0, "en_CA", UTZFMT_STYLE_SPECIFIC_SHORT, false, "America/Toronto", 3, UTZFMT_TIME_TYPE_STANDARD}, - {NULL, 0, NULL, UTZFMT_STYLE_GENERIC_LOCATION, false, NULL, 0, UTZFMT_TIME_TYPE_UNKNOWN} + // text inPos locale style + // parseOptions expected outPos timeType + {"Z", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL, + UTZFMT_PARSE_OPTION_NONE, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN}, + + {"Z", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG, + UTZFMT_PARSE_OPTION_NONE, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN}, + + {"Zambia time", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL, + UTZFMT_PARSE_OPTION_ALL_STYLES, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN}, + + {"Zambia time", 0, "en_US", UTZFMT_STYLE_GENERIC_LOCATION, + UTZFMT_PARSE_OPTION_NONE, "Africa/Lusaka", 11, UTZFMT_TIME_TYPE_UNKNOWN}, + + {"Zambia time", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, + UTZFMT_PARSE_OPTION_ALL_STYLES, "Africa/Lusaka", 11, UTZFMT_TIME_TYPE_UNKNOWN}, + + {"+00:00", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL, + UTZFMT_PARSE_OPTION_NONE, "Etc/GMT", 6, UTZFMT_TIME_TYPE_UNKNOWN}, + + {"-01:30:45", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL, + UTZFMT_PARSE_OPTION_NONE, "GMT-01:30:45", 9, UTZFMT_TIME_TYPE_UNKNOWN}, + + {"-7", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, + UTZFMT_PARSE_OPTION_NONE, "GMT-07:00", 2, UTZFMT_TIME_TYPE_UNKNOWN}, + + {"-2222", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, + UTZFMT_PARSE_OPTION_NONE, "GMT-22:22", 5, UTZFMT_TIME_TYPE_UNKNOWN}, + + {"-3333", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, + UTZFMT_PARSE_OPTION_NONE, "GMT-03:33", 4, UTZFMT_TIME_TYPE_UNKNOWN}, + + {"XXX+01:30YYY", 3, "en_US", UTZFMT_STYLE_LOCALIZED_GMT, + UTZFMT_PARSE_OPTION_NONE, "GMT+01:30", 9, UTZFMT_TIME_TYPE_UNKNOWN}, + + {"GMT0", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT, + UTZFMT_PARSE_OPTION_NONE, "Etc/GMT", 3, UTZFMT_TIME_TYPE_UNKNOWN}, + + {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT, + UTZFMT_PARSE_OPTION_NONE, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD}, + + {"ESTx", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT, + UTZFMT_PARSE_OPTION_NONE, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD}, + + {"EDTx", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT, + UTZFMT_PARSE_OPTION_NONE, "America/New_York", 3, UTZFMT_TIME_TYPE_DAYLIGHT}, + + {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG, + UTZFMT_PARSE_OPTION_NONE, NULL, 0, UTZFMT_TIME_TYPE_UNKNOWN}, + + {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG, + UTZFMT_PARSE_OPTION_ALL_STYLES, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD}, + + {"EST", 0, "en_CA", UTZFMT_STYLE_SPECIFIC_SHORT, + UTZFMT_PARSE_OPTION_NONE, "America/Toronto", 3, UTZFMT_TIME_TYPE_STANDARD}, + + {"CST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT, + UTZFMT_PARSE_OPTION_NONE, "America/Chicago", 3, UTZFMT_TIME_TYPE_STANDARD}, + + {"CST", 0, "en_GB", UTZFMT_STYLE_SPECIFIC_SHORT, + UTZFMT_PARSE_OPTION_NONE, NULL, 0, UTZFMT_TIME_TYPE_UNKNOWN}, + + {"CST", 0, "en_GB", UTZFMT_STYLE_SPECIFIC_SHORT, + UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS, "America/Chicago", 3, UTZFMT_TIME_TYPE_STANDARD}, + + {"--CST--", 2, "en_GB", UTZFMT_STYLE_SPECIFIC_SHORT, + UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS, "America/Chicago", 5, UTZFMT_TIME_TYPE_STANDARD}, + + {"CST", 0, "zh_CN", UTZFMT_STYLE_SPECIFIC_SHORT, + UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS, "Asia/Shanghai", 3, UTZFMT_TIME_TYPE_STANDARD}, + + {"EST", 0, "en_AU", UTZFMT_STYLE_SPECIFIC_SHORT, + UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS, "Australia/Sydney", 3, UTZFMT_TIME_TYPE_UNKNOWN}, + + {"AST", 0, "ar_SA", UTZFMT_STYLE_SPECIFIC_SHORT, + UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS, "Asia/Riyadh", 3, UTZFMT_TIME_TYPE_STANDARD}, + + {"AQTST", 0, "en", UTZFMT_STYLE_SPECIFIC_LONG, + UTZFMT_PARSE_OPTION_NONE, NULL, 0, UTZFMT_TIME_TYPE_UNKNOWN}, + + {"AQTST", 0, "en", UTZFMT_STYLE_SPECIFIC_LONG, + UTZFMT_PARSE_OPTION_ALL_STYLES, NULL, 0, UTZFMT_TIME_TYPE_UNKNOWN}, + + {"AQTST", 0, "en", UTZFMT_STYLE_SPECIFIC_LONG, + UTZFMT_PARSE_OPTION_ALL_STYLES | UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS, "Asia/Aqtobe", 5, UTZFMT_TIME_TYPE_DAYLIGHT}, + + {NULL, 0, NULL, UTZFMT_STYLE_GENERIC_LOCATION, + UTZFMT_PARSE_OPTION_NONE, NULL, 0, UTZFMT_TIME_TYPE_UNKNOWN} }; for (int32_t i = 0; DATA[i].text; i++) { @@ -725,8 +794,7 @@ TimeZoneFormatTest::TestParse(void) { } UTimeZoneFormatTimeType ttype = UTZFMT_TIME_TYPE_UNKNOWN; ParsePosition pos(DATA[i].inPos); - int32_t parseOptions = DATA[i].parseAll ? UTZFMT_PARSE_OPTION_ALL_STYLES : UTZFMT_PARSE_OPTION_NONE; - TimeZone* tz = tzfmt->parse(DATA[i].style, DATA[i].text, pos, parseOptions, &ttype); + TimeZone* tz = tzfmt->parse(DATA[i].style, DATA[i].text, pos, DATA[i].parseOptions, &ttype); UnicodeString errMsg; if (tz) { @@ -1025,4 +1093,102 @@ TimeZoneFormatTest::TestFormat(void) { } } +void +TimeZoneFormatTest::TestFormatTZDBNames(void) { + UDate dateJan = 1358208000000.0; // 2013-01-15T00:00:00Z + UDate dateJul = 1373846400000.0; // 2013-07-15T00:00:00Z + + const FormatTestData DATA[] = { + { + "en", + "America/Chicago", + dateJan, + UTZFMT_STYLE_SPECIFIC_SHORT, + "CST", + UTZFMT_TIME_TYPE_STANDARD + }, + { + "en", + "Asia/Shanghai", + dateJan, + UTZFMT_STYLE_SPECIFIC_SHORT, + "CST", + UTZFMT_TIME_TYPE_STANDARD + }, + { + "zh_Hans", + "Asia/Shanghai", + dateJan, + UTZFMT_STYLE_SPECIFIC_SHORT, + "CST", + UTZFMT_TIME_TYPE_STANDARD + }, + { + "en", + "America/Los_Angeles", + dateJul, + UTZFMT_STYLE_SPECIFIC_LONG, + "GMT-07:00", // No long display names + UTZFMT_TIME_TYPE_DAYLIGHT + }, + { + "ja", + "America/Los_Angeles", + dateJul, + UTZFMT_STYLE_SPECIFIC_SHORT, + "PDT", + UTZFMT_TIME_TYPE_DAYLIGHT + }, + { + "en", + "Australia/Sydney", + dateJan, + UTZFMT_STYLE_SPECIFIC_SHORT, + "EST", + UTZFMT_TIME_TYPE_DAYLIGHT + }, + { + "en", + "Australia/Sydney", + dateJul, + UTZFMT_STYLE_SPECIFIC_SHORT, + "EST", + UTZFMT_TIME_TYPE_STANDARD + }, + + {0, 0, 0.0, UTZFMT_STYLE_GENERIC_LOCATION, 0, UTZFMT_TIME_TYPE_UNKNOWN} + }; + + for (int32_t i = 0; DATA[i].locale; i++) { + UErrorCode status = U_ZERO_ERROR; + Locale loc(DATA[i].locale); + LocalPointer tzfmt(TimeZoneFormat::createInstance(loc, status)); + if (U_FAILURE(status)) { + dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status)); + continue; + } + TimeZoneNames *tzdbNames = TimeZoneNames::createTZDBInstance(loc, status); + if (U_FAILURE(status)) { + dataerrln("Fail TimeZoneNames::createTZDBInstance: %s", u_errorName(status)); + continue; + } + tzfmt->adoptTimeZoneNames(tzdbNames); + + LocalPointer tz(TimeZone::createTimeZone(DATA[i].tzid)); + UnicodeString out; + UTimeZoneFormatTimeType timeType; + + tzfmt->format(DATA[i].style, *(tz.getAlias()), DATA[i].date, out, &timeType); + UnicodeString expected(DATA[i].expected, -1, US_INV); + expected = expected.unescape(); + + assertEquals(UnicodeString("Format result for ") + DATA[i].tzid + " (Test Case " + i + ")", expected, out); + if (DATA[i].timeType != timeType) { + dataerrln(UnicodeString("Formatted time zone type (Test Case ") + i + "), returned=" + + timeType + ", expected=" + DATA[i].timeType); + } + } +} + + #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/test/intltest/tzfmttst.h b/icu4c/source/test/intltest/tzfmttst.h index 3d81a82b6dd..cb763576b40 100644 --- a/icu4c/source/test/intltest/tzfmttst.h +++ b/icu4c/source/test/intltest/tzfmttst.h @@ -1,6 +1,6 @@ /* ******************************************************************************* -* Copyright (C) 2007-2013, International Business Machines Corporation and * +* Copyright (C) 2007-2014, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* */ @@ -23,6 +23,7 @@ class TimeZoneFormatTest : public IntlTest { void TestParse(void); void TestISOFormat(void); void TestFormat(void); + void TestFormatTZDBNames(void); }; #endif /* #if !UCONFIG_NO_FORMATTING */