ICU-5990 Merging time zone formatting/parsing changes from yoshito's working branch to the trunk.
X-SVN-Rev: 22978
diff --git a/.gitattributes b/.gitattributes
index dbdf8a1..e1a82ed 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -48,6 +48,7 @@
*.spp -text
*.tri2 -text
+icu4c/source/data/misc/metazoneInfo.txt -text
icu4c/source/data/xml/collation/as.xml -text
icu4c/source/data/xml/collation/bn.xml -text
icu4c/source/data/xml/collation/cy.xml -text
diff --git a/icu4c/source/common/uloc.c b/icu4c/source/common/uloc.c
index 70b7cc0..309e2d0 100644
--- a/icu4c/source/common/uloc.c
+++ b/icu4c/source/common/uloc.c
@@ -170,7 +170,7 @@
"ty", "tyv", "udm", "ug", "uga", "uk", "umb", "und", "ur",
"uz", "vai", "ve", "vi", "vo", "vot", "wa", "wak",
"wal", "war", "was", "wen", "wo", "xal", "xh", "yao", "yap",
- "yi", "yo", "ypk", "za", "zap", "zen", "zh", "znd",
+ "yi", "yo", "ypk", "za", "zap", "zbl", "zen", "zh", "znd",
"zu", "zun", "zxx", "zza",
NULL,
"in", "iw", "ji", "jw", "sh", /* obsolete language codes */
@@ -310,8 +310,8 @@
"uzb", "vai", "ven", "vie", "vol", "vot", "wln", "wak",
/* "wal", "war", "was", "wen", "wo", "xal", "xh", "yao", "yap", */
"wal", "war", "was", "wen", "wol", "xal", "xho", "yao", "yap",
-/* "yi", "yo", "ypk", "za", "zap", "zen", "zh", "znd", */
- "yid", "yor", "ypk", "zha", "zap", "zen", "zho", "znd",
+/* "yi", "yo", "ypk", "za", "zap", "zbl", "zen", "zh", "znd", */
+ "yid", "yor", "ypk", "zha", "zap", "zbl", "zen", "zho", "znd",
/* "zu", "zun", "zxx", "zza", */
"zul", "zun", "zxx", "zza",
NULL,
diff --git a/icu4c/source/data/build.xml b/icu4c/source/data/build.xml
index 709d0b1..6c6d0c5 100644
--- a/icu4c/source/data/build.xml
+++ b/icu4c/source/data/build.xml
@@ -70,7 +70,7 @@
</taskdef>
</target>
<!-- target for generating ICU data -->
- <target name="all" depends="locales, resfiles, collation, colfiles, supplementalData, brkitr, brkfiles" />
+ <target name="all" depends="locales, resfiles, collation, colfiles, supplementalData, metazoneInfo, brkitr, brkfiles" />
<!-- parallel target -->
<target name="pall" depends="init">
@@ -237,6 +237,20 @@
</run>
</cldr-build>
</target>
+ <target name="metazoneInfo" depends="init,setup" description="builds metazoneInfo.txt from metazoneInfo.xml">
+ <cldr-build toolName="org.unicode.cldr.icu.LDML2ICUConverter" destFile="metazoneInfo.txt" noArgs="true">
+ <!-- launch the tool and generate the data after reading the config file -->
+ <run>
+ <args>
+ <arg name="-s" value="${env.CLDR_DIR}/common/supplemental" />
+ <arg name="-d" value="${env.ICU4C_DIR}/source/data/misc"/>
+ <arg name="-z"/>
+ <arg name="-f"/>
+ <arg name="-m" value="${env.CLDR_DIR}/common/supplemental" />
+ </args>
+ </run>
+ </cldr-build>
+ </target>
<target name="brkitr" depends="init,setup" description="builds break iterator files in ICU text format">
<cldr-build toolName="org.unicode.cldr.icu.LDML2ICUConverter" srcFile=".*xml" destFile=".*txt">
<run>
@@ -316,6 +330,9 @@
<fileset id="locales" dir="${env.ICU4C_DIR}/source/data/misc">
<include name="supplementalData.txt" />
</fileset>
+ <fileset id="locales" dir="${env.ICU4C_DIR}/source/data/misc">
+ <include name="metazoneInfo.txt" />
+ </fileset>
</delete>
</target>
</project>
diff --git a/icu4c/source/data/locales/bg.txt b/icu4c/source/data/locales/bg.txt
index 3ebc0a6..3f7b6b2 100644
--- a/icu4c/source/data/locales/bg.txt
+++ b/icu4c/source/data/locales/bg.txt
@@ -1413,7 +1413,7 @@
traditional{"Традиционно"}
}
}
- Version{"1.89"}
+ Version{"1.90"}
calendar{
gregorian{
AmPmMarkers{
@@ -2197,12 +2197,6 @@
"meta:GMT"{
ls{"Часова зона Гринуич"}
}
- "meta:Hawaii"{
- ld{"Лятна часова зона Хавай"}
- ls{"Часова зона Хавай"}
- sd{"HDT"}
- ss{"HST"}
- }
"meta:Israel"{
ld{"Лятна часова зона Израел"}
ls{"Часова зона Израел"}
diff --git a/icu4c/source/data/locales/cs.txt b/icu4c/source/data/locales/cs.txt
index 490e532..7b5e091 100644
--- a/icu4c/source/data/locales/cs.txt
+++ b/icu4c/source/data/locales/cs.txt
@@ -1441,7 +1441,7 @@
japanese{"Japonský kalendář"}
}
}
- Version{"1.103"}
+ Version{"1.104"}
calendar{
gregorian{
AmPmMarkers{
@@ -1723,12 +1723,6 @@
ls{"Greenwichský střední čas"}
ss{"GMT"}
}
- "meta:Hawaii"{
- ld{"Havajský letní čas"}
- ls{"Havajský standardní čas"}
- sd{"HDT"}
- ss{"HST"}
- }
"meta:Israel"{
ld{"Izraelský letní čas"}
ls{"Izraelský standardní čas"}
diff --git a/icu4c/source/data/locales/da.txt b/icu4c/source/data/locales/da.txt
index 402a167..2434aa7 100644
--- a/icu4c/source/data/locales/da.txt
+++ b/icu4c/source/data/locales/da.txt
@@ -1918,7 +1918,7 @@
traditional{"traditionel sorteringsrækkefølge"}
}
}
- Version{"1.89"}
+ Version{"1.90"}
calendar{
gregorian{
AmPmMarkers{
@@ -2270,7 +2270,6 @@
lg{"mellemeuropæisk tid"}
ls{"mellemeuropæisk normaltid"}
sd{"CEST"}
- sg{"CET"}
ss{"CET"}
}
"meta:Europe_Eastern"{
@@ -2279,7 +2278,6 @@
lg{"østeuropæisk tid"}
ls{"østeuropæisk normaltid"}
sd{"EEST"}
- sg{"EET"}
ss{"EET"}
}
"meta:Europe_Western"{
@@ -2287,23 +2285,14 @@
ld{"vesteuropæisk sommertid"}
lg{"vesteuropæisk tid"}
ls{"vesteuropæisk normaltid"}
- sg{"WET"}
+ sd{"WEST"}
+ ss{"WET"}
}
"meta:GMT"{
cu:int{1}
- ld{"GMT-sommertid"}
- lg{"verdenstid"}
ls{"Verdenstid"}
- sd{"GMT"}
- sg{"GMT"}
ss{"GMT"}
}
- "meta:Hawaii"{
- ld{"Hawaii-sommertid"}
- ls{"Hawaii-normaltid"}
- sd{"HDT"}
- ss{"HST"}
- }
"meta:Israel"{
ld{"Israelsk sommertid"}
ls{"Israelsk normaltid"}
diff --git a/icu4c/source/data/locales/en.txt b/icu4c/source/data/locales/en.txt
index 4a97bce..3c54935 100644
--- a/icu4c/source/data/locales/en.txt
+++ b/icu4c/source/data/locales/en.txt
@@ -75,6 +75,7 @@
BH{"Bahrain"}
BI{"Burundi"}
BJ{"Benin"}
+ BL{"Saint Barthélemy"}
BM{"Bermuda"}
BN{"Brunei"}
BO{"Bolivia"}
@@ -188,6 +189,7 @@
MC{"Monaco"}
MD{"Moldova"}
ME{"Montenegro"}
+ MF{"Saint Martin"}
MG{"Madagascar"}
MH{"Marshall Islands"}
MK{"Macedonia"}
@@ -1882,6 +1884,7 @@
ypk{"Yupik Language"}
za{"Zhuang"}
zap{"Zapotec"}
+ zbl{"Blissymbols"}
zen{"Zenaga"}
zh{"Chinese"}
zh_Hans{"Simplified Chinese"}
@@ -1918,6 +1921,7 @@
Scripts{
Arab{"Arabic"}
Armn{"Armenian"}
+ Avst{"Avestan"}
Bali{"Balinese"}
Batk{"Batak"}
Beng{"Bengali"}
@@ -1981,6 +1985,7 @@
Lyci{"Lycian"}
Lydi{"Lydian"}
Mand{"Mandaean"}
+ Mani{"Manichaean"}
Maya{"Mayan hieroglyphs"}
Mero{"Meroitic"}
Mlym{"Malayalam"}
@@ -1996,12 +2001,14 @@
Osma{"Osmanya"}
Perm{"Old Permic"}
Phag{"Phags-pa"}
+ Phlv{"Book Pahlavi"}
Phnx{"Phoenician"}
Plrd{"Pollard Phonetic"}
Qaai{"Inherited"}
Rjng{"Rejang"}
Roro{"Rongorongo"}
Runr{"Runic"}
+ Samr{"Samaritan"}
Sara{"Sarati"}
Saur{"Saurashtra"}
Sgnw{"SignWriting"}
@@ -2057,6 +2064,7 @@
Variants{
1606NICT{"Late Middle French to 1606"}
1901{"Traditional German orthography"}
+ 1994{"Standardized Resian orthography"}
1996{"German orthography of 1996"}
AREVELA{"Eastern Armenian"}
BAKU1926{"Unified Turkic Latin Alphabet"}
@@ -2067,6 +2075,7 @@
GAULISH{"Gaulish"}
GUOYU{"Mandarin or Standard Chinese"}
HAKKA{"Hakka"}
+ LIPAW{"The Lipovaz dialect of Resian"}
LOJBAN{"Lojban"}
MONOTON{"Monotonic"}
NEDIS{"Natisone dialect"}
@@ -2077,12 +2086,13 @@
REVISED{"Revised Orthography"}
ROZAJ{"Resian"}
SAAHO{"Saho"}
+ SCOTLAND{"Scottish Standard English"}
SCOUSE{"Scouse"}
SOLBA{"Stolvizza/Solbica dialect"}
TARASK{"Taraskievica orthography"}
XIANG{"Xiang or Hunanese"}
}
- Version{"1.158"}
+ Version{"1.161"}
calendar{
gregorian{
AmPmMarkers{
@@ -2253,8 +2263,8 @@
"meta:Acre"{
ld{"Acre Summer Time"}
ls{"Acre Time"}
- sd{"ACST"}
- ss{"ACT"}
+ sd{"ACST (Acre)"}
+ ss{"ACT (Acre)"}
}
"meta:Afghanistan"{
ls{"Afghanistan Time"}
diff --git a/icu4c/source/data/locales/en_AU.txt b/icu4c/source/data/locales/en_AU.txt
index 12bef77..1f276ae 100644
--- a/icu4c/source/data/locales/en_AU.txt
+++ b/icu4c/source/data/locales/en_AU.txt
@@ -23,7 +23,7 @@
"#,##0%",
"#E0",
}
- Version{"1.45"}
+ Version{"1.46"}
calendar{
gregorian{
DateTimeElements:intvector{
diff --git a/icu4c/source/data/locales/en_CA.txt b/icu4c/source/data/locales/en_CA.txt
index fe03b70..0a67cc5 100644
--- a/icu4c/source/data/locales/en_CA.txt
+++ b/icu4c/source/data/locales/en_CA.txt
@@ -17,7 +17,7 @@
"US Dollar",
}
}
- Version{"1.53"}
+ Version{"1.54"}
calendar{
gregorian{
DateTimeElements:intvector{
diff --git a/icu4c/source/data/locales/en_GB.txt b/icu4c/source/data/locales/en_GB.txt
index 0ce246f..d8d579e 100644
--- a/icu4c/source/data/locales/en_GB.txt
+++ b/icu4c/source/data/locales/en_GB.txt
@@ -13,7 +13,7 @@
"#,##0%",
"#E0",
}
- Version{"1.54"}
+ Version{"1.55"}
calendar{
gregorian{
DateTimePatterns{
diff --git a/icu4c/source/data/locales/en_NZ.txt b/icu4c/source/data/locales/en_NZ.txt
index ed22722..d021390 100644
--- a/icu4c/source/data/locales/en_NZ.txt
+++ b/icu4c/source/data/locales/en_NZ.txt
@@ -23,7 +23,7 @@
"#,##0%",
"#E0",
}
- Version{"1.48"}
+ Version{"1.49"}
calendar{
gregorian{
DateTimeElements:intvector{
diff --git a/icu4c/source/data/locales/en_ZA.txt b/icu4c/source/data/locales/en_ZA.txt
index c0a0a25..830d4a1 100644
--- a/icu4c/source/data/locales/en_ZA.txt
+++ b/icu4c/source/data/locales/en_ZA.txt
@@ -13,7 +13,7 @@
"#,##0%",
"#E0",
}
- Version{"1.49"}
+ Version{"1.50"}
calendar{
gregorian{
DateTimeElements:intvector{
diff --git a/icu4c/source/data/locales/en_ZW.txt b/icu4c/source/data/locales/en_ZW.txt
index 3bda53d..8cfc108 100644
--- a/icu4c/source/data/locales/en_ZW.txt
+++ b/icu4c/source/data/locales/en_ZW.txt
@@ -19,7 +19,7 @@
"#,##0%",
"#E0",
}
- Version{"1.42"}
+ Version{"1.43"}
calendar{
gregorian{
DateTimeElements:intvector{
diff --git a/icu4c/source/data/locales/es.txt b/icu4c/source/data/locales/es.txt
index 308c44f..671a34d 100644
--- a/icu4c/source/data/locales/es.txt
+++ b/icu4c/source/data/locales/es.txt
@@ -1953,7 +1953,7 @@
Variants{
REVISED{"Ortografía revisada"}
}
- Version{"1.98"}
+ Version{"1.99"}
calendar{
gregorian{
AmPmMarkers{
@@ -2496,7 +2496,7 @@
"meta:China"{
ld{"Hora de verano de China"}
ls{"Hora estándar de China"}
- ss{"CTT"}
+ ss{"CST (China)"}
}
"meta:Europe_Central"{
ld{"Hora de verano de Europa Central"}
@@ -2514,11 +2514,8 @@
ls{"Hora media de Greenwich"}
ss{"GMT"}
}
- "meta:Hawaii"{
+ "meta:Hawaii_Aleutian"{
cu:int{1}
- ld{"Hora de verano de Hawai"}
- ls{"Hora estándar de Hawai"}
- sd{"HDT"}
ss{"HST"}
}
"meta:Israel"{
@@ -2536,7 +2533,7 @@
"meta:Newfoundland"{
ld{"Hora de verano de Newfoundland"}
ls{"Hora estándar de Newfoundland"}
- ss{"CNT"}
+ ss{"NST"}
}
}
}
diff --git a/icu4c/source/data/locales/fa.txt b/icu4c/source/data/locales/fa.txt
index 0e6906e..550b99e 100644
--- a/icu4c/source/data/locales/fa.txt
+++ b/icu4c/source/data/locales/fa.txt
@@ -1056,7 +1056,7 @@
1996{"رسمالخط آلمانی ۱۹۹۶ میلادی"}
REVISED{"رسمالخط تجدیدنظرشده"}
}
- Version{"1.78"}
+ Version{"1.79"}
calendar{
gregorian{
AmPmMarkers{
@@ -1615,8 +1615,8 @@
ec{"تاهیتی"}
}
"meta:Afghanistan"{
- lg{"وقت افغانستان"}
- sg{"AFT"}
+ ls{"وقت افغانستان"}
+ ss{"AFT"}
}
"meta:Iran"{
ld{"وقت تابستانی ایران"}
diff --git a/icu4c/source/data/locales/fi.txt b/icu4c/source/data/locales/fi.txt
index c2e4ea5..9062d41 100644
--- a/icu4c/source/data/locales/fi.txt
+++ b/icu4c/source/data/locales/fi.txt
@@ -2069,7 +2069,7 @@
SCOUSE{"englannin scouse-murre"}
VALENCIA{"katalaanin valencia-murre"}
}
- Version{"1.95"}
+ Version{"1.96"}
calendar{
gregorian{
AmPmMarkers{
@@ -2756,13 +2756,6 @@
"meta:Guyana"{
cu:int{0}
}
- "meta:Hawaii"{
- cu:int{0}
- ld{"Havaijin kesäaika"}
- ls{"Havaijin normaaliaika"}
- sd{"HDT"}
- ss{"HST"}
- }
"meta:Hawaii_Aleutian"{
cu:int{0}
}
diff --git a/icu4c/source/data/locales/fr.txt b/icu4c/source/data/locales/fr.txt
index 6eced09..b160e8d 100644
--- a/icu4c/source/data/locales/fr.txt
+++ b/icu4c/source/data/locales/fr.txt
@@ -2023,7 +2023,7 @@
SCOUSE{"dialecte scouse"}
VALENCIA{"valencien"}
}
- Version{"1.106"}
+ Version{"1.107"}
calendar{
chinese{
monthNames{
@@ -2639,13 +2639,6 @@
sd{"HAEE"}
ss{"HEE"}
}
- "meta:Hawaii"{
- ld{"Heure avancée d’Hawaï"}
- lg{"Heure d’Hawaï"}
- ls{"Heure normale d’Hawaï"}
- sd{"HDT"}
- ss{"HST"}
- }
"meta:Israel"{
ld{"Heure avancée d’Israël"}
ls{"Heure normale d’Israël"}
diff --git a/icu4c/source/data/locales/fr_CA.txt b/icu4c/source/data/locales/fr_CA.txt
index b72af75..5dc68ab 100644
--- a/icu4c/source/data/locales/fr_CA.txt
+++ b/icu4c/source/data/locales/fr_CA.txt
@@ -23,7 +23,7 @@
"#,##0 %",
"#E0",
}
- Version{"1.49"}
+ Version{"1.50"}
calendar{
gregorian{
DateTimeElements:intvector{
diff --git a/icu4c/source/data/locales/hu.txt b/icu4c/source/data/locales/hu.txt
index 2e1bc2a..7711795 100644
--- a/icu4c/source/data/locales/hu.txt
+++ b/icu4c/source/data/locales/hu.txt
@@ -1748,7 +1748,7 @@
NEDIS{"Natisone dialektus"}
REVISED{"Átdolgozott helyesírás"}
}
- Version{"1.88"}
+ Version{"1.89"}
calendar{
buddhist{
eras{
diff --git a/icu4c/source/data/locales/is.txt b/icu4c/source/data/locales/is.txt
index 6b7716a..91ee093 100644
--- a/icu4c/source/data/locales/is.txt
+++ b/icu4c/source/data/locales/is.txt
@@ -1404,7 +1404,7 @@
traditional{"Hefðbundin"}
}
}
- Version{"1.76"}
+ Version{"1.77"}
calendar{
gregorian{
DateTimePatterns{
diff --git a/icu4c/source/data/locales/it.txt b/icu4c/source/data/locales/it.txt
index 0df2b3d..c57ed6a 100644
--- a/icu4c/source/data/locales/it.txt
+++ b/icu4c/source/data/locales/it.txt
@@ -1956,7 +1956,7 @@
ROZAJ{"resiano"}
SAAHO{"saho"}
}
- Version{"1.92"}
+ Version{"1.93"}
calendar{
gregorian{
AmPmMarkers{
@@ -2325,7 +2325,7 @@
ls{"Ora Standard Alaska"}
}
"meta:China"{
- ld{"Ora Standard Cina"}
+ ld{"Ora Legale Cina"}
ls{"Ora Standard Cina"}
sd{"CDT (Cina)"}
ss{"CST (Cina)"}
diff --git a/icu4c/source/data/locales/ja.txt b/icu4c/source/data/locales/ja.txt
index 7d381d8..f283390 100644
--- a/icu4c/source/data/locales/ja.txt
+++ b/icu4c/source/data/locales/ja.txt
@@ -1975,7 +1975,7 @@
POSIX{"コンピュータ"}
REVISED{"改訂版"}
}
- Version{"1.114"}
+ Version{"1.115"}
calendar{
gregorian{
AmPmMarkers{
@@ -3044,12 +3044,6 @@
ls{"グリニッジ標準時"}
ss{"GMT"}
}
- "meta:Hawaii"{
- ld{"ハワイ夏時間"}
- ls{"ハワイ標準時"}
- sd{"HDT"}
- ss{"HST"}
- }
"meta:Israel"{
ld{"イスラエル夏時間"}
ls{"イスラエル標準時"}
diff --git a/icu4c/source/data/locales/ko.txt b/icu4c/source/data/locales/ko.txt
index fdaf990..1d2032f 100644
--- a/icu4c/source/data/locales/ko.txt
+++ b/icu4c/source/data/locales/ko.txt
@@ -1906,7 +1906,7 @@
Variants{
REVISED{"개정"}
}
- Version{"1.95"}
+ Version{"1.96"}
calendar{
buddhist{
eras{
@@ -2951,12 +2951,6 @@
ls{"그리니치 표준시"}
ss{"GMT"}
}
- "meta:Hawaii"{
- ld{"하와이 하계 표준시"}
- ls{"하와이 표준시"}
- sd{"HDT"}
- ss{"HST"}
- }
"meta:Israel"{
ld{"이스라엘 하계 표준시"}
ls{"이스라엘 표준시"}
diff --git a/icu4c/source/data/locales/nl.txt b/icu4c/source/data/locales/nl.txt
index ee99102..96ff3b0 100644
--- a/icu4c/source/data/locales/nl.txt
+++ b/icu4c/source/data/locales/nl.txt
@@ -1996,7 +1996,7 @@
SAAHO{"Saho"}
SCOUSE{"Liverpools (Scouse)"}
}
- Version{"1.93"}
+ Version{"1.94"}
calendar{
gregorian{
DateTimePatterns{
@@ -2516,12 +2516,6 @@
ls{"Greenwich Mean Time"}
ss{"GMT"}
}
- "meta:Hawaii"{
- ld{"Hawaï-zomertijd"}
- ls{"Hawaï-standaardtijd"}
- sd{"HDT"}
- ss{"HST"}
- }
"meta:Israel"{
ld{"Israëlische zomertijd"}
ls{"Israëlische standaardtijd"}
diff --git a/icu4c/source/data/locales/pt.txt b/icu4c/source/data/locales/pt.txt
index 5c49c04..414128d 100644
--- a/icu4c/source/data/locales/pt.txt
+++ b/icu4c/source/data/locales/pt.txt
@@ -1991,7 +1991,7 @@
SAAHO{"saho"}
SCOUSE{"scouse"}
}
- Version{"1.89"}
+ Version{"1.90"}
calendar{
buddhist{
DateTimePatterns{
@@ -2443,7 +2443,7 @@
lg{"Horário da Austrália Central"}
ls{"Horário Padrão da Austrália Central"}
sd{"ACDT"}
- ss{"ACST"}
+ ss{"ACST (Austrália)"}
}
"meta:Australia_Eastern"{
ld{"Horário de Verão da Austrália Oriental"}
@@ -2611,21 +2611,9 @@
ls{"Horário Padrão de Gulf"}
ss{"GST"}
}
- "meta:Hawaii"{
- ld{"Horário de Verão do Havaí"}
- lg{"Horário do Havaí"}
- ls{"Horário Padrão do Havaí"}
- sd{"HDT"}
- sg{"HT"}
- ss{"HST"}
- }
"meta:Hawaii_Aleutian"{
- ld{"Horário de Verão do Havaí-Aleuta"}
- lg{"Horário do Havaí-Aleuta"}
ls{"Horário Padrão do Havaí-Aleuta"}
- sd{"HADT"}
- sg{"HAT"}
- ss{"HAST"}
+ ss{"HST"}
}
"meta:India"{
ls{"Horário Padrão da Índia"}
@@ -2647,7 +2635,7 @@
ld{"Horário de Verão de Israel"}
ls{"Horário Padrão de Israel"}
sd{"IDT"}
- ss{"IST"}
+ ss{"IST (Israel)"}
}
"meta:Japan"{
ld{"Horário de Verão do Japão"}
diff --git a/icu4c/source/data/locales/pt_PT.txt b/icu4c/source/data/locales/pt_PT.txt
index 328c03c..b0dd24b 100644
--- a/icu4c/source/data/locales/pt_PT.txt
+++ b/icu4c/source/data/locales/pt_PT.txt
@@ -199,7 +199,7 @@
phonebook{"Ordem da Lista Telefónica"}
}
}
- Version{"1.63"}
+ Version{"1.64"}
calendar{
buddhist{
DateTimePatterns{
@@ -400,10 +400,6 @@
"meta:Europe_Central"{
ls{"Horário Padrão da Europa Central"}
}
- "meta:Hawaii"{
- ld{"Horário de Verão do Havai"}
- ls{"Horário Padrão do Havai"}
- }
"meta:Newfoundland"{
ld{"Horário de Verão da Terra Nova"}
ls{"Horário Padrão da Terra Nova"}
diff --git a/icu4c/source/data/locales/resfiles.mk b/icu4c/source/data/locales/resfiles.mk
index 9f837e9..675f34b 100644
--- a/icu4c/source/data/locales/resfiles.mk
+++ b/icu4c/source/data/locales/resfiles.mk
@@ -16,13 +16,11 @@
# * To add an additional locale to the list:
# _____________________________________________________
# | GENRB_SOURCE_LOCAL = myLocale.txt ...
-# | GENRB_ALIAS_SOURCE_LOCAL = myAliasLocale.txt ...
#
# * To REPLACE the default list and only build with a few
# locale:
# _____________________________________________________
# | GENRB_SOURCE = ar.txt ar_AE.txt en.txt de.txt zh.txt
-# | GENRB_ALIAS_SOURCE = az_AZ.txt zh_CN.txt ...
#
#
# Generated by LDML2ICUConverter, from LDML source files.
diff --git a/icu4c/source/data/locales/root.txt b/icu4c/source/data/locales/root.txt
index e018960..09376ad 100644
--- a/icu4c/source/data/locales/root.txt
+++ b/icu4c/source/data/locales/root.txt
@@ -70,7 +70,7 @@
297,
210,
}
- Version{"1.121"}
+ Version{"1.123"}
calendar{
buddhist{
AmPmMarkers{
@@ -5075,7 +5075,7 @@
"1994-04-30 21:00",
}
mz3{
- "Moscow",
+ "Europe_Eastern",
"1997-03-30 01:00",
"9999-12-31 23:59",
}
diff --git a/icu4c/source/data/locales/sv.txt b/icu4c/source/data/locales/sv.txt
index a4e8d8a..ab36b3c 100644
--- a/icu4c/source/data/locales/sv.txt
+++ b/icu4c/source/data/locales/sv.txt
@@ -2048,7 +2048,7 @@
SCOUSE{"scouse"}
VALENCIA{"valensisk dialekt"}
}
- Version{"1.108"}
+ Version{"1.109"}
calendar{
gregorian{
AmPmMarkers{
@@ -2497,29 +2497,25 @@
lg{"västbrasiliansk tid"}
}
"meta:Africa_Central"{
- lg{"centralafrikansk tid"}
ls{"centralafrikansk tid"}
- sg{"CAT"}
ss{"CAT"}
}
"meta:Africa_Eastern"{
lg{"östafrikansk tid"}
ls{"östafrikansk normaltid"}
- sg{"EAT"}
ss{"EAT"}
}
"meta:Africa_Southern"{
lg{"sydafrikansk tid"}
ls{"sydafrikansk normaltid"}
- sd{"sydafrikansk sommartid"}
- ss{"SAT"}
+ sg{"SAT"}
+ ss{"SAST"}
}
"meta:Africa_Western"{
ld{"västafrikansk sommartid"}
lg{"västafrikansk tid"}
ls{"västafrikansk normaltid"}
sd{"VAST"}
- sg{"WAT"}
ss{"WAT"}
}
"meta:Alaska"{
@@ -2568,8 +2564,8 @@
ld{"saudiarabisk sommartid"}
lg{"saudiarabisk tid"}
ls{"saudiarabisk normaltid"}
- sg{"AT"}
- ss{"AST"}
+ sg{"AT (saudiarabisk)"}
+ ss{"AST (saudiarabisk)"}
}
"meta:Argentina"{
lg{"östargentinsk tid"}
@@ -2660,7 +2656,6 @@
lg{"centraleuropeisk tid"}
ls{"Centraleuropa, normaltid"}
sd{"CEST"}
- sg{"CET"}
ss{"CET"}
}
"meta:Europe_Eastern"{
@@ -2669,7 +2664,6 @@
lg{"östeuropeisk tid"}
ls{"Östeuropa, normaltid"}
sd{"EEST"}
- sg{"EET"}
ss{"EET"}
}
"meta:Europe_Western"{
@@ -2678,16 +2672,11 @@
lg{"västeuropeisk tid"}
ls{"västeuropeisk normaltid"}
sd{"WEST"}
- sg{"WET"}
ss{"WET"}
}
"meta:GMT"{
cu:int{1}
- ld{"greenwichtid"}
- lg{"greenwichtid"}
ls{"Greenwichtid"}
- sd{"GMT"}
- sg{"GMT"}
ss{"GMT"}
}
"meta:Galapagos"{
@@ -2714,13 +2703,6 @@
"meta:Guam"{
lg{"Guamtid"}
}
- "meta:Hawaii"{
- ld{"Hawaii, sommartid"}
- lg{"hawaiiansk tid"}
- ls{"Hawaii, normaltid"}
- sd{"HDT"}
- ss{"HST"}
- }
"meta:India"{
lg{"indisk tid"}
}
@@ -2741,7 +2723,7 @@
lg{"israelisk tid"}
ls{"Israel, normaltid"}
sd{"IDT"}
- ss{"IST"}
+ ss{"IST (Israel)"}
}
"meta:Japan"{
ld{"Japan, sommartid"}
@@ -2776,8 +2758,8 @@
ld{"Newfoundland, sommartid"}
lg{"New Foundland-tid"}
ls{"Newfoundland, normaltid"}
- sd{"CDT"}
- ss{"CNT"}
+ sd{"NDT"}
+ ss{"NST"}
}
"meta:Pakistan"{
lg{"pakistansk tid"}
diff --git a/icu4c/source/data/locales/th.txt b/icu4c/source/data/locales/th.txt
index 7d6f70c..2d6df24 100644
--- a/icu4c/source/data/locales/th.txt
+++ b/icu4c/source/data/locales/th.txt
@@ -1964,7 +1964,7 @@
ROZAJ{"เรเซียน"}
SAAHO{"ซาโฮ"}
}
- Version{"1.94"}
+ Version{"1.95"}
calendar{
buddhist{
DateTimePatterns{
diff --git a/icu4c/source/data/locales/tr.txt b/icu4c/source/data/locales/tr.txt
index bb5e84b..3f5e9af 100644
--- a/icu4c/source/data/locales/tr.txt
+++ b/icu4c/source/data/locales/tr.txt
@@ -2041,7 +2041,7 @@
SAAHO{"Saho"}
SCOUSE{"Scouse"}
}
- Version{"1.91"}
+ Version{"1.92"}
calendar{
coptic{
monthNames{
@@ -2435,8 +2435,8 @@
"meta:Acre"{
ld{"Acre Yaz Saati"}
ls{"Acre Saati"}
- sd{"ACST"}
- ss{"ACT"}
+ sd{"ACST (Acre)"}
+ ss{"ACT (Acre)"}
}
"meta:Afghanistan"{
ls{"Afganistan Saati"}
@@ -2842,21 +2842,9 @@
ls{"Guyana Saati"}
ss{"GYT"}
}
- "meta:Hawaii"{
- ld{"Hawaii Yaz Saati"}
- lg{"Hawaii Saati"}
- ls{"Hawaii Standart Saati"}
- sd{"HDT"}
- sg{"HT"}
- ss{"HST"}
- }
"meta:Hawaii_Aleutian"{
- ld{"Hawaii-Aleutian Yaz Saati"}
- lg{"Hawaii-Aleutian Saati"}
ls{"Hawaii-Aleutian Standart Saati"}
- sd{"HADT"}
- sg{"HAT"}
- ss{"HAST"}
+ ss{"HST"}
}
"meta:Hong_Kong"{
ld{"Hong Kong Yaz Saati"}
@@ -3207,7 +3195,7 @@
}
"meta:South_Georgia"{
ls{"Güney Georgia Saati"}
- ss{"GST (S. Georgia)"}
+ ss{"GST (Güney Georgia)"}
}
"meta:Suriname"{
ls{"Surinam Saati"}
diff --git a/icu4c/source/data/locales/zh.txt b/icu4c/source/data/locales/zh.txt
index c840c39..16a4e28 100644
--- a/icu4c/source/data/locales/zh.txt
+++ b/icu4c/source/data/locales/zh.txt
@@ -2026,7 +2026,7 @@
REVISED{"已修订的拼字学"}
SAAHO{"萨霍"}
}
- Version{"1.104"}
+ Version{"1.105"}
calendar{
gregorian{
AmPmMarkers{
@@ -3278,17 +3278,14 @@
ls{"Acre 标准时间"}
}
"meta:Africa_Central"{
- ld{"非洲中部夏令时间"}
lg{"非洲中部时间"}
ls{"中部非洲标准时间"}
}
"meta:Africa_Eastern"{
- ld{"非洲东部夏令时间"}
lg{"非洲东部时间"}
ls{"东部非洲标准时间"}
}
"meta:Africa_Southern"{
- ld{"非洲南部夏令时间"}
lg{"非洲南部时间"}
ls{"南部非洲标准时间"}
}
@@ -3375,17 +3372,11 @@
ls{"格林尼治标准时间"}
ss{"GMT"}
}
- "meta:Hawaii"{
- ld{"夏威夷夏令时间"}
- ls{"夏威夷标准时间"}
- sd{"HDT"}
- ss{"HST"}
- }
"meta:Israel"{
ld{"以色列夏令时间"}
ls{"以色列标准时间"}
sd{"IDT"}
- ss{"IST"}
+ ss{"IST (Israel)"}
}
"meta:Japan"{
ld{"日本夏令时间"}
diff --git a/icu4c/source/data/locales/zh_Hant.txt b/icu4c/source/data/locales/zh_Hant.txt
index eb42778..ca20d90 100644
--- a/icu4c/source/data/locales/zh_Hant.txt
+++ b/icu4c/source/data/locales/zh_Hant.txt
@@ -1662,7 +1662,7 @@
REVISED{"已修訂"}
SAAHO{"SAAHO"}
}
- Version{"1.89"}
+ Version{"1.90"}
calendar{
gregorian{
DateTimePatterns{
@@ -2511,9 +2511,6 @@
"meta:GMT"{
ls{"格林威治標準時間"}
}
- "meta:Hawaii"{
- ls{"夏威夷標準時間"}
- }
"meta:Israel"{
ls{"以色列標準時間"}
}
diff --git a/icu4c/source/data/misc/metazoneInfo.txt b/icu4c/source/data/misc/metazoneInfo.txt
new file mode 100755
index 0000000..801c8b9
--- /dev/null
+++ b/icu4c/source/data/misc/metazoneInfo.txt
@@ -0,0 +1,3719 @@
+// ***************************************************************************
+// *
+// * Copyright (C) 2007 International Business Machines
+// * Corporation and others. All Rights Reserved.
+// * Tool: com.ibm.icu.dev.tool.cldr.LDML2ICUConverter.java
+// * Source File:<path>/metazoneInfo.xml
+// *
+// ***************************************************************************
+metazoneInfo:table(nofallback){
+ metazoneMappings{
+ "Africa:Abidjan"{
+ mz0{
+ "GMT",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Accra"{
+ mz0{
+ "GMT",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Addis_Ababa"{
+ mz0{
+ "Africa_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Algiers"{
+ mz0{
+ "Europe_Western",
+ "1970-01-01 00:00",
+ "1977-10-20 23:00",
+ }
+ mz1{
+ "Europe_Central",
+ "1977-10-20 23:00",
+ "1979-10-25 23:00",
+ }
+ mz2{
+ "Europe_Western",
+ "1979-10-25 23:00",
+ "1981-05-01 00:00",
+ }
+ mz3{
+ "Europe_Central",
+ "1981-05-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Asmera"{
+ mz0{
+ "Africa_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Bamako"{
+ mz0{
+ "GMT",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Bangui"{
+ mz0{
+ "Africa_Western",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Banjul"{
+ mz0{
+ "GMT",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Bissau"{
+ mz0{
+ "Africa_FarWestern",
+ "1970-01-01 00:00",
+ "1975-01-01 01:00",
+ }
+ mz1{
+ "GMT",
+ "1975-01-01 01:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Blantyre"{
+ mz0{
+ "Africa_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Brazzaville"{
+ mz0{
+ "Africa_Western",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Bujumbura"{
+ mz0{
+ "Africa_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Cairo"{
+ mz0{
+ "Europe_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Casablanca"{
+ mz0{
+ "Europe_Western",
+ "1970-01-01 00:00",
+ "1984-03-16 00:00",
+ }
+ mz1{
+ "Europe_Central",
+ "1984-03-16 00:00",
+ "1985-12-31 23:00",
+ }
+ mz2{
+ "Europe_Western",
+ "1985-12-31 23:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Ceuta"{
+ mz0{
+ "Europe_Western",
+ "1970-01-01 00:00",
+ "1984-03-16 00:00",
+ }
+ mz1{
+ "Europe_Central",
+ "1984-03-16 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Conakry"{
+ mz0{
+ "GMT",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Dakar"{
+ mz0{
+ "GMT",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Dar_es_Salaam"{
+ mz0{
+ "Africa_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Djibouti"{
+ mz0{
+ "Africa_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Douala"{
+ mz0{
+ "Africa_Western",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:El_Aaiun"{
+ mz0{
+ "Africa_FarWestern",
+ "1970-01-01 00:00",
+ "1976-04-14 01:00",
+ }
+ mz1{
+ "Europe_Western",
+ "1976-04-14 01:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Freetown"{
+ mz0{
+ "GMT",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Gaborone"{
+ mz0{
+ "Africa_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Harare"{
+ mz0{
+ "Africa_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Johannesburg"{
+ mz0{
+ "Africa_Southern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Kampala"{
+ mz0{
+ "Africa_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Khartoum"{
+ mz0{
+ "Africa_Central",
+ "1970-01-01 00:00",
+ "2000-01-15 10:00",
+ }
+ mz1{
+ "Africa_Eastern",
+ "2000-01-15 10:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Kigali"{
+ mz0{
+ "Africa_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Kinshasa"{
+ mz0{
+ "Africa_Western",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Lagos"{
+ mz0{
+ "Africa_Western",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Libreville"{
+ mz0{
+ "Africa_Western",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Lome"{
+ mz0{
+ "GMT",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Luanda"{
+ mz0{
+ "Africa_Western",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Lubumbashi"{
+ mz0{
+ "Africa_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Lusaka"{
+ mz0{
+ "Africa_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Malabo"{
+ mz0{
+ "Africa_Western",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Maputo"{
+ mz0{
+ "Africa_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Maseru"{
+ mz0{
+ "Africa_Southern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Mbabane"{
+ mz0{
+ "Africa_Southern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Mogadishu"{
+ mz0{
+ "Africa_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Monrovia"{
+ mz0{
+ "Liberia",
+ "1970-01-01 00:00",
+ "1972-05-01 00:45",
+ }
+ mz1{
+ "GMT",
+ "1972-05-01 00:45",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Nairobi"{
+ mz0{
+ "Africa_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Ndjamena"{
+ mz0{
+ "Africa_Western",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Niamey"{
+ mz0{
+ "Africa_Western",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Nouakchott"{
+ mz0{
+ "GMT",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Ouagadougou"{
+ mz0{
+ "GMT",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Porto-Novo"{
+ mz0{
+ "Africa_Western",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Sao_Tome"{
+ mz0{
+ "GMT",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Tripoli"{
+ mz0{
+ "Europe_Eastern",
+ "1970-01-01 00:00",
+ "1981-12-31 22:00",
+ }
+ mz1{
+ "Europe_Central",
+ "1981-12-31 22:00",
+ "1990-05-03 23:00",
+ }
+ mz2{
+ "Europe_Eastern",
+ "1990-05-03 23:00",
+ "1996-09-29 22:00",
+ }
+ mz3{
+ "Europe_Central",
+ "1996-09-29 22:00",
+ "1997-10-03 22:00",
+ }
+ mz4{
+ "Europe_Eastern",
+ "1997-10-03 22:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Tunis"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Africa:Windhoek"{
+ mz0{
+ "Africa_Southern",
+ "1970-01-01 00:00",
+ "1990-03-20 22:00",
+ }
+ mz1{
+ "Africa_Central",
+ "1990-03-20 22:00",
+ "1994-04-02 22:00",
+ }
+ mz2{
+ "Africa_Western",
+ "1994-04-02 22:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Adak"{
+ mz0{
+ "Bering",
+ "1970-01-01 00:00",
+ "1983-10-30 12:00",
+ }
+ mz1{
+ "Hawaii_Aleutian",
+ "1983-10-30 12:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Anchorage"{
+ mz0{
+ "Alaska_Hawaii",
+ "1970-01-01 00:00",
+ "1983-10-30 11:00",
+ }
+ mz1{
+ "Alaska",
+ "1983-10-30 11:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Anguilla"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Antigua"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Araguaina"{
+ mz0{
+ "Brasilia",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Argentina:La_Rioja"{
+ mz0{
+ "Argentina",
+ "1970-01-01 00:00",
+ "1991-03-01 02:00",
+ }
+ mz1{
+ "Argentina_Western",
+ "1991-03-01 02:00",
+ "1991-05-07 04:00",
+ }
+ mz2{
+ "Argentina",
+ "1991-05-07 04:00",
+ "2004-06-01 03:00",
+ }
+ mz3{
+ "Argentina_Western",
+ "2004-06-01 03:00",
+ "2004-06-20 04:00",
+ }
+ mz4{
+ "Argentina",
+ "2004-06-20 04:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Argentina:Rio_Gallegos"{
+ mz0{
+ "Argentina",
+ "1970-01-01 00:00",
+ "2004-06-01 03:00",
+ }
+ mz1{
+ "Argentina_Western",
+ "2004-06-01 03:00",
+ "2004-06-20 04:00",
+ }
+ mz2{
+ "Argentina",
+ "2004-06-20 04:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Argentina:San_Juan"{
+ mz0{
+ "Argentina",
+ "1970-01-01 00:00",
+ "1991-03-01 02:00",
+ }
+ mz1{
+ "Argentina_Western",
+ "1991-03-01 02:00",
+ "1991-05-07 04:00",
+ }
+ mz2{
+ "Argentina",
+ "1991-05-07 04:00",
+ "2004-05-31 03:00",
+ }
+ mz3{
+ "Argentina_Western",
+ "2004-05-31 03:00",
+ "2004-07-25 04:00",
+ }
+ mz4{
+ "Argentina",
+ "2004-07-25 04:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Argentina:Tucuman"{
+ mz0{
+ "Argentina",
+ "1970-01-01 00:00",
+ "1991-03-03 02:00",
+ }
+ mz1{
+ "Argentina_Western",
+ "1991-03-03 02:00",
+ "1991-10-20 04:00",
+ }
+ mz2{
+ "Argentina",
+ "1991-10-20 04:00",
+ "2004-06-01 03:00",
+ }
+ mz3{
+ "Argentina_Western",
+ "2004-06-01 03:00",
+ "2004-06-13 04:00",
+ }
+ mz4{
+ "Argentina",
+ "2004-06-13 04:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Argentina:Ushuaia"{
+ mz0{
+ "Argentina",
+ "1970-01-01 00:00",
+ "2004-05-30 03:00",
+ }
+ mz1{
+ "Argentina_Western",
+ "2004-05-30 03:00",
+ "2004-06-20 04:00",
+ }
+ mz2{
+ "Argentina",
+ "2004-06-20 04:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Aruba"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Asuncion"{
+ mz0{
+ "Paraguay",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Bahia"{
+ mz0{
+ "Brasilia",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Barbados"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Belem"{
+ mz0{
+ "Brasilia",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Belize"{
+ mz0{
+ "America_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Blanc-Sablon"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Boa_Vista"{
+ mz0{
+ "Amazon",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Bogota"{
+ mz0{
+ "Colombia",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Boise"{
+ mz0{
+ "America_Mountain",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Buenos_Aires"{
+ mz0{
+ "Argentina",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Cambridge_Bay"{
+ mz0{
+ "America_Mountain",
+ "1970-01-01 00:00",
+ "1999-10-31 08:00",
+ }
+ mz1{
+ "America_Central",
+ "1999-10-31 08:00",
+ "2000-10-29 07:00",
+ }
+ mz2{
+ "America_Eastern",
+ "2000-10-29 07:00",
+ "2000-11-05 05:00",
+ }
+ mz3{
+ "America_Central",
+ "2000-11-05 05:00",
+ "2001-04-01 09:00",
+ }
+ mz4{
+ "America_Mountain",
+ "2001-04-01 09:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Campo_Grande"{
+ mz0{
+ "Amazon",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Cancun"{
+ mz0{
+ "America_Central",
+ "1970-01-01 00:00",
+ "1981-12-23 06:00",
+ }
+ mz1{
+ "America_Eastern",
+ "1981-12-23 06:00",
+ "1998-08-02 06:00",
+ }
+ mz2{
+ "America_Central",
+ "1998-08-02 06:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Caracas"{
+ mz0{
+ "Venezuela",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Catamarca"{
+ mz0{
+ "Argentina",
+ "1970-01-01 00:00",
+ "1991-03-03 02:00",
+ }
+ mz1{
+ "Argentina_Western",
+ "1991-03-03 02:00",
+ "1991-10-20 04:00",
+ }
+ mz2{
+ "Argentina",
+ "1991-10-20 04:00",
+ "2004-06-01 03:00",
+ }
+ mz3{
+ "Argentina_Western",
+ "2004-06-01 03:00",
+ "2004-06-20 04:00",
+ }
+ mz4{
+ "Argentina",
+ "2004-06-20 04:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Cayenne"{
+ mz0{
+ "French_Guiana",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Cayman"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Chicago"{
+ mz0{
+ "America_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Chihuahua"{
+ mz0{
+ "America_Central",
+ "1970-01-01 00:00",
+ "1998-04-05 09:00",
+ }
+ mz1{
+ "America_Mountain",
+ "1998-04-05 09:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Coral_Harbour"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Cordoba"{
+ mz0{
+ "Argentina",
+ "1970-01-01 00:00",
+ "1991-03-03 02:00",
+ }
+ mz1{
+ "Argentina_Western",
+ "1991-03-03 02:00",
+ "1991-10-20 04:00",
+ }
+ mz2{
+ "Argentina",
+ "1991-10-20 04:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Costa_Rica"{
+ mz0{
+ "America_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Cuiaba"{
+ mz0{
+ "Amazon",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Curacao"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Danmarkshavn"{
+ mz0{
+ "Greenland_Western",
+ "1970-01-01 00:00",
+ "1996-01-01 03:00",
+ }
+ mz1{
+ "GMT",
+ "1996-01-01 03:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Dawson"{
+ mz0{
+ "Yukon",
+ "1970-01-01 00:00",
+ "1973-10-28 09:00",
+ }
+ mz1{
+ "America_Pacific",
+ "1973-10-28 09:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Dawson_Creek"{
+ mz0{
+ "America_Pacific",
+ "1970-01-01 00:00",
+ "1972-08-30 09:00",
+ }
+ mz1{
+ "America_Mountain",
+ "1972-08-30 09:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Denver"{
+ mz0{
+ "America_Mountain",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Detroit"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Dominica"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Edmonton"{
+ mz0{
+ "America_Mountain",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Eirunepe"{
+ mz0{
+ "Acre",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:El_Salvador"{
+ mz0{
+ "America_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Fortaleza"{
+ mz0{
+ "Brasilia",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Glace_Bay"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Godthab"{
+ mz0{
+ "Greenland_Western",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Goose_Bay"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "1988-04-03 04:01",
+ }
+ mz1{
+ "Goose_Bay",
+ "1988-04-03 04:01",
+ "1988-10-30 02:01",
+ }
+ mz2{
+ "Atlantic",
+ "1988-10-30 02:01",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Grand_Turk"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Grenada"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Guadeloupe"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Guatemala"{
+ mz0{
+ "America_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Guayaquil"{
+ mz0{
+ "Ecuador",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Guyana"{
+ mz0{
+ "Guyana",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Halifax"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Havana"{
+ mz0{
+ "Cuba",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Hermosillo"{
+ mz0{
+ "America_Pacific",
+ "1970-01-01 00:00",
+ "1970-01-01 08:00",
+ }
+ mz1{
+ "America_Mountain",
+ "1970-01-01 08:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Indiana:Knox"{
+ mz0{
+ "America_Central",
+ "1970-01-01 00:00",
+ "1991-10-27 07:00",
+ }
+ mz1{
+ "America_Eastern",
+ "1991-10-27 07:00",
+ "2006-04-02 07:00",
+ }
+ mz2{
+ "America_Central",
+ "2006-04-02 07:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Indiana:Marengo"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "1974-01-06 07:00",
+ }
+ mz1{
+ "America_Central",
+ "1974-01-06 07:00",
+ "1974-10-27 07:00",
+ }
+ mz2{
+ "America_Eastern",
+ "1974-10-27 07:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Indiana:Petersburg"{
+ mz0{
+ "America_Central",
+ "1970-01-01 00:00",
+ "1977-10-30 07:00",
+ }
+ mz1{
+ "America_Eastern",
+ "1977-10-30 07:00",
+ "2006-04-02 07:00",
+ }
+ mz2{
+ "America_Central",
+ "2006-04-02 07:00",
+ "2007-11-04 07:00",
+ }
+ mz3{
+ "America_Eastern",
+ "2007-11-04 07:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Indiana:Tell_City"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "2006-04-02 07:00",
+ }
+ mz1{
+ "America_Central",
+ "2006-04-02 07:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Indiana:Vevay"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Indiana:Vincennes"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "2006-04-02 07:00",
+ }
+ mz1{
+ "America_Central",
+ "2006-04-02 07:00",
+ "2007-11-04 07:00",
+ }
+ mz2{
+ "America_Eastern",
+ "2007-11-04 07:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Indiana:Winamac"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "2006-04-02 07:00",
+ }
+ mz1{
+ "America_Central",
+ "2006-04-02 07:00",
+ "2007-03-11 08:00",
+ }
+ mz2{
+ "America_Eastern",
+ "2007-03-11 08:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Indianapolis"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Inuvik"{
+ mz0{
+ "America_Pacific",
+ "1970-01-01 00:00",
+ "1979-04-29 10:00",
+ }
+ mz1{
+ "America_Mountain",
+ "1979-04-29 10:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Iqaluit"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "1999-10-31 06:00",
+ }
+ mz1{
+ "America_Central",
+ "1999-10-31 06:00",
+ "2000-10-29 07:00",
+ }
+ mz2{
+ "America_Eastern",
+ "2000-10-29 07:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Jamaica"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Jujuy"{
+ mz0{
+ "Argentina",
+ "1970-01-01 00:00",
+ "1990-03-04 02:00",
+ }
+ mz1{
+ "Argentina_Western",
+ "1990-03-04 02:00",
+ "1991-10-06 04:00",
+ }
+ mz2{
+ "Argentina",
+ "1991-10-06 04:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Juneau"{
+ mz0{
+ "America_Pacific",
+ "1970-01-01 00:00",
+ "1983-10-30 09:00",
+ }
+ mz1{
+ "Alaska",
+ "1983-10-30 09:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Kentucky:Monticello"{
+ mz0{
+ "America_Central",
+ "1970-01-01 00:00",
+ "2000-10-29 07:00",
+ }
+ mz1{
+ "America_Eastern",
+ "2000-10-29 07:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:La_Paz"{
+ mz0{
+ "Bolivia",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Lima"{
+ mz0{
+ "Peru",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Los_Angeles"{
+ mz0{
+ "America_Pacific",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Louisville"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "1974-01-06 07:00",
+ }
+ mz1{
+ "America_Central",
+ "1974-01-06 07:00",
+ "1974-10-27 07:00",
+ }
+ mz2{
+ "America_Eastern",
+ "1974-10-27 07:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Maceio"{
+ mz0{
+ "Brasilia",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Managua"{
+ mz0{
+ "America_Central",
+ "1970-01-01 00:00",
+ "1973-05-01 06:00",
+ }
+ mz1{
+ "America_Eastern",
+ "1973-05-01 06:00",
+ "1975-02-16 05:00",
+ }
+ mz2{
+ "America_Central",
+ "1975-02-16 05:00",
+ "1992-01-01 10:00",
+ }
+ mz3{
+ "America_Eastern",
+ "1992-01-01 10:00",
+ "1992-09-24 05:00",
+ }
+ mz4{
+ "America_Central",
+ "1992-09-24 05:00",
+ "1993-01-01 06:00",
+ }
+ mz5{
+ "America_Eastern",
+ "1993-01-01 06:00",
+ "1997-01-01 05:00",
+ }
+ mz6{
+ "America_Central",
+ "1997-01-01 05:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Manaus"{
+ mz0{
+ "Amazon",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Martinique"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Mazatlan"{
+ mz0{
+ "America_Pacific",
+ "1970-01-01 00:00",
+ "1970-01-01 08:00",
+ }
+ mz1{
+ "America_Mountain",
+ "1970-01-01 08:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Mendoza"{
+ mz0{
+ "Argentina",
+ "1970-01-01 00:00",
+ "1990-03-04 02:00",
+ }
+ mz1{
+ "Argentina_Western",
+ "1990-03-04 02:00",
+ "1992-10-18 04:00",
+ }
+ mz2{
+ "Argentina",
+ "1992-10-18 04:00",
+ "2004-05-23 03:00",
+ }
+ mz3{
+ "Argentina_Western",
+ "2004-05-23 03:00",
+ "2004-09-26 04:00",
+ }
+ mz4{
+ "Argentina",
+ "2004-09-26 04:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Menominee"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "1973-04-29 07:00",
+ }
+ mz1{
+ "America_Central",
+ "1973-04-29 07:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Merida"{
+ mz0{
+ "America_Central",
+ "1970-01-01 00:00",
+ "1981-12-23 06:00",
+ }
+ mz1{
+ "America_Eastern",
+ "1981-12-23 06:00",
+ "1982-12-02 05:00",
+ }
+ mz2{
+ "America_Central",
+ "1982-12-02 05:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Mexico_City"{
+ mz0{
+ "America_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Miquelon"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "1980-05-01 04:00",
+ }
+ mz1{
+ "Pierre_Miquelon",
+ "1980-05-01 04:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Moncton"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Monterrey"{
+ mz0{
+ "America_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Montevideo"{
+ mz0{
+ "Uruguay",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Montreal"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Montserrat"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Nassau"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:New_York"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Nipigon"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Nome"{
+ mz0{
+ "Bering",
+ "1970-01-01 00:00",
+ "1983-10-30 12:00",
+ }
+ mz1{
+ "Alaska",
+ "1983-10-30 12:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Noronha"{
+ mz0{
+ "Noronha",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:North_Dakota:Center"{
+ mz0{
+ "America_Mountain",
+ "1970-01-01 00:00",
+ "1992-10-25 08:00",
+ }
+ mz1{
+ "America_Central",
+ "1992-10-25 08:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:North_Dakota:New_Salem"{
+ mz0{
+ "America_Mountain",
+ "1970-01-01 00:00",
+ "2003-10-26 08:00",
+ }
+ mz1{
+ "America_Central",
+ "2003-10-26 08:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Panama"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Pangnirtung"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "1995-04-02 06:00",
+ }
+ mz1{
+ "America_Eastern",
+ "1995-04-02 06:00",
+ "1999-10-31 06:00",
+ }
+ mz2{
+ "America_Central",
+ "1999-10-31 06:00",
+ "2000-10-29 07:00",
+ }
+ mz3{
+ "America_Eastern",
+ "2000-10-29 07:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Paramaribo"{
+ mz0{
+ "Dutch_Guiana",
+ "1970-01-01 00:00",
+ "1975-11-20 03:30",
+ }
+ mz1{
+ "Suriname",
+ "1975-11-20 03:30",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Phoenix"{
+ mz0{
+ "America_Mountain",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Port-au-Prince"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Port_of_Spain"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Porto_Velho"{
+ mz0{
+ "Amazon",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Puerto_Rico"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Rainy_River"{
+ mz0{
+ "America_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Rankin_Inlet"{
+ mz0{
+ "America_Central",
+ "1970-01-01 00:00",
+ "2000-10-29 07:00",
+ }
+ mz1{
+ "America_Eastern",
+ "2000-10-29 07:00",
+ "2001-04-01 08:00",
+ }
+ mz2{
+ "America_Central",
+ "2001-04-01 08:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Recife"{
+ mz0{
+ "Brasilia",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Regina"{
+ mz0{
+ "America_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Resolute"{
+ mz0{
+ "America_Central",
+ "1970-01-01 00:00",
+ "2000-10-29 07:00",
+ }
+ mz1{
+ "America_Eastern",
+ "2000-10-29 07:00",
+ "2001-04-01 08:00",
+ }
+ mz2{
+ "America_Central",
+ "2001-04-01 08:00",
+ "2006-10-29 07:00",
+ }
+ mz3{
+ "America_Eastern",
+ "2006-10-29 07:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Rio_Branco"{
+ mz0{
+ "Acre",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Santiago"{
+ mz0{
+ "Chile",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Santo_Domingo"{
+ mz0{
+ "Dominican",
+ "1970-01-01 00:00",
+ "1974-10-27 05:00",
+ }
+ mz1{
+ "Atlantic",
+ "1974-10-27 05:00",
+ "2000-10-29 06:00",
+ }
+ mz2{
+ "America_Eastern",
+ "2000-10-29 06:00",
+ "2000-12-03 06:00",
+ }
+ mz3{
+ "Atlantic",
+ "2000-12-03 06:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Sao_Paulo"{
+ mz0{
+ "Brasilia",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Scoresbysund"{
+ mz0{
+ "Greenland_Central",
+ "1970-01-01 00:00",
+ "1981-03-29 02:00",
+ }
+ mz1{
+ "Greenland_Eastern",
+ "1981-03-29 02:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Shiprock"{
+ mz0{
+ "America_Mountain",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:St_Johns"{
+ mz0{
+ "Newfoundland",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:St_Kitts"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:St_Lucia"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:St_Thomas"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:St_Vincent"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Swift_Current"{
+ mz0{
+ "America_Mountain",
+ "1970-01-01 00:00",
+ "1972-04-30 09:00",
+ }
+ mz1{
+ "America_Central",
+ "1972-04-30 09:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Tegucigalpa"{
+ mz0{
+ "America_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Thule"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Thunder_Bay"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Tijuana"{
+ mz0{
+ "America_Pacific",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Toronto"{
+ mz0{
+ "America_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Tortola"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Vancouver"{
+ mz0{
+ "America_Pacific",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Whitehorse"{
+ mz0{
+ "America_Pacific",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Winnipeg"{
+ mz0{
+ "America_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Yakutat"{
+ mz0{
+ "Yukon",
+ "1970-01-01 00:00",
+ "1983-10-30 10:00",
+ }
+ mz1{
+ "Alaska",
+ "1983-10-30 10:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "America:Yellowknife"{
+ mz0{
+ "America_Mountain",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Antarctica:Casey"{
+ mz0{
+ "Australia_Western",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Antarctica:Davis"{
+ mz0{
+ "Davis",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Antarctica:DumontDUrville"{
+ mz0{
+ "DumontDUrville",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Antarctica:Mawson"{
+ mz0{
+ "Mawson",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Antarctica:McMurdo"{
+ mz0{
+ "New_Zealand",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Antarctica:Palmer"{
+ mz0{
+ "Argentina",
+ "1970-01-01 00:00",
+ "1982-05-01 03:00",
+ }
+ mz1{
+ "Chile",
+ "1982-05-01 03:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Antarctica:Rothera"{
+ mz0{
+ "Rothera",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Antarctica:Syowa"{
+ mz0{
+ "Syowa",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Antarctica:Vostok"{
+ mz0{
+ "Vostok",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Arctic:Longyearbyen"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Aden"{
+ mz0{
+ "Arabian",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Almaty"{
+ mz0{
+ "Almaty",
+ "1970-01-01 00:00",
+ "2005-03-14 18:00",
+ }
+ mz1{
+ "Kazakhstan_Eastern",
+ "2005-03-14 18:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Amman"{
+ mz0{
+ "Europe_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Anadyr"{
+ mz0{
+ "Anadyr",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Aqtau"{
+ mz0{
+ "Shevchenko",
+ "1970-01-01 00:00",
+ "1991-12-15 19:00",
+ }
+ mz1{
+ "Aqtau",
+ "1991-12-15 19:00",
+ "2005-03-14 20:00",
+ }
+ mz2{
+ "Kazakhstan_Western",
+ "2005-03-14 20:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Aqtobe"{
+ mz0{
+ "Aktyubinsk",
+ "1970-01-01 00:00",
+ "1991-12-15 19:00",
+ }
+ mz1{
+ "Aqtobe",
+ "1991-12-15 19:00",
+ "2005-03-14 19:00",
+ }
+ mz2{
+ "Kazakhstan_Western",
+ "2005-03-14 19:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Ashgabat"{
+ mz0{
+ "Ashkhabad",
+ "1970-01-01 00:00",
+ "1991-10-26 20:00",
+ }
+ mz1{
+ "Turkmenistan",
+ "1991-10-26 20:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Baghdad"{
+ mz0{
+ "Arabian",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Bahrain"{
+ mz0{
+ "Gulf",
+ "1970-01-01 00:00",
+ "1972-05-31 20:00",
+ }
+ mz1{
+ "Arabian",
+ "1972-05-31 20:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Baku"{
+ mz0{
+ "Baku",
+ "1970-01-01 00:00",
+ "1991-08-29 20:00",
+ }
+ mz1{
+ "Azerbaijan",
+ "1991-08-29 20:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Bangkok"{
+ mz0{
+ "Indochina",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Beirut"{
+ mz0{
+ "Europe_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Bishkek"{
+ mz0{
+ "Frunze",
+ "1970-01-01 00:00",
+ "1991-08-30 20:00",
+ }
+ mz1{
+ "Kyrgystan",
+ "1991-08-30 20:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Brunei"{
+ mz0{
+ "Brunei",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Calcutta"{
+ mz0{
+ "India",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Choibalsan"{
+ mz0{
+ "Mongolia",
+ "1970-01-01 00:00",
+ "1983-03-31 16:00",
+ }
+ mz1{
+ "Choibalsan",
+ "1983-03-31 16:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Chongqing"{
+ mz0{
+ "Long_Shu",
+ "1970-01-01 00:00",
+ "1980-04-30 17:00",
+ }
+ mz1{
+ "China",
+ "1980-04-30 17:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Colombo"{
+ mz0{
+ "India",
+ "1970-01-01 00:00",
+ "1996-05-24 18:30",
+ }
+ mz1{
+ "Lanka",
+ "1996-05-24 18:30",
+ "2006-04-14 18:30",
+ }
+ mz2{
+ "India",
+ "2006-04-14 18:30",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Damascus"{
+ mz0{
+ "Europe_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Dhaka"{
+ mz0{
+ "Dacca",
+ "1970-01-01 00:00",
+ "1971-03-25 18:00",
+ }
+ mz1{
+ "Bangladesh",
+ "1971-03-25 18:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Dili"{
+ mz0{
+ "East_Timor",
+ "1970-01-01 00:00",
+ "1976-05-02 15:00",
+ }
+ mz1{
+ "Indonesia_Central",
+ "1976-05-02 15:00",
+ "2000-09-16 16:00",
+ }
+ mz2{
+ "East_Timor",
+ "2000-09-16 16:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Dubai"{
+ mz0{
+ "Gulf",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Dushanbe"{
+ mz0{
+ "Dushanbe",
+ "1970-01-01 00:00",
+ "1991-09-08 21:00",
+ }
+ mz1{
+ "Tajikistan",
+ "1991-09-08 21:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Gaza"{
+ mz0{
+ "Israel",
+ "1970-01-01 00:00",
+ "1995-12-31 22:00",
+ }
+ mz1{
+ "Europe_Eastern",
+ "1995-12-31 22:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Harbin"{
+ mz0{
+ "Changbai",
+ "1970-01-01 00:00",
+ "1980-04-30 15:30",
+ }
+ mz1{
+ "China",
+ "1980-04-30 15:30",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Hong_Kong"{
+ mz0{
+ "Hong_Kong",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Hovd"{
+ mz0{
+ "Hovd",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Irkutsk"{
+ mz0{
+ "Irkutsk",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Jakarta"{
+ mz0{
+ "Indonesia_Western",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Jayapura"{
+ mz0{
+ "Indonesia_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Jerusalem"{
+ mz0{
+ "Israel",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Kabul"{
+ mz0{
+ "Afghanistan",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Kamchatka"{
+ mz0{
+ "Kamchatka",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Karachi"{
+ mz0{
+ "Karachi",
+ "1970-01-01 00:00",
+ "1971-03-25 19:00",
+ }
+ mz1{
+ "Pakistan",
+ "1971-03-25 19:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Kashgar"{
+ mz0{
+ "Kashgar",
+ "1970-01-01 00:00",
+ "1980-04-30 19:00",
+ }
+ mz1{
+ "China",
+ "1980-04-30 19:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Katmandu"{
+ mz0{
+ "Nepal",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Krasnoyarsk"{
+ mz0{
+ "Krasnoyarsk",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Kuala_Lumpur"{
+ mz0{
+ "Malaya",
+ "1970-01-01 00:00",
+ "1981-12-31 16:30",
+ }
+ mz1{
+ "Malaysia",
+ "1981-12-31 16:30",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Kuching"{
+ mz0{
+ "Borneo",
+ "1970-01-01 00:00",
+ "1981-12-31 16:00",
+ }
+ mz1{
+ "Malaysia",
+ "1981-12-31 16:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Kuwait"{
+ mz0{
+ "Arabian",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Macau"{
+ mz0{
+ "Macau",
+ "1970-01-01 00:00",
+ "1999-12-19 16:00",
+ }
+ mz1{
+ "China",
+ "1999-12-19 16:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Magadan"{
+ mz0{
+ "Magadan",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Makassar"{
+ mz0{
+ "Indonesia_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Manila"{
+ mz0{
+ "Philippines",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Muscat"{
+ mz0{
+ "Gulf",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Nicosia"{
+ mz0{
+ "Europe_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Novosibirsk"{
+ mz0{
+ "Novosibirsk",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Omsk"{
+ mz0{
+ "Omsk",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Oral"{
+ mz0{
+ "Uralsk",
+ "1970-01-01 00:00",
+ "1991-12-15 20:00",
+ }
+ mz1{
+ "Oral",
+ "1991-12-15 20:00",
+ "2005-03-14 20:00",
+ }
+ mz2{
+ "Kazakhstan_Western",
+ "2005-03-14 20:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Phnom_Penh"{
+ mz0{
+ "Indochina",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Pontianak"{
+ mz0{
+ "Indonesia_Central",
+ "1970-01-01 00:00",
+ "1987-12-31 16:00",
+ }
+ mz1{
+ "Indonesia_Western",
+ "1987-12-31 16:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Pyongyang"{
+ mz0{
+ "Korea",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Qatar"{
+ mz0{
+ "Gulf",
+ "1970-01-01 00:00",
+ "1972-05-31 20:00",
+ }
+ mz1{
+ "Arabian",
+ "1972-05-31 20:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Qyzylorda"{
+ mz0{
+ "Kizilorda",
+ "1970-01-01 00:00",
+ "1991-12-15 19:00",
+ }
+ mz1{
+ "Qyzylorda",
+ "1991-12-15 19:00",
+ "2005-03-14 18:00",
+ }
+ mz2{
+ "Kazakhstan_Eastern",
+ "2005-03-14 18:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Rangoon"{
+ mz0{
+ "Myanmar",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Riyadh"{
+ mz0{
+ "Arabian",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Saigon"{
+ mz0{
+ "Indochina",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Sakhalin"{
+ mz0{
+ "Sakhalin",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Samarkand"{
+ mz0{
+ "Samarkand",
+ "1970-01-01 00:00",
+ "1981-09-30 18:00",
+ }
+ mz1{
+ "Tashkent",
+ "1981-09-30 18:00",
+ "1982-03-31 18:00",
+ }
+ mz2{
+ "Samarkand",
+ "1982-03-31 18:00",
+ "1991-08-31 18:00",
+ }
+ mz3{
+ "Uzbekistan",
+ "1991-08-31 18:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Seoul"{
+ mz0{
+ "Korea",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Shanghai"{
+ mz0{
+ "China",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Singapore"{
+ mz0{
+ "Singapore",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Taipei"{
+ mz0{
+ "China",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Tashkent"{
+ mz0{
+ "Tashkent",
+ "1970-01-01 00:00",
+ "1991-08-31 18:00",
+ }
+ mz1{
+ "Uzbekistan",
+ "1991-08-31 18:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Tbilisi"{
+ mz0{
+ "Tbilisi",
+ "1970-01-01 00:00",
+ "1991-04-08 20:00",
+ }
+ mz1{
+ "Georgia",
+ "1991-04-08 20:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Tehran"{
+ mz0{
+ "Iran",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Thimphu"{
+ mz0{
+ "India",
+ "1970-01-01 00:00",
+ "1987-09-30 18:30",
+ }
+ mz1{
+ "Bhutan",
+ "1987-09-30 18:30",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Tokyo"{
+ mz0{
+ "Japan",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Ulaanbaatar"{
+ mz0{
+ "Mongolia",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Urumqi"{
+ mz0{
+ "Urumqi",
+ "1970-01-01 00:00",
+ "1980-04-30 18:00",
+ }
+ mz1{
+ "China",
+ "1980-04-30 18:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Vientiane"{
+ mz0{
+ "Indochina",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Vladivostok"{
+ mz0{
+ "Vladivostok",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Yakutsk"{
+ mz0{
+ "Yakutsk",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Yekaterinburg"{
+ mz0{
+ "Sverdlovsk",
+ "1970-01-01 00:00",
+ "1992-01-18 22:00",
+ }
+ mz1{
+ "Yekaterinburg",
+ "1992-01-18 22:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Asia:Yerevan"{
+ mz0{
+ "Yerevan",
+ "1970-01-01 00:00",
+ "1991-09-22 20:00",
+ }
+ mz1{
+ "Armenia",
+ "1991-09-22 20:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Atlantic:Azores"{
+ mz0{
+ "Azores",
+ "1970-01-01 00:00",
+ "1992-09-27 02:00",
+ }
+ mz1{
+ "Europe_Western",
+ "1992-09-27 02:00",
+ "1993-03-28 01:00",
+ }
+ mz2{
+ "Azores",
+ "1993-03-28 01:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Atlantic:Bermuda"{
+ mz0{
+ "Atlantic",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Atlantic:Canary"{
+ mz0{
+ "Europe_Western",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Atlantic:Cape_Verde"{
+ mz0{
+ "Cape_Verde",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Atlantic:Faeroe"{
+ mz0{
+ "Europe_Western",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Atlantic:Jan_Mayen"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Atlantic:Madeira"{
+ mz0{
+ "Europe_Western",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Atlantic:Reykjavik"{
+ mz0{
+ "GMT",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Atlantic:South_Georgia"{
+ mz0{
+ "South_Georgia",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Atlantic:St_Helena"{
+ mz0{
+ "GMT",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Atlantic:Stanley"{
+ mz0{
+ "Falkland",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Australia:Adelaide"{
+ mz0{
+ "Australia_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Australia:Brisbane"{
+ mz0{
+ "Australia_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Australia:Broken_Hill"{
+ mz0{
+ "Australia_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Australia:Currie"{
+ mz0{
+ "Australia_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Australia:Darwin"{
+ mz0{
+ "Australia_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Australia:Eucla"{
+ mz0{
+ "Australia_CentralWestern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Australia:Hobart"{
+ mz0{
+ "Australia_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Australia:Lindeman"{
+ mz0{
+ "Australia_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Australia:Lord_Howe"{
+ mz0{
+ "Australia_Eastern",
+ "1970-01-01 00:00",
+ "1981-02-28 14:00",
+ }
+ mz1{
+ "Lord_Howe",
+ "1981-02-28 14:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Australia:Melbourne"{
+ mz0{
+ "Australia_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Australia:Perth"{
+ mz0{
+ "Australia_Western",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Australia:Sydney"{
+ mz0{
+ "Australia_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Amsterdam"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Andorra"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Athens"{
+ mz0{
+ "Europe_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Belgrade"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Berlin"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Bratislava"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Brussels"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Bucharest"{
+ mz0{
+ "Europe_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Budapest"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Chisinau"{
+ mz0{
+ "Moscow",
+ "1970-01-01 00:00",
+ "1990-05-05 21:00",
+ }
+ mz1{
+ "Europe_Eastern",
+ "1990-05-05 21:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Copenhagen"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Dublin"{
+ mz0{
+ "Irish",
+ "1970-01-01 00:00",
+ "1971-10-31 02:00",
+ }
+ mz1{
+ "GMT",
+ "1971-10-31 02:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Gibraltar"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Guernsey"{
+ mz0{
+ "British",
+ "1970-01-01 00:00",
+ "1971-10-31 02:00",
+ }
+ mz1{
+ "GMT",
+ "1971-10-31 02:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Helsinki"{
+ mz0{
+ "Europe_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Isle_of_Man"{
+ mz0{
+ "British",
+ "1970-01-01 00:00",
+ "1971-10-31 02:00",
+ }
+ mz1{
+ "GMT",
+ "1971-10-31 02:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Istanbul"{
+ mz0{
+ "Europe_Eastern",
+ "1970-01-01 00:00",
+ "1978-10-14 21:00",
+ }
+ mz1{
+ "Turkey",
+ "1978-10-14 21:00",
+ "1985-04-19 21:00",
+ }
+ mz2{
+ "Europe_Eastern",
+ "1985-04-19 21:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Jersey"{
+ mz0{
+ "British",
+ "1970-01-01 00:00",
+ "1971-10-31 02:00",
+ }
+ mz1{
+ "GMT",
+ "1971-10-31 02:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Kaliningrad"{
+ mz0{
+ "Moscow",
+ "1970-01-01 00:00",
+ "1991-03-30 23:00",
+ }
+ mz1{
+ "Europe_Eastern",
+ "1991-03-30 23:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Kiev"{
+ mz0{
+ "Moscow",
+ "1970-01-01 00:00",
+ "1990-06-30 23:00",
+ }
+ mz1{
+ "Europe_Eastern",
+ "1990-06-30 23:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Lisbon"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "1976-09-26 00:00",
+ }
+ mz1{
+ "Europe_Western",
+ "1976-09-26 00:00",
+ "1992-09-27 01:00",
+ }
+ mz2{
+ "Europe_Central",
+ "1992-09-27 01:00",
+ "1996-03-31 01:00",
+ }
+ mz3{
+ "Europe_Western",
+ "1996-03-31 01:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Ljubljana"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:London"{
+ mz0{
+ "British",
+ "1970-01-01 00:00",
+ "1971-10-31 02:00",
+ }
+ mz1{
+ "GMT",
+ "1971-10-31 02:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Luxembourg"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Madrid"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Malta"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Mariehamn"{
+ mz0{
+ "Europe_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Minsk"{
+ mz0{
+ "Moscow",
+ "1970-01-01 00:00",
+ "1991-03-30 23:00",
+ }
+ mz1{
+ "Europe_Eastern",
+ "1991-03-30 23:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Monaco"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Moscow"{
+ mz0{
+ "Moscow",
+ "1970-01-01 00:00",
+ "1991-03-30 23:00",
+ }
+ mz1{
+ "Europe_Eastern",
+ "1991-03-30 23:00",
+ "1992-01-19 00:00",
+ }
+ mz2{
+ "Moscow",
+ "1992-01-19 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Oslo"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Paris"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Podgorica"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Prague"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Riga"{
+ mz0{
+ "Moscow",
+ "1970-01-01 00:00",
+ "1989-03-25 23:00",
+ }
+ mz1{
+ "Europe_Eastern",
+ "1989-03-25 23:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Rome"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Samara"{
+ mz0{
+ "Kuybyshev",
+ "1970-01-01 00:00",
+ "1991-10-20 00:00",
+ }
+ mz1{
+ "Samara",
+ "1991-10-20 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:San_Marino"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Sarajevo"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Simferopol"{
+ mz0{
+ "Moscow",
+ "1970-01-01 00:00",
+ "1990-06-30 23:00",
+ }
+ mz1{
+ "Europe_Eastern",
+ "1990-06-30 23:00",
+ "1994-04-30 21:00",
+ }
+ mz2{
+ "Moscow",
+ "1994-04-30 21:00",
+ "1997-03-30 01:00",
+ }
+ mz3{
+ "Europe_Eastern",
+ "1997-03-30 01:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Skopje"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Sofia"{
+ mz0{
+ "Europe_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Stockholm"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Tallinn"{
+ mz0{
+ "Moscow",
+ "1970-01-01 00:00",
+ "1989-03-25 23:00",
+ }
+ mz1{
+ "Europe_Eastern",
+ "1989-03-25 23:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Tirane"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Uzhgorod"{
+ mz0{
+ "Moscow",
+ "1970-01-01 00:00",
+ "1990-06-30 23:00",
+ }
+ mz1{
+ "Europe_Central",
+ "1990-06-30 23:00",
+ "1991-03-31 02:00",
+ }
+ mz2{
+ "Europe_Eastern",
+ "1991-03-31 02:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Vaduz"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Vatican"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Vienna"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Vilnius"{
+ mz0{
+ "Moscow",
+ "1970-01-01 00:00",
+ "1991-03-30 23:00",
+ }
+ mz1{
+ "Europe_Eastern",
+ "1991-03-30 23:00",
+ "1998-03-29 01:00",
+ }
+ mz2{
+ "Europe_Central",
+ "1998-03-29 01:00",
+ "1999-10-31 01:00",
+ }
+ mz3{
+ "Europe_Eastern",
+ "1999-10-31 01:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Volgograd"{
+ mz0{
+ "Volgograd",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Warsaw"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Zagreb"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Zaporozhye"{
+ mz0{
+ "Moscow",
+ "1970-01-01 00:00",
+ "1991-03-30 23:00",
+ }
+ mz1{
+ "Europe_Eastern",
+ "1991-03-30 23:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Europe:Zurich"{
+ mz0{
+ "Europe_Central",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Indian:Antananarivo"{
+ mz0{
+ "Africa_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Indian:Chagos"{
+ mz0{
+ "Indian_Ocean",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Indian:Christmas"{
+ mz0{
+ "Christmas",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Indian:Cocos"{
+ mz0{
+ "Cocos",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Indian:Comoro"{
+ mz0{
+ "Africa_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Indian:Kerguelen"{
+ mz0{
+ "French_Southern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Indian:Mahe"{
+ mz0{
+ "Seychelles",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Indian:Maldives"{
+ mz0{
+ "Maldives",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Indian:Mauritius"{
+ mz0{
+ "Mauritius",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Indian:Mayotte"{
+ mz0{
+ "Africa_Eastern",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Indian:Reunion"{
+ mz0{
+ "Reunion",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Apia"{
+ mz0{
+ "Bering",
+ "1970-01-01 00:00",
+ "1983-10-30 12:00",
+ }
+ mz1{
+ "Samoa",
+ "1983-10-30 12:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Auckland"{
+ mz0{
+ "New_Zealand",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Chatham"{
+ mz0{
+ "Chatham",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Easter"{
+ mz0{
+ "Easter",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Efate"{
+ mz0{
+ "Vanuatu",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Enderbury"{
+ mz0{
+ "Phoenix_Islands",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Fakaofo"{
+ mz0{
+ "Tokelau",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Fiji"{
+ mz0{
+ "Fiji",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Funafuti"{
+ mz0{
+ "Tuvalu",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Galapagos"{
+ mz0{
+ "Ecuador",
+ "1970-01-01 00:00",
+ "1986-01-01 05:00",
+ }
+ mz1{
+ "Galapagos",
+ "1986-01-01 05:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Gambier"{
+ mz0{
+ "Gambier",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Guadalcanal"{
+ mz0{
+ "Solomon",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Guam"{
+ mz0{
+ "Guam",
+ "1970-01-01 00:00",
+ "2000-12-22 14:00",
+ }
+ mz1{
+ "Chamorro",
+ "2000-12-22 14:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Honolulu"{
+ mz0{
+ "Alaska_Hawaii",
+ "1970-01-01 00:00",
+ "1983-10-30 11:00",
+ }
+ mz1{
+ "Hawaii_Aleutian",
+ "1983-10-30 11:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Johnston"{
+ mz0{
+ "Alaska_Hawaii",
+ "1970-01-01 00:00",
+ "1983-10-30 11:00",
+ }
+ mz1{
+ "Hawaii_Aleutian",
+ "1983-10-30 11:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Kiritimati"{
+ mz0{
+ "Line_Islands",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Kosrae"{
+ mz0{
+ "Kosrae",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Kwajalein"{
+ mz0{
+ "Kwajalein",
+ "1970-01-01 00:00",
+ "1993-08-20 12:00",
+ }
+ mz1{
+ "Marshall_Islands",
+ "1993-08-20 12:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Majuro"{
+ mz0{
+ "Marshall_Islands",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Marquesas"{
+ mz0{
+ "Marquesas",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Midway"{
+ mz0{
+ "Bering",
+ "1970-01-01 00:00",
+ "1983-10-30 12:00",
+ }
+ mz1{
+ "Samoa",
+ "1983-10-30 12:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Nauru"{
+ mz0{
+ "Nauru",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Niue"{
+ mz0{
+ "Niue",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Norfolk"{
+ mz0{
+ "Norfolk",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Noumea"{
+ mz0{
+ "New_Caledonia",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Pago_Pago"{
+ mz0{
+ "Bering",
+ "1970-01-01 00:00",
+ "1983-10-30 12:00",
+ }
+ mz1{
+ "Samoa",
+ "1983-10-30 12:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Palau"{
+ mz0{
+ "Palau",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Pitcairn"{
+ mz0{
+ "Pitcairn",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Ponape"{
+ mz0{
+ "Ponape",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Port_Moresby"{
+ mz0{
+ "Papua_New_Guinea",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Rarotonga"{
+ mz0{
+ "Cook",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Saipan"{
+ mz0{
+ "North_Mariana",
+ "1970-01-01 00:00",
+ "2000-12-22 14:00",
+ }
+ mz1{
+ "Chamorro",
+ "2000-12-22 14:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Tahiti"{
+ mz0{
+ "Tahiti",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Tarawa"{
+ mz0{
+ "Gilbert_Islands",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Tongatapu"{
+ mz0{
+ "Tonga",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Truk"{
+ mz0{
+ "Truk",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Wake"{
+ mz0{
+ "Wake",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ "Pacific:Wallis"{
+ mz0{
+ "Wallis",
+ "1970-01-01 00:00",
+ "9999-12-31 23:59",
+ }
+ }
+ }
+}
diff --git a/icu4c/source/data/misc/miscfiles.mk b/icu4c/source/data/misc/miscfiles.mk
index e2ff181..e7b56ab 100644
--- a/icu4c/source/data/misc/miscfiles.mk
+++ b/icu4c/source/data/misc/miscfiles.mk
@@ -23,4 +23,4 @@
#
MISC_SOURCE = \
-zoneinfo.txt supplementalData.txt
+zoneinfo.txt supplementalData.txt metazoneInfo.txt
diff --git a/icu4c/source/data/misc/supplementalData.txt b/icu4c/source/data/misc/supplementalData.txt
index f1ec11a..bb47de2 100644
--- a/icu4c/source/data/misc/supplementalData.txt
+++ b/icu4c/source/data/misc/supplementalData.txt
@@ -516,6 +516,26 @@
}
}
}
+ BL{
+ {
+ id{"EUR"}
+ from:intvector{ /** 1999-01-01 */
+ 213,
+ 320825952,
+ }
+ }
+ {
+ id{"FRF"}
+ from:intvector{ /** 1960-01-01 */
+ -74,
+ -2086527392,
+ }
+ to:intvector{ /** 2002-02-17 */
+ 235,
+ 1908405440,
+ }
+ }
+ }
BM{
{
id{"BMD"}
@@ -654,6 +674,19 @@
}
}
}
+ BU{
+ {
+ id{"BUK"}
+ from:intvector{ /** 1952-07-01 */
+ -133,
+ -1143896928,
+ }
+ to:intvector{ /** 1989-06-18 */
+ 139,
+ -210261440,
+ }
+ }
+ }
BV{
{
id{"NOK"}
@@ -875,10 +908,10 @@
}
CS{
{
- id{"EUR"}
- from:intvector{ /** 2003-02-04 */
- 242,
- -2038532928,
+ id{"CSD"}
+ from:intvector{ /** 2002-05-15 */
+ 235,
+ 1735785440,
}
to:intvector{ /** 2006-06-03 */
264,
@@ -886,10 +919,10 @@
}
}
{
- id{"CSD"}
- from:intvector{ /** 2002-05-15 */
- 235,
- 1735785440,
+ id{"EUR"}
+ from:intvector{ /** 2003-02-04 */
+ 242,
+ -2038532928,
}
to:intvector{ /** 2006-06-03 */
264,
@@ -997,6 +1030,19 @@
}
}
}
+ DD{
+ {
+ id{"DDM"}
+ from:intvector{ /** 1948-07-20 */
+ -162,
+ -1178645344,
+ }
+ to:intvector{ /** 1990-10-02 */
+ 146,
+ -121192512,
+ }
+ }
+ }
DE{
{
id{"EUR"}
@@ -1424,9 +1470,9 @@
GP{
{
id{"EUR"}
- from:intvector{ /** 1999 */
+ from:intvector{ /** 1999-01-01 */
213,
- 320765952,
+ 320825952,
}
}
{
@@ -2210,6 +2256,26 @@
}
}
}
+ MF{
+ {
+ id{"EUR"}
+ from:intvector{ /** 1999-01-01 */
+ 213,
+ 320825952,
+ }
+ }
+ {
+ id{"FRF"}
+ from:intvector{ /** 1960-01-01 */
+ -74,
+ -2086527392,
+ }
+ to:intvector{ /** 2002-02-17 */
+ 235,
+ 1908405440,
+ }
+ }
+ }
MG{
{
id{"MGA"}
@@ -2920,6 +2986,15 @@
}
}
}
+ QU{
+ {
+ id{"EUR"}
+ from:intvector{ /** 1999-01-01 */
+ 213,
+ 320825952,
+ }
+ }
+ }
RE{
{
id{"EUR"}
@@ -3296,6 +3371,19 @@
}
}
}
+ SU{
+ {
+ id{"SUR"}
+ from:intvector{ /** 1961-01-01 */
+ -67,
+ -528898464,
+ }
+ to:intvector{ /** 1991-12-25 */
+ 154,
+ -957610880,
+ }
+ }
+ }
SV{
{
id{"SVC"}
@@ -3495,6 +3583,30 @@
}
}
}
+ TP{
+ {
+ id{"TPE"}
+ from:intvector{ /** 1959-01-02 */
+ -81,
+ 823610976,
+ }
+ to:intvector{ /** 2002-05-20 */
+ 235,
+ -2127181856,
+ }
+ }
+ {
+ id{"IDR"}
+ from:intvector{ /** 1975-12-07 */
+ 36,
+ -628269952,
+ }
+ to:intvector{ /** 2002-05-20 */
+ 235,
+ -2127181856,
+ }
+ }
+ }
TR{
{
id{"TRY"}
@@ -3814,6 +3926,41 @@
}
}
}
+ YU{
+ {
+ id{"YUM"}
+ from:intvector{ /** 1994-01-24 */
+ 176,
+ -839551392,
+ }
+ to:intvector{ /** 2002-05-15 */
+ 235,
+ 1735785440,
+ }
+ }
+ {
+ id{"YUN"}
+ from:intvector{ /** 1990-01-01 */
+ 146,
+ -208132512,
+ }
+ to:intvector{ /** 1992-07-24 */
+ 162,
+ 426918048,
+ }
+ }
+ {
+ id{"YUD"}
+ from:intvector{ /** 1966-01-01 */
+ -30,
+ -1676288416,
+ }
+ to:intvector{ /** 1990-01-01 */
+ 146,
+ -208132512,
+ }
+ }
+ }
ZA{
{
id{"ZAR"}
@@ -3832,6 +3979,30 @@
}
}
}
+ ZR{
+ {
+ id{"ZRN"}
+ from:intvector{ /** 1993-11-01 */
+ 168,
+ -2413024,
+ }
+ to:intvector{ /** 1998-07 */
+ 205,
+ -1150042976,
+ }
+ }
+ {
+ id{"ZRZ"}
+ from:intvector{ /** 1971-10-27 */
+ 7,
+ -576738368,
+ }
+ to:intvector{ /** 1993-11-01 */
+ 168,
+ -2413024,
+ }
+ }
+ }
ZW{
{
id{"ZWD"}
@@ -4258,13 +4429,6 @@
}
}
}
- auv{
- secondary{
- territories{
- "FR",
- }
- }
- }
av{
primary{
scripts{
@@ -5361,6 +5525,7 @@
"BF",
"BI",
"BJ",
+ "BL",
"CA",
"CD",
"CF",
@@ -5381,6 +5546,7 @@
"LU",
"MA",
"MC",
+ "MF",
"MG",
"ML",
"MQ",
@@ -6136,6 +6302,13 @@
}
}
}
+ kg{
+ secondary{
+ territories{
+ "CD",
+ }
+ }
+ }
kha{
primary{
scripts{
@@ -6312,13 +6485,6 @@
}
}
}
- kon{
- secondary{
- territories{
- "CD",
- }
- }
- }
kos{
primary{
scripts{
@@ -7307,6 +7473,11 @@
"Latn",
}
}
+ secondary{
+ territories{
+ "FR",
+ }
+ }
}
om{
primary{
@@ -9097,7 +9268,6 @@
"meta:Europe_Western_EH"{"Africa/El_Aaiun"}
"meta:Europe_Western_FO"{"Atlantic/Faeroe"}
"meta:Europe_Western_MA"{"Africa/Casablanca"}
- "meta:Europe_Western_PT"{"Europe/Lisbon"}
"meta:Falkland_001"{"Atlantic/Stanley"}
"meta:Fiji_001"{"Pacific/Fiji"}
"meta:French_Guiana_001"{"America/Cayenne"}
@@ -9257,7 +9427,7 @@
"America:Buenos_Aires"{"SA Eastern"}
"America:Caracas"{"SA Western"}
"America:Chicago"{"Central"}
- "America:Chihuahua"{"Mountain Standard Time (Mexico)"}
+ "America:Chihuahua"{"Mexico Standard Time 2"}
"America:Denver"{"Mountain"}
"America:Godthab"{"Greenland"}
"America:Guatemala"{"Central America"}
@@ -9265,7 +9435,7 @@
"America:Indianapolis"{"US Eastern"}
"America:Los_Angeles"{"Pacific"}
"America:Manaus"{"Central Brazilian"}
- "America:Mexico_City"{"Central Standard Time (Mexico)"}
+ "America:Mexico_City"{"Mexico"}
"America:Montevideo"{"Montevideo"}
"America:New_York"{"Eastern"}
"America:Noronha"{"Mid-Atlantic"}
@@ -9473,6 +9643,7 @@
"AN",
"AW",
"BB",
+ "BL",
"BS",
"CU",
"DM",
@@ -9484,6 +9655,7 @@
"KN",
"KY",
"LC",
+ "MF",
"MQ",
"MS",
"PR",
@@ -10466,9 +10638,6 @@
}
"Antarctica:McMurdo"{
territory{"AQ"}
- aliases{
- "Antarctica/South_Pole",
- }
}
"Antarctica:Palmer"{
territory{"AQ"}
@@ -10476,6 +10645,9 @@
"Antarctica:Rothera"{
territory{"AQ"}
}
+ "Antarctica:South_Pole"{
+ territory{"AQ"}
+ }
"Antarctica:Syowa"{
territory{"AQ"}
}
diff --git a/icu4c/source/i18n/Makefile.in b/icu4c/source/i18n/Makefile.in
index f8a64ea..4b39413 100644
--- a/icu4c/source/i18n/Makefile.in
+++ b/icu4c/source/i18n/Makefile.in
@@ -78,7 +78,7 @@
regexcmp.o rematch.o repattrn.o regexst.o udatpg.o uregex.o uregexc.o \
ulocdata.o measfmt.o currfmt.o curramt.o currunit.o measure.o utmscale.o \
csdetect.o csmatch.o csr2022.o csrecog.o csrmbcs.o csrsbcs.o csrucode.o csrutf8.o inputext.o \
-windtfmt.o winnmfmt.o basictz.o dtrule.o rbtz.o tzrule.o tztrans.o vtzone.o
+windtfmt.o winnmfmt.o basictz.o dtrule.o rbtz.o tzrule.o tztrans.o vtzone.o zonemeta.o zstrfmt.o
## Header files to install
HEADERS = $(srcdir)/unicode/*.h
diff --git a/icu4c/source/i18n/basictz.cpp b/icu4c/source/i18n/basictz.cpp
index 12ac310..546adb6 100644
--- a/icu4c/source/i18n/basictz.cpp
+++ b/icu4c/source/i18n/basictz.cpp
@@ -512,6 +512,15 @@
transitionRules = NULL;
}
+void
+BasicTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) /*const*/ {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ status = U_UNSUPPORTED_ERROR;
+}
+
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/calendar.cpp b/icu4c/source/i18n/calendar.cpp
index 976309a..219e8e2 100644
--- a/icu4c/source/i18n/calendar.cpp
+++ b/icu4c/source/i18n/calendar.cpp
@@ -1094,7 +1094,7 @@
double localMillis = internalGetTime();
int32_t rawOffset, dstOffset;
getTimeZone().getOffset(localMillis, FALSE, rawOffset, dstOffset, ec);
- localMillis += rawOffset;
+ localMillis += (rawOffset + dstOffset);
// Mark fields as set. Do this before calling handleComputeFields().
uint32_t mask = //fInternalSetMask;
@@ -1134,33 +1134,8 @@
//__FILE__, __LINE__, fFields[UCAL_JULIAN_DAY], localMillis);
#endif
- // In some cases we will have to call this method again below to
- // adjust for DST pushing us into the next Julian day.
computeGregorianAndDOWFields(fFields[UCAL_JULIAN_DAY], ec);
- int32_t millisInDay = (int32_t) (localMillis - (days * kOneDay));
- if (millisInDay < 0) millisInDay += (int32_t)kOneDay;
-
- // Adjust our millisInDay for DST. dstOffset will be zero if DST
- // is not in effect at this time of year, or if our zone does not
- // use DST.
- millisInDay += dstOffset;
-
- // If DST has pushed us into the next day, we must call
- // computeGregorianAndDOWFields() again. This happens in DST between
- // 12:00 am and 1:00 am every day. The first call to
- // computeGregorianAndDOWFields() will give the wrong day, since the
- // Standard time is in the previous day.
- if (millisInDay >= (int32_t)kOneDay) {
- millisInDay -= (int32_t)kOneDay; // ASSUME dstOffset < 24:00
-
- // We don't worry about overflow of JULIAN_DAY because the
- // allowable range of JULIAN_DAY has slop at the ends (that is,
- // the max is less that 0x7FFFFFFF and the min is greater than
- // -0x80000000).
- computeGregorianAndDOWFields(++fFields[UCAL_JULIAN_DAY], ec);
- }
-
// Call framework method to have subclass compute its fields.
// These must include, at a minimum, MONTH, DAY_OF_MONTH,
// EXTENDED_YEAR, YEAR, DAY_OF_YEAR. This method will call internalSet(),
@@ -1174,6 +1149,7 @@
// Compute time-related fields. These are indepent of the date and
// of the subclass algorithm. They depend only on the local zone
// wall milliseconds in day.
+ int32_t millisInDay = (int32_t) (localMillis - (days * kOneDay));
fFields[UCAL_MILLISECONDS_IN_DAY] = millisInDay;
fFields[UCAL_MILLISECOND] = millisInDay % 1000;
millisInDay /= 1000;
@@ -2348,11 +2324,11 @@
// 1. The transition into DST. Here, a designated time of 2:00 am - 2:59 am
// can be in standard or in DST depending. However, 2:00 am is an invalid
// representation (the representation jumps from 1:59:59 am Std to 3:00:00 am DST).
- // We assume standard time.
+ // We assume standard time, that is, 2:30 am is interpreted as 3:30 am DST.
// 2. The transition out of DST. Here, a designated time of 1:00 am - 1:59 am
// can be in standard or DST. Both are valid representations (the rep
// jumps from 1:59:59 DST to 1:00:00 Std).
- // Again, we assume standard time.
+ // Again, we assume standard time, that is, 1:30 am is interpreted as 1:30 am Std.
// We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
// or DST_OFFSET fields; then we use those fields.
if (fStamp[UCAL_ZONE_OFFSET] >= ((int32_t)kMinimumUserStamp) ||
diff --git a/icu4c/source/i18n/datefmt.cpp b/icu4c/source/i18n/datefmt.cpp
index daeb2e3..f49ca94 100644
--- a/icu4c/source/i18n/datefmt.cpp
+++ b/icu4c/source/i18n/datefmt.cpp
@@ -170,24 +170,32 @@
DateFormat::parse(const UnicodeString& text,
ParsePosition& pos) const
{
+ UDate d = 0; // Error return UDate is 0 (the epoch)
if (fCalendar != NULL) {
int32_t start = pos.getIndex();
+
+ // Parse may update TimeZone used by the calendar.
+ TimeZone *tzsav = (TimeZone*)fCalendar->getTimeZone().clone();
+
fCalendar->clear();
parse(text, *fCalendar, pos);
if (pos.getIndex() != start) {
UErrorCode ec = U_ZERO_ERROR;
- UDate d = fCalendar->getTime(ec);
- if (U_SUCCESS(ec)) {
- return d; // Successful function exit
+ d = fCalendar->getTime(ec);
+ if (U_FAILURE(ec)) {
+ // We arrive here if fCalendar is non-lenient and there
+ // is an out-of-range field. We don't know which field
+ // was illegal so we set the error index to the start.
+ pos.setIndex(start);
+ pos.setErrorIndex(start);
+ d = 0;
}
- // We arrive here if fCalendar is non-lenient and there
- // is an out-of-range field. We don't know which field
- // was illegal so we set the error index to the start.
- pos.setIndex(start);
- pos.setErrorIndex(start);
}
+
+ // Restore TimeZone
+ fCalendar->adoptTimeZone(tzsav);
}
- return 0; // Error return UDate is 0 (the epoch)
+ return d;
}
//----------------------------------------------------------------------
diff --git a/icu4c/source/i18n/dtfmtsym.cpp b/icu4c/source/i18n/dtfmtsym.cpp
index c32da93..8614493 100644
--- a/icu4c/source/i18n/dtfmtsym.cpp
+++ b/icu4c/source/i18n/dtfmtsym.cpp
@@ -34,7 +34,8 @@
#include "locbased.h"
#include "gregoimp.h"
#include "hash.h"
-#include "uresimp.h"
+#include "uresimp.h"
+#include "zstrfmt.h"
// *****************************************************************************
// class DateFormatSymbols
@@ -130,6 +131,17 @@
{0x0047, 0x004D, 0x0054, 0x0000} /* "GMT" */
};
+static const UChar gLastResortGmtFormat[] =
+ {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0x0000}; /* GMT{0} */
+
+static const UChar gLastResortGmtHourFormats[4][10] =
+{
+ {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}, /* -HH:mm:ss */
+ {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000, 0x0000, 0x0000, 0x0000}, /* -HH:mm */
+ {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}, /* +HH:mm:ss */
+ {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000, 0x0000, 0x0000, 0x0000} /* +HH:mm */
+};
+
/* Sizes for the last resort string arrays */
typedef enum LastResortSize {
kMonthNum = 13,
@@ -148,7 +160,10 @@
kEraLen = 3,
kZoneNum = 5,
- kZoneLen = 4
+ kZoneLen = 4,
+
+ kGmtHourNum = 4,
+ kGmtHourLen = 10
} LastResortSize;
U_NAMESPACE_BEGIN
@@ -171,39 +186,15 @@
static const char gNamesStandaloneTag[]="stand-alone";
static const char gAmPmMarkersTag[]="AmPmMarkers";
static const char gQuartersTag[]="quarters";
-static const char gMaptimezonesTag[]="mapTimezones";
-static const char gMetazonesTag[]="metazones";
-static const char gTerritoryTag[]="territory";
-static const char gCountriesTag[]="Countries";
-static const char gZoneFormattingTag[]="zoneFormatting";
-static const char gMultizoneTag[]="multizone";
-static const char gRegionFormatTag[]="zoneStrings/regionFormat";
-static const char gFallbackFormatTag[]="zoneStrings/fallbackFormat";
-/**
- * These are the tags we expect to see in time zone data resource bundle files
- * associated with a locale.
- */
static const char gZoneStringsTag[]="zoneStrings";
+static const char gGmtFormatTag[]="gmtFormat";
+static const char gHourFormatTag[]="hourFormat";
+
static const char gLocalPatternCharsTag[]="localPatternChars";
static UMTX LOCK;
-/*
- * Keep this variable in synch with max length of display strings
- */
-#define ZID_KEY_MAX 128
-#define UTZ_MAX_DISPLAY_STRINGS_LENGTH 7
-#define UTZ_SHORT_GENERIC "sg"
-#define UTZ_SHORT_STANDARD "ss"
-#define UTZ_SHORT_DAYLIGHT "sd"
-#define UTZ_LONG_GENERIC "lg"
-#define UTZ_LONG_STANDARD "ls"
-#define UTZ_LONG_DAYLIGHT "ld"
-#define UTZ_EXEMPLAR_CITY "ec"
-#define UTZ_USES_METAZONE "um"
-#define UTZ_COMMONLY_USED "cu"
-
/**
* Jitterbug 2974: MSVC has a bug whereby new X[0] behaves badly.
* Work around this.
@@ -212,12 +203,6 @@
return new UnicodeString[count ? count : 1];
}
-U_CDECL_BEGIN
-static void deleteUnicodeStringArray(void* obj) {
- delete[] (UnicodeString*)obj;
-}
-U_CDECL_END
-
//------------------------------------------------------
DateFormatSymbols::DateFormatSymbols(const Locale& locale,
@@ -327,26 +312,25 @@
assignArray(fShortQuarters, fShortQuartersCount, other.fShortQuarters, other.fShortQuartersCount);
assignArray(fStandaloneQuarters, fStandaloneQuartersCount, other.fStandaloneQuarters, other.fStandaloneQuartersCount);
assignArray(fStandaloneShortQuarters, fStandaloneShortQuartersCount, other.fStandaloneShortQuarters, other.fStandaloneShortQuartersCount);
- // the zoneStrings data is initialized on demand
- //fZoneStringsRowCount = other.fZoneStringsRowCount;
- //fZoneStringsColCount = other.fZoneStringsColCount;
- //createZoneStrings((const UnicodeString**)other.fZoneStrings);
- // initialize on demand
- fZoneStringsHash = NULL;
- fZoneIDEnumeration = NULL;
- fZoneStrings = NULL;
- fZoneStringsColCount = 0;
- fZoneStringsRowCount = 0;
- fResourceBundle = NULL;
- fCountry = other.fCountry;
- if(other.fZoneStringsHash!=NULL){
- fZoneStringsHash = createZoneStringsHash(other.fZoneStringsHash);
- fZoneIDEnumeration = other.fZoneIDEnumeration->clone();
- }else{
- UErrorCode status =U_ZERO_ERROR;
- fResourceBundle = ures_clone(other.fResourceBundle, &status);
- // TODO: what should be done in case of error?
+ fGmtFormat = other.fGmtFormat;
+ assignArray(fGmtHourFormats, fGmtHourFormatsCount, other.fGmtHourFormats, other.fGmtHourFormatsCount);
+
+ if (other.fZoneStrings != NULL) {
+ fZoneStringsColCount = other.fZoneStringsColCount;
+ fZoneStringsRowCount = other.fZoneStringsRowCount;
+ createZoneStrings((const UnicodeString**)other.fZoneStrings);
+
+ } else {
+ fZoneStrings = NULL;
+ fZoneStringsColCount = 0;
+ fZoneStringsRowCount = 0;
}
+ fZSFLocale = other.fZSFLocale;
+ // Other zone strings data is created on demand
+ fZoneStringFormat = NULL;
+ fLocaleZoneStrings = NULL;
+ fZSFCachePtr = NULL;
+ fZSFLocal = NULL;
// fastCopyFrom() - see assignArray comments
fLocalPatternChars.fastCopyFrom(other.fLocalPatternChars);
@@ -389,6 +373,7 @@
if (fShortQuarters) delete[] fShortQuarters;
if (fStandaloneQuarters) delete[] fStandaloneQuarters;
if (fStandaloneShortQuarters) delete[] fStandaloneShortQuarters;
+ if (fGmtHourFormats) delete[] fGmtHourFormats;
disposeZoneStrings();
}
@@ -396,23 +381,32 @@
void DateFormatSymbols::disposeZoneStrings()
{
if (fZoneStrings) {
- for (int32_t row=0; row<fZoneStringsRowCount; ++row)
+ for (int32_t row = 0; row < fZoneStringsRowCount; ++row) {
delete[] fZoneStrings[row];
+ }
uprv_free(fZoneStrings);
- }
- if(fZoneStringsHash){
- delete fZoneStringsHash;
- fZoneStringsHash = NULL;
}
- if(fZoneIDEnumeration){
- delete fZoneIDEnumeration;
- fZoneIDEnumeration = NULL;
+ if (fLocaleZoneStrings) {
+ for (int32_t row = 0; row < fZoneStringsRowCount; ++row) {
+ delete[] fLocaleZoneStrings[row];
+ }
+ uprv_free(fLocaleZoneStrings);
}
- if (fResourceBundle){
- ures_close(fResourceBundle);
- fResourceBundle = NULL;
+ if (fZSFLocal) {
+ delete fZSFLocal;
+ }
+ if (fZSFCachePtr) {
+ delete fZSFCachePtr;
}
+ fZoneStrings = NULL;
+ fLocaleZoneStrings = NULL;
+ fZoneStringsRowCount = 0;
+ fZoneStringsColCount = 0;
+
+ fZoneStringFormat = NULL;
+ fZSFLocal = NULL;
+ fZSFCachePtr = NULL;
}
UBool
@@ -454,7 +448,9 @@
fQuartersCount == other.fQuartersCount &&
fShortQuartersCount == other.fShortQuartersCount &&
fStandaloneQuartersCount == other.fStandaloneQuartersCount &&
- fStandaloneShortQuartersCount == other.fStandaloneShortQuartersCount)
+ fStandaloneShortQuartersCount == other.fStandaloneShortQuartersCount &&
+ fGmtHourFormatsCount == other.fGmtHourFormatsCount &&
+ fGmtFormat == other.fGmtFormat)
{
// Now compare the arrays themselves
if (arrayCompare(fEras, other.fEras, fErasCount) &&
@@ -475,25 +471,25 @@
arrayCompare(fQuarters, other.fQuarters, fQuartersCount) &&
arrayCompare(fShortQuarters, other.fShortQuarters, fShortQuartersCount) &&
arrayCompare(fStandaloneQuarters, other.fStandaloneQuarters, fStandaloneQuartersCount) &&
- arrayCompare(fStandaloneShortQuarters, other.fStandaloneShortQuarters, fStandaloneShortQuartersCount))
+ arrayCompare(fStandaloneShortQuarters, other.fStandaloneShortQuarters, fStandaloneShortQuartersCount) &&
+ arrayCompare(fGmtHourFormats, other.fGmtHourFormats, fGmtHourFormatsCount))
{
-
- if(fZoneStringsHash == NULL || other.fZoneStringsHash == NULL){
- // fZoneStringsHash is not initialized compare the resource bundles
- if(ures_equal(fResourceBundle, other.fResourceBundle)== FALSE){
- return FALSE;
+ // Compare the contents of fZoneStrings
+ if (fZoneStrings == NULL && other.fZoneStrings == NULL) {
+ if (fZSFLocale == other.fZSFLocale) {
+ return TRUE;
}
- }else{
- if(fZoneStringsHash->equals(*other.fZoneStringsHash) == FALSE){
- return FALSE;
+ } else if (fZoneStrings != NULL && other.fZoneStrings != NULL) {
+ if (fZoneStringsRowCount == other.fZoneStringsRowCount
+ && fZoneStringsColCount == other.fZoneStringsColCount) {
+ UBool cmpres = TRUE;
+ for (int32_t i = 0; (i < fZoneStringsRowCount) && cmpres; i++) {
+ cmpres = arrayCompare(fZoneStrings[i], other.fZoneStrings[i], fZoneStringsColCount);
+ }
+ return cmpres;
}
- // we always make sure that we update the enumeration when the hash is
- // updated. So we can be sure that once we compare the hashes the
- // enumerations are also equal
}
- // since fZoneStrings data member is deprecated .. and may not be initialized
- // so don't compare them
- return TRUE;
+ return FALSE;
}
}
return FALSE;
@@ -997,25 +993,73 @@
}
//------------------------------------------------------
+const ZoneStringFormat*
+DateFormatSymbols::getZoneStringFormat(void) const {
+ umtx_lock(&LOCK);
+ if (fZoneStringFormat == NULL) {
+ ((DateFormatSymbols*)this)->initZoneStringFormat();
+ }
+ umtx_unlock(&LOCK);
+ return fZoneStringFormat;
+}
+
+void
+DateFormatSymbols::initZoneStringFormat(void) {
+ if (fZoneStringFormat == NULL) {
+ UErrorCode status = U_ZERO_ERROR;
+ if (fZoneStrings) {
+ // Create an istance of ZoneStringFormat by the custom zone strings array
+ fZSFLocal = new ZoneStringFormat(fZoneStrings, fZoneStringsRowCount,
+ fZoneStringsColCount, status);
+ if (U_FAILURE(status)) {
+ delete fZSFLocal;
+ } else {
+ fZoneStringFormat = (const ZoneStringFormat*)fZSFLocal;
+ }
+ } else {
+ fZSFCachePtr = ZoneStringFormat::getZoneStringFormat(fZSFLocale, status);
+ if (U_FAILURE(status)) {
+ delete fZSFCachePtr;
+ } else {
+ fZoneStringFormat = fZSFCachePtr->get();
+ }
+ }
+ }
+}
const UnicodeString**
DateFormatSymbols::getZoneStrings(int32_t& rowCount, int32_t& columnCount) const
{
+ const UnicodeString **result = NULL;
+
umtx_lock(&LOCK);
- UErrorCode status = U_ZERO_ERROR;
- if(fZoneStrings==NULL){
- // cast away const to get around the problem for lazy initialization
- ((DateFormatSymbols*)this)->initZoneStringsArray(status);
+ if (fZoneStrings == NULL) {
+ if (fLocaleZoneStrings == NULL) {
+ ((DateFormatSymbols*)this)->initZoneStringsArray();
+ }
+ result = (const UnicodeString**)fLocaleZoneStrings;
+ } else {
+ result = (const UnicodeString**)fZoneStrings;
}
rowCount = fZoneStringsRowCount;
columnCount = fZoneStringsColCount;
umtx_unlock(&LOCK);
- if(U_FAILURE(status)){
- rowCount = 0;
- columnCount = 0;
- return NULL;
+
+ return result;
+}
+
+void
+DateFormatSymbols::initZoneStringsArray(void) {
+ if (fZoneStrings == NULL && fLocaleZoneStrings == NULL) {
+ if (fZoneStringFormat == NULL) {
+ initZoneStringFormat();
+ }
+ if (fZoneStringFormat) {
+ UErrorCode status = U_ZERO_ERROR;
+ fLocaleZoneStrings = fZoneStringFormat->createZoneStringsArray(uprv_getUTCtime() /* use current time */,
+ fZoneStringsRowCount, fZoneStringsColCount, status);
+ }
}
- return (const UnicodeString**)fZoneStrings; // Compiler requires cast
}
void
@@ -1030,7 +1074,6 @@
fZoneStringsRowCount = rowCount;
fZoneStringsColCount = columnCount;
createZoneStrings((const UnicodeString**)strings);
- initZoneStrings((const UnicodeString**)strings, rowCount,columnCount, status);
}
//------------------------------------------------------
@@ -1144,14 +1187,22 @@
fStandaloneQuartersCount = 0;
fStandaloneShortQuarters = NULL;
fStandaloneShortQuartersCount = 0;
+ fGmtHourFormats = NULL;
+ fGmtHourFormatsCount = 0;
fZoneStringsRowCount = 0;
fZoneStringsColCount = 0;
fZoneStrings = NULL;
- fZoneStringsHash = NULL;
- fZoneIDEnumeration = NULL;
- fResourceBundle = NULL;
- fCountry = NULL;
-
+ fLocaleZoneStrings = NULL;
+
+ fZoneStringFormat = NULL;
+ fZSFLocal = NULL;
+ fZSFCachePtr = NULL;
+
+ // We need to preserve the requested locale for
+ // lazy ZoneStringFormat instantiation. ZoneStringFormat
+ // is region sensitive, thus, bundle locale bundle's locale
+ // is not sufficient.
+ fZSFLocale = locale;
if (U_FAILURE(status)) return;
@@ -1161,8 +1212,12 @@
* these.
*/
CalendarData calData(locale, type, status);
- fResourceBundle = ures_open(NULL, locale.getName(), &status);
- fCountry = locale.getCountry();
+
+ /**
+ * Use the localeBundle for getting zone GMT formatting patterns
+ */
+ UResourceBundle *localeBundle = ures_open(NULL, locale.getName(), &status);
+ UResourceBundle *zoneStringsArray = ures_getByKeyWithFallback(localeBundle, gZoneStringsTag, NULL, &status);
// load the first data item
UResourceBundle *erasMain = calData.getByKey(gErasTag, status);
@@ -1212,6 +1267,8 @@
initField(&fShortQuarters, fShortQuartersCount, (const UChar *)gLastResortQuarters, kQuarterNum, kQuarterLen, status);
initField(&fStandaloneQuarters, fStandaloneQuartersCount, (const UChar *)gLastResortQuarters, kQuarterNum, kQuarterLen, status);
initField(&fStandaloneShortQuarters, fStandaloneShortQuartersCount, (const UChar *)gLastResortQuarters, kQuarterNum, kQuarterLen, status);
+ initField(&fGmtHourFormats, fGmtHourFormatsCount, (const UChar *)gLastResortGmtHourFormats, kGmtHourNum, kGmtHourLen, status);
+ fGmtFormat.setTo(TRUE, gLastResortGmtFormat, -1);
fLocalPatternChars.setTo(TRUE, gPatternChars, PATTERN_CHARS_LEN);
}
goto cleanup;
@@ -1274,6 +1331,41 @@
status = U_ZERO_ERROR;
initField(&fStandaloneShortQuarters, fStandaloneShortQuartersCount, calData.getByKey2(gQuartersTag, gNamesAbbrTag, status), status);
}
+
+ // GMT format patterns
+ resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gGmtFormatTag, &len, &status);
+ if (len > 0) {
+ fGmtFormat.setTo(TRUE, resStr, len);
+ }
+
+ resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gHourFormatTag, &len, &status);
+ if (len > 0) {
+ UChar *sep = u_strchr(resStr, (UChar)0x003B /* ';' */);
+ if (sep != NULL) {
+ fGmtHourFormats = newUnicodeStringArray(GMT_HOUR_COUNT);
+ if (fGmtHourFormats == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ } else {
+ fGmtHourFormatsCount = GMT_HOUR_COUNT;
+ fGmtHourFormats[GMT_NEGATIVE_HM].setTo(TRUE, sep + 1, -1);
+ fGmtHourFormats[GMT_POSITIVE_HM].setTo(FALSE, resStr, sep - resStr);
+
+ // CLDR 1.5 does not have GMT offset pattern including second field.
+ // For now, append "ss" to the end.
+ if (fGmtHourFormats[GMT_NEGATIVE_HM].indexOf((UChar)0x003A /* ':' */) != -1) {
+ fGmtHourFormats[GMT_NEGATIVE_HMS] = fGmtHourFormats[GMT_NEGATIVE_HM] + ":ss";
+ } else {
+ fGmtHourFormats[GMT_NEGATIVE_HMS] = fGmtHourFormats[GMT_NEGATIVE_HM] + "ss";
+ }
+ if (fGmtHourFormats[GMT_POSITIVE_HM].indexOf((UChar)0x003A /* ':' */) != -1) {
+ fGmtHourFormats[GMT_POSITIVE_HMS] = fGmtHourFormats[GMT_POSITIVE_HM] + ":ss";
+ } else {
+ fGmtHourFormats[GMT_POSITIVE_HMS] = fGmtHourFormats[GMT_POSITIVE_HM] + "ss";
+ }
+ }
+ }
+ }
+
// ICU 3.8 or later version no longer uses localized date-time pattern characters by default (ticket#5597)
/*
// fastCopyFrom()/setTo() - see assignArray comments
@@ -1411,55 +1503,8 @@
cleanup:
ures_close(eras);
ures_close(eraNames);
-}
-
-/**
- * Package private: used by SimpleDateFormat
- * Gets the index for the given time zone ID to obtain the timezone
- * strings for formatting. The time zone ID is just for programmatic
- * lookup. NOT LOCALIZED!!!
- * @param ID the given time zone ID.
- * @return the index of the given time zone ID. Returns -1 if
- * the given time zone ID can't be located in the DateFormatSymbols object.
- * @see java.util.SimpleTimeZone
- */
-int32_t DateFormatSymbols::getZoneIndex(const UnicodeString& ID) const
-{
- int32_t result = _getZoneIndex(ID);
- if (result >= 0) {
- return result;
- }
-
- // Do a search through the equivalency group for the given ID
- int32_t n = TimeZone::countEquivalentIDs(ID);
- if (n > 1) {
- int32_t i;
- for (i=0; i<n; ++i) {
- UnicodeString equivID = TimeZone::getEquivalentID(ID, i);
- if (equivID != ID) {
- int32_t equivResult = _getZoneIndex(equivID);
- if (equivResult >= 0) {
- return equivResult;
- }
- }
- }
- }
-
- return -1;
-}
-
-/**
- * Lookup the given ID. Do NOT do an equivalency search.
- */
-int32_t DateFormatSymbols::_getZoneIndex(const UnicodeString& ID) const
-{
- for(int32_t index = 0; index < fZoneStringsRowCount; index++) {
- if (0 == ID.caseCompare(fZoneStrings[index][0], 0)) {
- return index;
- }
- }
-
- return -1;
+ ures_close(zoneStringsArray);
+ ures_close(localeBundle);
}
Locale
@@ -1468,920 +1513,6 @@
return locBased.getLocale(type, status);
}
-class TimeZoneKeysEnumeration : public StringEnumeration {
-private:
- UnicodeString* strings;
- int32_t length;
- int32_t current;
- int32_t capacity;
- TimeZoneKeysEnumeration(UnicodeString* oldStrs, int32_t count){
- strings = newUnicodeStringArray(count);
- if(strings==NULL){
- return;
- }
- capacity = count;
- current = 0;
- for(length = 0; length<capacity; length++){
- strings[length].setTo(oldStrs[length]);
- }
- }
-public:
- static UClassID U_EXPORT2 getStaticClassID(void);
- virtual UClassID getDynamicClassID(void) const;
-
- TimeZoneKeysEnumeration(int32_t count, UErrorCode status){
- strings = newUnicodeStringArray(count);
- if(strings == NULL){
- status = U_MEMORY_ALLOCATION_ERROR;
- }
- length = 0;
- current = 0;
- capacity = count;
- }
-
- void put(const UnicodeString& str, UErrorCode& status){
- if(length < capacity){
- strings[length++].setTo(str);
- }else{
- status = U_INDEX_OUTOFBOUNDS_ERROR;
- }
- }
- virtual ~TimeZoneKeysEnumeration() {
- delete[] strings;
- }
-
- virtual StringEnumeration * clone() const
- {
- return new TimeZoneKeysEnumeration(strings, length);
- }
-
- virtual int32_t count(UErrorCode &/*status*/) const {
- return length;
- }
- virtual const UChar* unext(int32_t *resultLength, UErrorCode& /*status*/){
- if(current < length){
- const UChar* ret = strings[current].getBuffer();
- *resultLength = strings[current].length();
- current++;
- return ret;
- }
- return NULL;
- }
-
- virtual const UnicodeString* snext(UErrorCode& status) {
- if(U_FAILURE(status)){
- return NULL;
- }
- if(current < length){
- return &strings[current++];
- }
- return NULL;
- }
- /* this method is for thread safe iteration */
- const UnicodeString* snext(int32_t& pos, UErrorCode& status)const {
- if(U_FAILURE(status)){
- return NULL;
- }
- if(pos < length){
- return &strings[pos++];
- }
- return NULL;
- }
-
- virtual void reset(UErrorCode& /*status*/) {
- current = 0;
-
- }
-private:
- UBool equals(const StringEnumeration& other) const{
- if (other.getDynamicClassID() != TimeZoneKeysEnumeration::getStaticClassID()) {
- return FALSE;
- }
- TimeZoneKeysEnumeration& enum2 = (TimeZoneKeysEnumeration&)(other);
- UErrorCode status = U_ZERO_ERROR;
-
- int32_t count1 = count(status);
- int32_t count2 = other.count(status);
- if(count1 != count2){
- return FALSE;
- }
- int32_t pos1 = 0;
- int32_t pos2 = 0;
- const UnicodeString* str1 = NULL;
- const UnicodeString* str2 = NULL;
-
- while((str1 = snext(pos1, status))!=NULL){
- str2 = enum2.snext(pos2, status);
- if(U_FAILURE(status)){
- return FALSE;
- }
- if(*str1 != *str2){
- // bail out at the first failure
- return FALSE;
- }
-
- }
- // if we reached here that means that the enumerations are equal
- return TRUE;
- }
-public:
- virtual UBool operator==(const StringEnumeration& that)const{
- return ((this == &that) ||
- (getDynamicClassID() == that.getDynamicClassID() &&
- StringEnumeration::operator==(that) &&
- equals(that)));
- }
-};
-
-UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeZoneKeysEnumeration)
-
-void
-DateFormatSymbols::initZoneStringsArray(UErrorCode& status){
- if(fZoneStringsHash == NULL){
- initZoneStrings(status);
- }
- if(U_FAILURE(status)){
- return;
- }
- fZoneStringsRowCount = fZoneIDEnumeration->count(status);
- fZoneStringsColCount = 8;
- fZoneStrings = (UnicodeString **)uprv_malloc(fZoneStringsRowCount * sizeof(UnicodeString *));
- /* if we can't get a chunk of heap then the system is going down. Pin the blame on system*/
- if (fZoneStrings == NULL) {
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- const UnicodeString *zid = NULL;
- TimeZoneKeysEnumeration *keys = (TimeZoneKeysEnumeration*) fZoneIDEnumeration;
- int32_t pos = 0;
- int32_t i = 0;
- while((zid=keys->snext(pos,status))!=NULL){
- *(fZoneStrings+i) = newUnicodeStringArray(fZoneStringsColCount);
- /* test for NULL */
- if ((*(fZoneStrings+i)) == 0) {
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- UnicodeString* strings = (UnicodeString*)fZoneStringsHash->get(*zid);
- fZoneStrings[i][0].setTo(*zid);
- fZoneStrings[i][1].setTo(strings[TIMEZONE_LONG_STANDARD]);
- fZoneStrings[i][2].setTo(strings[TIMEZONE_SHORT_STANDARD]);
- fZoneStrings[i][3].setTo(strings[TIMEZONE_LONG_DAYLIGHT]);
- fZoneStrings[i][4].setTo(strings[TIMEZONE_SHORT_DAYLIGHT]);
- fZoneStrings[i][5].setTo(strings[TIMEZONE_EXEMPLAR_CITY]);
- fZoneStrings[i][6].setTo(strings[TIMEZONE_LONG_GENERIC]);
- fZoneStrings[i][7].setTo(strings[TIMEZONE_SHORT_GENERIC]);
- i++;
- }
-}
-
-U_CDECL_BEGIN
-static UBool U_CALLCONV
-compareTZHashValues(const UHashTok val1, const UHashTok val2){
-
- const UnicodeString* array1 = (UnicodeString*) val1.pointer;
- const UnicodeString* array2 = (UnicodeString*) val2.pointer;
- if(array1==array2){
- return TRUE;
- }
- if(array1==NULL || array2==NULL){
- return FALSE;
- }
- for(int32_t j=0; j< UTZ_MAX_DISPLAY_STRINGS_LENGTH; j++){
- if(array1[j] != array2[j]){
- return FALSE;
- }
- }
- return TRUE;
-}
-U_CDECL_END
-
-void
-DateFormatSymbols::initZoneStrings(UErrorCode &status){
- if(U_FAILURE(status)){
- return;
- }
-
- if(fZoneStringsHash != NULL){
- return;
- }
- int32_t i;
-
- fZoneStringsHash = new Hashtable(uhash_compareUnicodeString, compareTZHashValues, status);
- if(fZoneStringsHash==NULL){
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- fZoneStringsHash->setValueDeleter(deleteUnicodeStringArray);
-
- if(fResourceBundle != NULL){
- UnicodeString solidus = UNICODE_STRING_SIMPLE("/");
- UnicodeString colon = UNICODE_STRING_SIMPLE(":");
- UResourceBundle *zoneArray, *zoneItem;
- for(const UResourceBundle* rb = fResourceBundle; rb!=NULL; rb=ures_getParentBundle(rb)){
- zoneArray = ures_getByKey(rb, gZoneStringsTag, NULL, &status);
- if(U_FAILURE(status)){
- break;
- }
- while(ures_hasNext(zoneArray)){
- UErrorCode tempStatus = U_ZERO_ERROR;
- zoneItem = ures_getNextResource(zoneArray, NULL, &status);
- UnicodeString key(ures_getKey(zoneItem), -1, US_INV);
- if (key.indexOf(colon) == -1) {
- ures_close(zoneItem);
- continue;
- }
- UnicodeString* strArray = newUnicodeStringArray(UTZ_MAX_DISPLAY_STRINGS_LENGTH);
- key.findAndReplace(colon, solidus);
- int32_t len = 0;
- //fetch the strings with fine grained fallback
- const UChar* str = ures_getStringByKeyWithFallback(zoneItem,UTZ_SHORT_STANDARD, &len, &tempStatus);
- if(U_SUCCESS(tempStatus)){
- strArray[TIMEZONE_SHORT_STANDARD].setTo(TRUE, str, len);
- }else{
- tempStatus = U_ZERO_ERROR;
- }
- str = ures_getStringByKeyWithFallback(zoneItem,UTZ_SHORT_GENERIC, &len, &tempStatus);
- if(U_SUCCESS(tempStatus)){
- strArray[TIMEZONE_SHORT_GENERIC].setTo(TRUE, str, len);
- }else{
- tempStatus = U_ZERO_ERROR;
- }
- str = ures_getStringByKeyWithFallback(zoneItem,UTZ_SHORT_DAYLIGHT, &len, &tempStatus);
- if(U_SUCCESS(tempStatus)){
- strArray[TIMEZONE_SHORT_DAYLIGHT].setTo(TRUE, str, len);
- }else{
- tempStatus = U_ZERO_ERROR;
- }
- str = ures_getStringByKeyWithFallback(zoneItem,UTZ_LONG_STANDARD, &len, &tempStatus);
- if(U_SUCCESS(tempStatus)){
- strArray[TIMEZONE_LONG_STANDARD].setTo(TRUE, str, len);
- }else{
- tempStatus = U_ZERO_ERROR;
- }
- str = ures_getStringByKeyWithFallback(zoneItem,UTZ_LONG_GENERIC, &len, &tempStatus);
- if(U_SUCCESS(tempStatus)){
- strArray[TIMEZONE_LONG_GENERIC].setTo(TRUE, str, len);
- }else{
- tempStatus = U_ZERO_ERROR;
- }
- str = ures_getStringByKeyWithFallback(zoneItem,UTZ_LONG_DAYLIGHT, &len, &tempStatus);
- if(U_SUCCESS(tempStatus)){
- strArray[TIMEZONE_LONG_DAYLIGHT].setTo(TRUE, str, len);
- }else{
- tempStatus = U_ZERO_ERROR;
- }
- str = ures_getStringByKeyWithFallback(zoneItem,UTZ_EXEMPLAR_CITY, &len, &tempStatus);
- if(U_SUCCESS(tempStatus)){
- strArray[TIMEZONE_EXEMPLAR_CITY].setTo(TRUE, str, len);
- }else{
- tempStatus = U_ZERO_ERROR;
- }
- // store the strings in hash
- fZoneStringsHash->put(key, strArray, status);
- ures_close(zoneItem);
- }
-
- ures_close(zoneArray);
- }
-
- // Need to make sure that all zoneStrings in root are covered as well, otherwise metazone lookups won't
- // work properly
- UResourceBundle* root_res = ures_open(NULL, "", &status);
- zoneArray = ures_getByKey(root_res, gZoneStringsTag, NULL, &status);
- if (U_SUCCESS(status)) {
- while(ures_hasNext(zoneArray)){
- UErrorCode tempStatus = U_ZERO_ERROR;
- zoneItem = ures_getNextResource(zoneArray, NULL, &status);
- UnicodeString key(ures_getKey(zoneItem), -1, US_INV);
- if ( key.indexOf(colon) == -1 ) {
- ures_close(zoneItem);
- continue;
- }
- key.findAndReplace(colon, solidus);
-
- // Don't step on anything that is already there
- UnicodeString* existingArray = (UnicodeString*)fZoneStringsHash->get(key);
- if(existingArray != NULL){
- ures_close(zoneItem);
- continue;
- }
- UnicodeString* strArray = newUnicodeStringArray(UTZ_MAX_DISPLAY_STRINGS_LENGTH);
- int32_t len = 0;
-
- const UChar *str = ures_getStringByKeyWithFallback(zoneItem,UTZ_EXEMPLAR_CITY, &len, &tempStatus);
- if(U_SUCCESS(tempStatus)){
- strArray[TIMEZONE_EXEMPLAR_CITY].setTo(TRUE, str, len);
- }else{
- tempStatus = U_ZERO_ERROR;
- }
- // store the strings in hash
- fZoneStringsHash->put(key, strArray, status);
- ures_close(zoneItem);
- }
- ures_close(zoneArray);
- ures_close(root_res);
- }
-
- int32_t length = fZoneStringsHash->count();
- TimeZoneKeysEnumeration* keysEnum = new TimeZoneKeysEnumeration(length, status);
- fZoneIDEnumeration = keysEnum;
- if(fZoneIDEnumeration==NULL){
- delete fZoneStringsHash;
- fZoneStringsHash = NULL;
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- int32_t pos=-1;
- const UnicodeString* key;
- const UHashElement* elem = NULL;
- while((elem = fZoneStringsHash->nextElement(pos))!= NULL){
- const UHashTok keyTok = elem->key;
- key = (const UnicodeString*)keyTok.pointer;
- keysEnum->put(*key, status);
- }
- }else{
- //last resort strings
- UnicodeString* array = newUnicodeStringArray(UTZ_MAX_DISPLAY_STRINGS_LENGTH);
- if(array==NULL){
- delete fZoneStringsHash;
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- int32_t length = ARRAY_LENGTH(gLastResortZoneStrings);
- UnicodeString key(gLastResortZoneStrings[0]);
- TimeZoneKeysEnumeration* keysEnum = new TimeZoneKeysEnumeration(length, status);
- fZoneIDEnumeration = keysEnum;
- if(fZoneIDEnumeration==NULL){
- delete fZoneStringsHash;
- delete[] array;
- fZoneStringsHash = NULL;
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- keysEnum->put(key, status);
- int32_t j=1;
- for(i=0; i< length; ){
- array[i++].setTo(gLastResortZoneStrings[j++]);
- }
- fZoneStringsHash->put(key, array, status);
- }
-}
-void
-DateFormatSymbols::initZoneStrings(const UnicodeString** strings, int32_t rowCount, int32_t columnCount, UErrorCode& status){
- if(strings==NULL || rowCount<0 || columnCount<0){
- status = U_ILLEGAL_ARGUMENT_ERROR;
- return;
- }
- TimeZoneKeysEnumeration* keysEnum = new TimeZoneKeysEnumeration(rowCount, status);
- fZoneIDEnumeration = keysEnum;
- if(U_FAILURE(status)){
- return;
- }
- if(fZoneIDEnumeration==NULL){
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- fZoneStringsHash = new Hashtable(uhash_compareUnicodeString, compareTZHashValues, status);
- if(U_FAILURE(status)){
- return;
- }
- if(fZoneStringsHash==NULL){
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- fZoneStringsHash->setValueDeleter(deleteUnicodeStringArray);
- for (int32_t row=0; row<rowCount; ++row){
- // the first string in the array is the key.
- UnicodeString key = strings[row][0];
- keysEnum->put(key, status);
- UnicodeString* array = newUnicodeStringArray(UTZ_MAX_DISPLAY_STRINGS_LENGTH);
- if(array==NULL){
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- for (int32_t col=1; col<columnCount; ++col) {
- // fastCopyFrom() - see assignArray comments
- switch (col){
- case 1:
- array[TIMEZONE_LONG_STANDARD].setTo(strings[row][col]);
- break;
- case 2:
- array[TIMEZONE_SHORT_STANDARD].setTo(strings[row][col]);
- break;
- case 3:
- array[TIMEZONE_LONG_DAYLIGHT].setTo(strings[row][col]);
- break;
- case 4:
- array[TIMEZONE_LONG_DAYLIGHT].setTo(strings[row][col]);
- break;
- case 5:
- array[TIMEZONE_EXEMPLAR_CITY].setTo(strings[row][col]);
- break;
- case 6:
- array[TIMEZONE_LONG_GENERIC].setTo(strings[row][col]);
- break;
- case 7:
- array[TIMEZONE_SHORT_GENERIC].setTo(strings[row][col]);
- break;
- default:
- status = U_ILLEGAL_ARGUMENT_ERROR;
- }
- // populate the hash table
- fZoneStringsHash->put(strings[row][0], array, status);
- }
- }
-
-}
-
-UnicodeString&
-DateFormatSymbols::getZoneString(const UnicodeString &zid, const TimeZoneTranslationType type,
- UnicodeString &result, UErrorCode &status){
-
- if(fZoneStringsHash == NULL){
- //lazy initialization
- initZoneStrings(status);
- }
- if(U_FAILURE(status)){
- return result;
- }
-
- UnicodeString* stringsArray = (UnicodeString*)fZoneStringsHash->get(zid);
- if(stringsArray != NULL){
- result.setTo(stringsArray[type],0);
- }
- return result;
-}
-
-UnicodeString
-DateFormatSymbols::getMetazoneString(const UnicodeString &zid, const TimeZoneTranslationType type, Calendar &cal,
- UnicodeString &result, UErrorCode &status)
-{
- UErrorCode tempStatus = U_ZERO_ERROR;
- int32_t len;
- UnicodeString mzid(UNICODE_STRING_SIMPLE("meta/"));
-
- // Get the appropriate metazone mapping from the resource bundles
-
- char usesMetazoneKey[ZID_KEY_MAX];
- char zidkey[ZID_KEY_MAX];
-
- uprv_strcpy(usesMetazoneKey,gZoneStringsTag);
- uprv_strcat(usesMetazoneKey,"/");
-
- len = zid.length();
- len = (len >= (ZID_KEY_MAX-1) ? ZID_KEY_MAX-1 : len);
- u_UCharsToChars(zid.getBuffer(), zidkey, len);
- zidkey[len] = 0; // NULL terminate
-
- // Replace / with : for zid
- len = (int32_t)uprv_strlen(zidkey);
- for (int i = 0; i < len; i++) {
- if (zidkey[i] == '/') {
- zidkey[i] = ':';
- }
- }
-
- uprv_strcat(usesMetazoneKey,zidkey);
- uprv_strcat(usesMetazoneKey,"/");
- uprv_strcat(usesMetazoneKey,UTZ_USES_METAZONE);
-
- UResourceBundle *um = ures_getByKeyWithFallback(fResourceBundle, usesMetazoneKey, NULL, &tempStatus);
- if (U_FAILURE(tempStatus)) {
- return result;
- }
-
- UnicodeString* stringsArray = (UnicodeString*)fZoneStringsHash->get(zid);
-
- if(stringsArray != NULL){
- SimpleDateFormat df(UNICODE_STRING_SIMPLE("yyyy-MM-dd HH:mm"), Locale(""),tempStatus);
- TimeZone *tz = TimeZone::createTimeZone(UNICODE_STRING_SIMPLE("Etc/GMT"));
- df.setTimeZone(*tz);
- delete tz;
- UnicodeString theTime;
- df.format(cal.getTime(tempStatus),theTime);
-
- while (ures_hasNext(um)) {
- UResourceBundle *mz = ures_getNextResource(um,NULL,&status);
- const UChar *mz_name = ures_getStringByIndex(mz,0,&len,&status);
- const UChar *mz_from = ures_getStringByIndex(mz,1,&len,&status);
- const UChar *mz_to = ures_getStringByIndex(mz,2,&len,&status);
- ures_close(mz);
- if(U_FAILURE(status)){
- break;
- }
-
- if (mz_name[0] != 0 &&
- UnicodeString(TRUE, mz_from, -1) <= theTime &&
- UnicodeString(TRUE, mz_to, -1) > theTime )
- {
- mzid += mz_name;
- getZoneString(mzid,type,result,status);
- break;
- }
- }
- }
- ures_close(um);
- if ( mzid.length() > 5 ) {
- return mzid;
- }
- return result;
-}
-
-UnicodeString&
-DateFormatSymbols::getFallbackString(const UnicodeString &zid, UnicodeString &result, UErrorCode &status)
-{
- UnicodeString exemplarCity;
- char zidkey[ZID_KEY_MAX];
- char zoneTerritoryChars[ULOC_COUNTRY_CAPACITY];
- UnicodeString displayCountry;
- UnicodeString solidus = UNICODE_STRING_SIMPLE("/");
- UnicodeString und = UNICODE_STRING_SIMPLE("_");
- UnicodeString spc = UNICODE_STRING_SIMPLE(" ");
- const UChar* aZone = NULL;
- UBool IsMultiZone = FALSE;
-
-
- int32_t len = zid.length();
- len = (len >= (ZID_KEY_MAX-1) ? ZID_KEY_MAX-1 : len);
- u_UCharsToChars(zid.getBuffer(), zidkey, len);
- zidkey[len] = 0; // NULL terminate
-
- // Replace / with : for zid
- len = (int32_t)uprv_strlen(zidkey);
- for (int i = 0; i < len; i++) {
- if (zidkey[i] == '/') {
- zidkey[i] = ':';
- }
- }
-
- result.remove();
-
- UResourceBundle* supplementalDataBundle = ures_openDirect(NULL, kSUPPLEMENTAL, &status);
- if (U_FAILURE(status) || fResourceBundle == NULL ) {
- return result;
- }
-
- UResourceBundle* zoneFormatting = ures_getByKey(supplementalDataBundle, gZoneFormattingTag, NULL, &status);
- UResourceBundle* thisZone = ures_getByKey(zoneFormatting, zidkey, NULL, &status);
- if (U_FAILURE(status)) {
- ures_close(zoneFormatting);
- ures_close(supplementalDataBundle);
- return result;
- }
-
- UResourceBundle* multiZone = ures_getByKey(zoneFormatting, gMultizoneTag, NULL, &status);
- const UChar *zoneTerritory = ures_getStringByKey(thisZone,gTerritoryTag,&len,&status);
- u_UCharsToChars(zoneTerritory, zoneTerritoryChars, u_strlen(zoneTerritory));
- zoneTerritoryChars[u_strlen(zoneTerritory)] = 0; // NULL terminate
-
- UResourceBundle* countries = ures_getByKey(fResourceBundle, gCountriesTag, NULL, &status);
- if ( u_strlen(zoneTerritory) > 0 && countries != NULL ) {
- displayCountry = ures_getStringByKeyWithFallback(countries,zoneTerritoryChars,&len,&status);
- }
-
- if ( U_FAILURE(status) ) {
- status = U_ZERO_ERROR;
- displayCountry = UnicodeString(zoneTerritory);
- }
-
- while ( ures_hasNext(multiZone) ) {
- aZone = ures_getNextString(multiZone,&len,NULL,&status);
- if ( u_strcmp(aZone,zoneTerritory) == 0 ) {
- IsMultiZone = TRUE;
- continue;
- }
- }
-
- if ( IsMultiZone ) {
- getZoneString(zid, TIMEZONE_EXEMPLAR_CITY, exemplarCity, status);
- if ( exemplarCity.length()==0 ) {
- exemplarCity.setTo(UnicodeString(zid,zid.lastIndexOf(solidus)+1));
- exemplarCity.findAndReplace(und,spc);
- }
- Formattable cityCountryArray[2];
- UnicodeString pattern = UnicodeString(ures_getStringByKeyWithFallback(fResourceBundle,gFallbackFormatTag,&len,&status));
- if ( U_FAILURE(status) ) {
- pattern = UNICODE_STRING_SIMPLE("{1} ({0})");
- status = U_ZERO_ERROR;
- }
- cityCountryArray[0].adoptString(new UnicodeString(exemplarCity));
- cityCountryArray[1].adoptString(new UnicodeString(displayCountry));
- MessageFormat::format(pattern,cityCountryArray, 2, result, status);
- } else {
- Formattable countryArray[1];
- UnicodeString pattern = UnicodeString(ures_getStringByKeyWithFallback(fResourceBundle,gRegionFormatTag,&len,&status));
- if ( U_FAILURE(status) ) {
- pattern = UNICODE_STRING_SIMPLE("{0}");
- status = U_ZERO_ERROR;
- }
- countryArray[0].adoptString(new UnicodeString(displayCountry));
- MessageFormat::format(pattern,countryArray, 1, result, status);
- }
-
- ures_close(thisZone);
- ures_close(zoneFormatting);
- ures_close(supplementalDataBundle);
- ures_close(countries);
- ures_close(multiZone);
-
- return result;
-}
-
-UBool
-DateFormatSymbols::isCommonlyUsed(const UnicodeString &zid){
- UErrorCode status=U_ZERO_ERROR;
- UResourceBundle *zoneArray, *zoneItem, *cuRes;
- UnicodeString solidus = UNICODE_STRING_SIMPLE("/");
- UnicodeString colon = UNICODE_STRING_SIMPLE(":");
- UnicodeString key(zid);
- char keychars[ZID_KEY_MAX+1];
-
- key.findAndReplace(solidus,colon);
-
- for(const UResourceBundle* rb = fResourceBundle; rb!=NULL; rb=ures_getParentBundle(rb)){
- zoneArray = ures_getByKey(rb, gZoneStringsTag, NULL, &status);
- if(U_FAILURE(status)){
- status = U_ZERO_ERROR;
- continue;
- }
- int32_t len = key.length();
- u_UCharsToChars(key.getBuffer(), keychars, len);
- keychars[len] = 0; // NULL terminate
- zoneItem = ures_getByKey(zoneArray,keychars,NULL, &status);
- if(U_FAILURE(status)){
- ures_close(zoneArray);
- status = U_ZERO_ERROR;
- continue;
- }
-
- cuRes = ures_getByKey(zoneItem,UTZ_COMMONLY_USED,NULL,&status);
- if(U_FAILURE(status)){
- ures_close(zoneItem);
- ures_close(zoneArray);
- status = U_ZERO_ERROR;
- continue;
- }
- int32_t cuValue = ures_getInt(cuRes,&status);
-
- ures_close(cuRes);
- ures_close(zoneItem);
- ures_close(zoneArray);
-
- if(U_FAILURE(status)){
- status = U_ZERO_ERROR;
- continue;
- }
-
- if ( cuValue == 1 ) {
- return TRUE;
- }
- }
- return FALSE;
-}
-
-StringEnumeration*
-DateFormatSymbols::createZoneStringIDs(UErrorCode &status){
- if(U_FAILURE(status)){
- return NULL;
- }
- if(fZoneStringsHash == NULL){
- //lazy initialization
- initZoneStrings(status);
- }
- return fZoneIDEnumeration->clone();
-}
-
-/**
- * Sets timezone strings.
- * @draft ICU 3.6
- */
-void
-DateFormatSymbols::setZoneString(const UnicodeString &zid, const TimeZoneTranslationType type,
- const UnicodeString &value, UErrorCode &status){
- if(fZoneStringsHash == NULL){
- //lazy initialization
- initZoneStrings(status);
- }
- if(U_FAILURE(status)){
- return;
- }
- UnicodeString* stringsArray = (UnicodeString*)fZoneStringsHash->get(zid);
- if(stringsArray != NULL){
- stringsArray[type].setTo(value);
- }else{
- stringsArray = newUnicodeStringArray(UTZ_MAX_DISPLAY_STRINGS_LENGTH);
- if(stringsArray==NULL){
- status = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- stringsArray[type].setTo(value);
- fZoneStringsHash->put(zid, stringsArray, status);
- TimeZoneKeysEnumeration* keys = (TimeZoneKeysEnumeration*) fZoneIDEnumeration;
- keys->put(zid, status);
- }
-}
-
-Hashtable*
-DateFormatSymbols::createZoneStringsHash(const Hashtable* otherHash){
- UErrorCode status = U_ZERO_ERROR;
- Hashtable* hash = new Hashtable(uhash_compareUnicodeString, compareTZHashValues, status);
- if(hash==NULL){
- return NULL;
- }
- if(U_FAILURE(status)){
- return NULL;
- }
- hash->setValueDeleter(deleteUnicodeStringArray);
- int32_t pos = -1;
- const UHashElement* elem = NULL;
- // walk through the hash table and create a deep clone
- while((elem = otherHash->nextElement(pos))!= NULL){
- const UHashTok otherKeyTok = elem->key;
- const UHashTok otherValueTok = elem->value;
- UnicodeString* otherKey = (UnicodeString*)otherKeyTok.pointer;
- UnicodeString* otherArray = (UnicodeString*)otherValueTok.pointer;
- UnicodeString* array = newUnicodeStringArray(UTZ_MAX_DISPLAY_STRINGS_LENGTH);
- if(array==NULL){
- return NULL;
- }
- UnicodeString key(*otherKey);
- for(int32_t i=0; i<UTZ_MAX_DISPLAY_STRINGS_LENGTH; i++){
- array[i].setTo(otherArray[i]);
- }
- hash->put(key, array, status);
- if(U_FAILURE(status)){
- delete[] array;
- return NULL;
- }
- }
- return hash;
-}
-
-
-UnicodeString&
-DateFormatSymbols::getZoneID(const UnicodeString& zid, UnicodeString& result, UErrorCode& status){
- if(fZoneStringsHash == NULL){
- initZoneStrings(status);
- }
- if(U_FAILURE(status)){
- return result;
- }
- UnicodeString* strings = (UnicodeString*)fZoneStringsHash->get(zid);
- if (strings != NULL) {
- return result.setTo(zid,0);
- }
-
- // Do a search through the equivalency group for the given ID
- int32_t n = TimeZone::countEquivalentIDs(zid);
- if (n > 1) {
- int32_t i;
- for (i=0; i<n; ++i) {
- UnicodeString equivID = TimeZone::getEquivalentID(zid, i);
- if (equivID != zid) {
- strings = (UnicodeString*)fZoneStringsHash->get(equivID);
- if (strings != NULL) {
- return result.setTo(equivID,0);
- }
- }
- }
- }else{
- result.setTo(zid);
- }
- return result;
-}
-
-void
-DateFormatSymbols::getZoneType(const UnicodeString& zid, const UnicodeString& text, int32_t start,
- TimeZoneTranslationType& type, UnicodeString& value, UErrorCode& status){
- if(fZoneStringsHash == NULL){
- initZoneStrings(status);
- }
- if(U_FAILURE(status)){
- return;
- }
- type = TIMEZONE_COUNT;
- UnicodeString* strings = (UnicodeString*)fZoneStringsHash->get(zid);
- if(strings != NULL){
- for(int32_t j=0; j<UTZ_MAX_DISPLAY_STRINGS_LENGTH; j++){
- if(strings[j].length() >0 && text.caseCompare(start, strings[j].length(), strings[j], 0)==0){
- type = (TimeZoneTranslationType)j;
- value.setTo(strings[j]);
- return;
- }
- }
- }
-}
-void
-DateFormatSymbols::findZoneIDTypeValue( UnicodeString& zid, const UnicodeString& text, int32_t start,
- TimeZoneTranslationType& type, UnicodeString& value,
- UErrorCode& status){
- if(fZoneStringsHash == NULL){
- initZoneStrings(status);
- }
- if(U_FAILURE(status)){
- return;
- }
- const UnicodeString* myKey = NULL;
- int32_t pos = 0;
- TimeZoneKeysEnumeration *keys = (TimeZoneKeysEnumeration*)fZoneIDEnumeration;
- while( (myKey=keys->snext(pos, status))!= NULL){
- UnicodeString* strings = (UnicodeString*)fZoneStringsHash->get(*myKey);
- if(strings != NULL){
- for(int32_t j=0; j<UTZ_MAX_DISPLAY_STRINGS_LENGTH; j++){
- if(strings[j].length()>0 && text.caseCompare(start, strings[j].length(), strings[j], 0)==0){
- type = (TimeZoneTranslationType)j;
- value.setTo(strings[j]);
- if (myKey->startsWith(UNICODE_STRING_SIMPLE("meta"))) {
- zid.setTo(resolveParsedMetazone(*myKey));
- }
- else {
- zid.setTo(*myKey);
- }
- return;
- }
- }
- }
- }
-
- // Check for generic tz fallback strings if we have gone through all zone strings and haven't found
- // anything.
-
- UnicodeString fbString;
- StringEnumeration *tzKeys = TimeZone::createEnumeration();
-
- while( (myKey=tzKeys->snext(status))!= NULL){
- status = U_ZERO_ERROR;
- this->getFallbackString(*myKey,fbString,status);
- if ( U_FAILURE(status) ) {
- status = U_ZERO_ERROR;
- continue;
- }
-
- if(fbString.length()>0 && text.compare(start, fbString.length(), fbString)==0){
- type = (TimeZoneTranslationType) TIMEZONE_LONG_GENERIC;
- value.setTo(fbString);
- zid.setTo(*myKey);
- break;
- }
- }
- delete tzKeys;
-}
-
-UnicodeString
-DateFormatSymbols::resolveParsedMetazone( const UnicodeString& zid ) {
-
- UErrorCode status = U_ZERO_ERROR;
-
- UResourceBundle* supplementalDataBundle = ures_openDirect(NULL, kSUPPLEMENTAL, &status);
- UResourceBundle* mapTz = ures_getByKey(supplementalDataBundle, gMaptimezonesTag, NULL, &status);
- if(U_FAILURE(status)){
- ures_close(supplementalDataBundle);
- return UNICODE_STRING_SIMPLE("Etc/GMT");
- }
-
- UResourceBundle* metazoneMap = ures_getByKey(mapTz, gMetazonesTag, NULL, &status);
- char mzMapKey[ZID_KEY_MAX+4];
-
- int32_t len = zid.length();
- len = (len >= (ZID_KEY_MAX-1) ? ZID_KEY_MAX-1 : len);
- u_UCharsToChars(zid.getBuffer(), mzMapKey, len);
- mzMapKey[len] = 0; // NULL terminate
-
- for (int i = 0; i < len; i++) {
- if (mzMapKey[i] == '/') {
- mzMapKey[i] = ':';
- }
- }
-
- uprv_strcat(mzMapKey,"_");
- uprv_strcat(mzMapKey,fCountry);
-
- int32_t len2;
- const UChar* resStr = ures_getStringByKey(metazoneMap, mzMapKey, &len2, &status);
-
- // If we can't find a territory-specific metazone mapping, then use the generic one
- // which is the metazone name followed by _001
-
- if(U_FAILURE(status)){
- status = U_ZERO_ERROR;
- mzMapKey[len] = 0;
- uprv_strcat(mzMapKey,"_001");
- resStr = ures_getStringByKey(metazoneMap, mzMapKey, &len2, &status);
- }
-
- ures_close(metazoneMap);
- ures_close(mapTz);
- ures_close(supplementalDataBundle);
-
- if(U_SUCCESS(status)){
- return resStr;
- }
- else {
- return UNICODE_STRING_SIMPLE("Etc/GMT");
- }
-
-}
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/i18n.vcproj b/icu4c/source/i18n/i18n.vcproj
index 997445c..50ff078 100644
--- a/icu4c/source/i18n/i18n.vcproj
+++ b/icu4c/source/i18n/i18n.vcproj
@@ -1646,6 +1646,22 @@
RelativePath=".\winnmfmt.h"
>
</File>
+ <File
+ RelativePath=".\zonemeta.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\zonemeta.h"
+ >
+ </File>
+ <File
+ RelativePath=".\zstrfmt.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\zstrfmt.h"
+ >
+ </File>
</Filter>
<Filter
Name="misc"
diff --git a/icu4c/source/i18n/olsontz.cpp b/icu4c/source/i18n/olsontz.cpp
index 8599d4d..e98b9ef 100644
--- a/icu4c/source/i18n/olsontz.cpp
+++ b/icu4c/source/i18n/olsontz.cpp
@@ -317,11 +317,11 @@
millis, monthLength, ec);
}
- // Compute local epoch seconds from input fields
- double time = Grego::fieldsToDay(year, month, dom) * SECONDS_PER_DAY +
- uprv_floor(millis / (double) U_MILLIS_PER_SECOND);
-
- return zoneOffset(findTransition(time, TRUE)) * U_MILLIS_PER_SECOND;
+ // Compute local epoch millis from input fields
+ UDate date = (UDate)(Grego::fieldsToDay(year, month, dom) * U_MILLIS_PER_DAY + millis);
+ int32_t rawoff, dstoff;
+ getHistoricalOffset(date, TRUE, kDaylight, kStandard, rawoff, dstoff);
+ return rawoff + dstoff;
}
/**
@@ -332,40 +332,30 @@
if (U_FAILURE(ec)) {
return;
}
-
// The check against finalMillis will suffice most of the time, except
// for the case in which finalMillis == DBL_MAX, date == DBL_MAX,
// and finalZone == 0. For this case we add "&& finalZone != 0".
if (date >= finalMillis && finalZone != 0) {
- int32_t year, month, dom, dow;
- double millis;
- double days = Math::floorDivide(date, (double)U_MILLIS_PER_DAY, millis);
-
- Grego::dayToFields(days, year, month, dom, dow);
+ finalZone->getOffset(date, local, rawoff, dstoff, ec);
+ } else {
+ getHistoricalOffset(date, local, kFormer, kLatter, rawoff, dstoff);
+ }
+}
- rawoff = finalZone->getRawOffset();
-
- if (!local) {
- // Adjust from GMT to local
- date += rawoff;
- double days2 = Math::floorDivide(date, (double)U_MILLIS_PER_DAY, millis);
- if (days2 != days) {
- Grego::dayToFields(days2, year, month, dom, dow);
- }
- }
-
- dstoff = finalZone->getOffset(
- GregorianCalendar::AD, year, month,
- dom, (uint8_t) dow, (int32_t) millis, ec) - rawoff;
+void
+OlsonTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ int32_t& rawoff, int32_t& dstoff, UErrorCode& ec) /*const*/ {
+ if (U_FAILURE(ec)) {
return;
}
-
- double secs = uprv_floor(date / U_MILLIS_PER_SECOND);
- int16_t i = findTransition(secs, local);
- rawoff = rawOffset(i) * U_MILLIS_PER_SECOND;
- dstoff = dstOffset(i) * U_MILLIS_PER_SECOND;
+ if (date >= finalMillis && finalZone != 0) {
+ finalZone->getOffsetFromLocal(date, nonExistingTimeOpt, duplicatedTimeOpt, rawoff, dstoff, ec);
+ } else {
+ getHistoricalOffset(date, TRUE, nonExistingTimeOpt, duplicatedTimeOpt, rawoff, dstoff);
+ }
}
+
/**
* TimeZone API.
*/
@@ -394,69 +384,84 @@
double days = Math::floorDivide(((double)ms), (double)U_MILLIS_PER_DAY, millis);
Grego::dayToFields(days, year, month, dom, dow);
- U_DEBUG_TZ_MSG((" findTransition: time %.1f (%04d.%02d.%02d+%.1fh)\n", ms,
+ U_DEBUG_TZ_MSG((" getHistoricalOffset: time %.1f (%04d.%02d.%02d+%.1fh)\n", ms,
year, month+1, dom, (millis/kOneHour)));
}
#endif
-/**
- * Find the smallest i (in 0..transitionCount-1) such that time >=
- * transition(i), where transition(i) is either the GMT or the local
- * transition time, as specified by `local'.
- * @param time epoch seconds, either GMT or local wall
- * @param local if TRUE, `time' is in local wall units, otherwise it
- * is GMT
- * @return an index i, where 0 <= i < transitionCount, and
- * transition(i) <= time < transition(i+1), or i == 0 if
- * transitionCount == 0 or time < transition(0).
- */
-int16_t OlsonTimeZone::findTransition(double time, UBool local) const {
- int16_t i = 0;
- U_DEBUG_TZ_MSG(("findTransition(%.1f, %s)\n", time, local?"T":"F"));
+void
+OlsonTimeZone::getHistoricalOffset(UDate date, UBool local,
+ int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt,
+ int32_t& rawoff, int32_t& dstoff) const {
+ U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst)\n",
+ date, local?"T":"F", NonExistingTimeOpt, DuplicatedTimeOpt));
#if defined U_DEBUG_TZ
- printTime(time*1000.0);
+ printTime(date*1000.0);
#endif
-
if (transitionCount != 0) {
+ double sec = uprv_floor(date / U_MILLIS_PER_SECOND);
// Linear search from the end is the fastest approach, since
// most lookups will happen at/near the end.
+ int16_t i;
for (i = transitionCount - 1; i > 0; --i) {
int32_t transition = transitionTimes[i];
- if (local) {
- int32_t zoneOffsetPrev = zoneOffset(typeData[i-1]);
- int32_t zoneOffsetCurr = zoneOffset(typeData[i]);
-
- // use the lowest offset ( == standard time ). as per tzregts.cpp which says:
- /**
- * @bug 4084933
- * The expected behavior of TimeZone around the boundaries is:
- * (Assume transition time of 2:00 AM)
- * day of onset 1:59 AM STD = display name 1:59 AM ST
- * 2:00 AM STD = display name 3:00 AM DT
- * day of end 0:59 AM STD = display name 1:59 AM DT
- * 1:00 AM STD = display name 1:00 AM ST
- */
- if(zoneOffsetPrev<zoneOffsetCurr) {
- transition += zoneOffsetPrev;
+ if (local) {
+ int32_t offsetBefore = zoneOffset(typeData[i-1]);
+ UBool dstBefore = dstOffset(typeData[i-1]) != 0;
+
+ int32_t offsetAfter = zoneOffset(typeData[i]);
+ UBool dstAfter = dstOffset(typeData[i]) != 0;
+
+ UBool dstToStd = dstBefore && !dstAfter;
+ UBool stdToDst = !dstBefore && dstAfter;
+
+ if (offsetAfter - offsetBefore >= 0) {
+ // Positive transition, which makes a non-existing local time range
+ if (((NonExistingTimeOpt & kStdDstMask) == kStandard && dstToStd)
+ || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && stdToDst)) {
+ transition += offsetBefore;
+ } else if (((NonExistingTimeOpt & kStdDstMask) == kStandard && stdToDst)
+ || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && dstToStd)) {
+ transition += offsetAfter;
+ } else if ((NonExistingTimeOpt & kFormerLatterMask) == kLatter) {
+ transition += offsetBefore;
+ } else {
+ // Interprets the time with rule before the transition,
+ // default for non-existing time range
+ transition += offsetAfter;
+ }
} else {
- transition += zoneOffsetCurr;
+ // Negative transition, which makes a duplicated local time range
+ if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && dstToStd)
+ || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && stdToDst)) {
+ transition += offsetAfter;
+ } else if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && stdToDst)
+ || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && dstToStd)) {
+ transition += offsetBefore;
+ } else if ((DuplicatedTimeOpt & kFormerLatterMask) == kFormer) {
+ transition += offsetBefore;
+ } else {
+ // Interprets the time with rule after the transition,
+ // default for duplicated local time range
+ transition += offsetAfter;
+ }
}
}
- if (time >= transition) {
- U_DEBUG_TZ_MSG(("Found@%d: time=%.1f, localtransition=%d (orig %d) dz %d\n", i, time, transition, transitionTimes[i],
+ if (sec >= transition) {
+ U_DEBUG_TZ_MSG(("Found@%d: time=%.1f, localtransition=%d (orig %d) dz %d\n", i, sec, transition, transitionTimes[i],
zoneOffset(typeData[i-1])));
#if defined U_DEBUG_TZ
- printTime(transition*1000.0);
- printTime(transitionTimes[i]*1000.0);
+ printTime(transition*1000.0);
+ printTime(transitionTimes[i]*1000.0);
#endif
break;
} else {
- U_DEBUG_TZ_MSG(("miss@%d: time=%.1f, localtransition=%d (orig %d) dz %d\n", i, time, transition, transitionTimes[i],
+ U_DEBUG_TZ_MSG(("miss@%d: time=%.1f, localtransition=%d (orig %d) dz %d\n", i, sec, transition, transitionTimes[i],
zoneOffset(typeData[i-1])));
#if defined U_DEBUG_TZ
- printTime(transition*1000.0);
- printTime(transitionTimes[i]*1000.0);
+ printTime(transition*1000.0);
+ printTime(transitionTimes[i]*1000.0);
#endif
}
}
@@ -465,17 +470,25 @@
// Check invariants for GMT times; if these pass for GMT times
// the local logic should be working too.
- U_ASSERT(local || time < transitionTimes[0] || time >= transitionTimes[i]);
- U_ASSERT(local || i == transitionCount-1 || time < transitionTimes[i+1]);
+ U_ASSERT(local || sec < transitionTimes[0] || sec >= transitionTimes[i]);
+ U_ASSERT(local || i == transitionCount-1 || sec < transitionTimes[i+1]);
- U_DEBUG_TZ_MSG(("findTransition(%.1f, %s)= trans %d\n", time, local?"T":"F", i));
- i = typeData[i];
+ U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst) - trans %d\n",
+ date, local?"T":"F", NonExistingTimeOpt, DuplicatedTimeOpt, i));
+
+ // Since ICU tzdata 2007c, the first transition data is actually not a
+ // transition, but used for representing the initial offset. So the code
+ // below works even if i == 0.
+ int16_t index = typeData[i];
+ rawoff = rawOffset(index) * U_MILLIS_PER_SECOND;
+ dstoff = dstOffset(index) * U_MILLIS_PER_SECOND;
+ } else {
+ // No transitions, single pair of offsets only
+ rawoff = rawOffset(0) * U_MILLIS_PER_SECOND;
+ dstoff = dstOffset(0) * U_MILLIS_PER_SECOND;
}
-
- U_ASSERT(i>=0 && i<typeCount);
-
- U_DEBUG_TZ_MSG(("findTransition(%.1f, %s)=%d, offset %d\n", time, local?"T":"F", i, zoneOffset(i)));
- return i;
+ U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst) - raw=%d, dst=%d\n",
+ date, local?"T":"F", NonExistingTimeOpt, DuplicatedTimeOpt, rawoff, dstoff));
}
/**
diff --git a/icu4c/source/i18n/olsontz.h b/icu4c/source/i18n/olsontz.h
index d5c4bce..64f8c0d 100644
--- a/icu4c/source/i18n/olsontz.h
+++ b/icu4c/source/i18n/olsontz.h
@@ -182,6 +182,12 @@
int32_t& dstOffset, UErrorCode& ec) const;
/**
+ * BasicTimeZone API.
+ */
+ virtual void getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ int32_t& rawoff, int32_t& dstoff, UErrorCode& ec) /*const*/;
+
+ /**
* TimeZone API. This method has no effect since objects of this
* class are quasi-immutable (the base class allows the ID to be
* changed).
@@ -279,7 +285,9 @@
void constructEmpty();
- int16_t findTransition(double time, UBool local) const;
+ void getHistoricalOffset(UDate date, UBool local,
+ int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt,
+ int32_t& rawoff, int32_t& dstoff) const;
int32_t zoneOffset(int16_t index) const;
int32_t rawOffset(int16_t index) const;
diff --git a/icu4c/source/i18n/rbtz.cpp b/icu4c/source/i18n/rbtz.cpp
index 3894376..f810d70 100644
--- a/icu4c/source/i18n/rbtz.cpp
+++ b/icu4c/source/i18n/rbtz.cpp
@@ -46,14 +46,6 @@
return TRUE;
}
-static UDate getTransitionTime(Transition* transition, UBool local) {
- UDate time = transition->time;
- if (local) {
- time += transition->from->getRawOffset() + transition->from->getDSTSavings();
- }
- return time;
-}
-
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedTimeZone)
RuleBasedTimeZone::RuleBasedTimeZone(const UnicodeString& id, InitialTimeZoneRule* initialRule)
@@ -183,7 +175,7 @@
for (i = 0; i < historicCount; i++) {
done[i] = FALSE;
}
- while (true) {
+ while (TRUE) {
int32_t curStdOffset = curRule->getRawOffset();
int32_t curDstSavings = curRule->getDSTSavings();
UDate nextTransitionTime = MAX_MILLIS;
@@ -377,7 +369,7 @@
}
int32_t rawOffset, dstOffset;
UDate time = (UDate)Grego::fieldsToDay(year, month, day) * U_MILLIS_PER_DAY + millis;
- getOffset(time, true, rawOffset, dstOffset, status);
+ getOffsetInternal(time, TRUE, kDaylight, kStandard, rawOffset, dstOffset, status);
if (U_FAILURE(status)) {
return 0;
}
@@ -387,6 +379,24 @@
void
RuleBasedTimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset,
int32_t& dstOffset, UErrorCode& status) const {
+ getOffsetInternal(date, local, kFormer, kLatter, rawOffset, dstOffset, status);
+}
+
+void
+RuleBasedTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) /*const*/ {
+ getOffsetInternal(date, TRUE, nonExistingTimeOpt, duplicatedTimeOpt, rawOffset, dstOffset, status);
+}
+
+
+/*
+ * The internal getOffset implementation
+ */
+void
+RuleBasedTimeZone::getOffsetInternal(UDate date, UBool local,
+ int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt,
+ int32_t& rawOffset, int32_t& dstOffset,
+ UErrorCode& status) const {
rawOffset = 0;
dstOffset = 0;
@@ -404,15 +414,17 @@
if (fHistoricTransitions == NULL) {
rule = fInitialRule;
} else {
- UDate tstart = getTransitionTime((Transition*)fHistoricTransitions->elementAt(0), local);
+ UDate tstart = getTransitionTime((Transition*)fHistoricTransitions->elementAt(0),
+ local, NonExistingTimeOpt, DuplicatedTimeOpt);
if (date < tstart) {
rule = fInitialRule;
} else {
int32_t idx = fHistoricTransitions->size() - 1;
- UDate tend = getTransitionTime((Transition*)fHistoricTransitions->elementAt(idx), local);
+ UDate tend = getTransitionTime((Transition*)fHistoricTransitions->elementAt(idx),
+ local, NonExistingTimeOpt, DuplicatedTimeOpt);
if (date > tend) {
if (fFinalRules != NULL) {
- rule = findRuleInFinal(date, local);
+ rule = findRuleInFinal(date, local, NonExistingTimeOpt, DuplicatedTimeOpt);
} else {
// no final rule, use the last rule
rule = ((Transition*)fHistoricTransitions->elementAt(idx))->to;
@@ -420,7 +432,8 @@
} else {
// Find a historical transition
while (idx >= 0) {
- if (date >= getTransitionTime((Transition*)fHistoricTransitions->elementAt(idx), local)) {
+ if (date >= getTransitionTime((Transition*)fHistoricTransitions->elementAt(idx),
+ local, NonExistingTimeOpt, DuplicatedTimeOpt)) {
break;
}
idx--;
@@ -651,7 +664,8 @@
}
TimeZoneRule*
-RuleBasedTimeZone::findRuleInFinal(UDate date, UBool local) const {
+RuleBasedTimeZone::findRuleInFinal(UDate date, UBool local,
+ int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const {
if (fFinalRules == NULL) {
return NULL;
}
@@ -664,12 +678,25 @@
UDate start0, start1;
UDate base;
+ int32_t localDelta;
- base = local ? date - fr1->getRawOffset() - fr1->getDSTSavings() : date;
- UBool avail0 = fr0->getPreviousStart(base, fr1->getRawOffset(), fr1->getDSTSavings(), true, start0);
+ base = date;
+ if (local) {
+ localDelta = getLocalDelta(fr1->getRawOffset(), fr1->getDSTSavings(),
+ fr0->getRawOffset(), fr0->getDSTSavings(),
+ NonExistingTimeOpt, DuplicatedTimeOpt);
+ base -= localDelta;
+ }
+ UBool avail0 = fr0->getPreviousStart(base, fr1->getRawOffset(), fr1->getDSTSavings(), TRUE, start0);
- base = local ? date - fr0->getRawOffset() - fr0->getDSTSavings() : date;
- UBool avail1 = fr1->getPreviousStart(base, fr0->getRawOffset(), fr0->getDSTSavings(), true, start1);
+ base = date;
+ if (local) {
+ localDelta = getLocalDelta(fr0->getRawOffset(), fr0->getDSTSavings(),
+ fr1->getRawOffset(), fr1->getDSTSavings(),
+ NonExistingTimeOpt, DuplicatedTimeOpt);
+ base -= localDelta;
+ }
+ UBool avail1 = fr1->getPreviousStart(base, fr0->getRawOffset(), fr0->getDSTSavings(), TRUE, start1);
if (avail0 && (!avail1 || start0 > start1)) {
return fr0;
@@ -689,14 +716,14 @@
UBool found = FALSE;
Transition result;
Transition *tzt = (Transition*)fHistoricTransitions->elementAt(0);
- UDate tt = getTransitionTime(tzt, FALSE);
+ UDate tt = tzt->time;
if (tt > base || (inclusive && tt == base)) {
result = *tzt;
found = TRUE;
} else {
int32_t idx = fHistoricTransitions->size() - 1;
tzt = (Transition*)fHistoricTransitions->elementAt(idx);
- tt = getTransitionTime(tzt, FALSE);
+ tt = tzt->time;
if (inclusive && tt == base) {
result = *tzt;
found = TRUE;
@@ -730,7 +757,7 @@
Transition *prev = tzt;
while (idx > 0) {
tzt = (Transition*)fHistoricTransitions->elementAt(idx);
- tt = getTransitionTime(tzt, FALSE);
+ tt = tzt->time;
if (tt < base || (!inclusive && tt == base)) {
break;
}
@@ -772,14 +799,14 @@
UBool found = FALSE;
Transition result;
Transition *tzt = (Transition*)fHistoricTransitions->elementAt(0);
- UDate tt = getTransitionTime(tzt, FALSE);
+ UDate tt = tzt->time;
if (inclusive && tt == base) {
result = *tzt;
found = TRUE;
} else if (tt < base) {
int32_t idx = fHistoricTransitions->size() - 1;
tzt = (Transition*)fHistoricTransitions->elementAt(idx);
- tt = getTransitionTime(tzt, FALSE);
+ tt = tzt->time;
if (inclusive && tt == base) {
result = *tzt;
found = TRUE;
@@ -813,7 +840,7 @@
idx--;
while (idx >= 0) {
tzt = (Transition*)fHistoricTransitions->elementAt(idx);
- tt = getTransitionTime(tzt, FALSE);
+ tt = tzt->time;
if (tt < base || (inclusive && tt == base)) {
break;
}
@@ -839,6 +866,63 @@
return FALSE;
}
+UDate
+RuleBasedTimeZone::getTransitionTime(Transition* transition, UBool local,
+ int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const {
+ UDate time = transition->time;
+ if (local) {
+ time += getLocalDelta(transition->from->getRawOffset(), transition->from->getDSTSavings(),
+ transition->to->getRawOffset(), transition->to->getDSTSavings(),
+ NonExistingTimeOpt, DuplicatedTimeOpt);
+ }
+ return time;
+}
+
+int32_t
+RuleBasedTimeZone::getLocalDelta(int32_t rawBefore, int32_t dstBefore, int32_t rawAfter, int32_t dstAfter,
+ int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const {
+ int32_t delta = 0;
+
+ int32_t offsetBefore = rawBefore + dstBefore;
+ int32_t offsetAfter = rawAfter + dstAfter;
+
+ UBool dstToStd = (dstBefore != 0) && (dstAfter == 0);
+ UBool stdToDst = (dstBefore == 0) && (dstAfter != 0);
+
+ if (offsetAfter - offsetBefore >= 0) {
+ // Positive transition, which makes a non-existing local time range
+ if (((NonExistingTimeOpt & kStdDstMask) == kStandard && dstToStd)
+ || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && stdToDst)) {
+ delta = offsetBefore;
+ } else if (((NonExistingTimeOpt & kStdDstMask) == kStandard && stdToDst)
+ || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && dstToStd)) {
+ delta = offsetAfter;
+ } else if ((NonExistingTimeOpt & kFormerLatterMask) == kLatter) {
+ delta = offsetBefore;
+ } else {
+ // Interprets the time with rule before the transition,
+ // default for non-existing time range
+ delta = offsetAfter;
+ }
+ } else {
+ // Negative transition, which makes a duplicated local time range
+ if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && dstToStd)
+ || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && stdToDst)) {
+ delta = offsetAfter;
+ } else if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && stdToDst)
+ || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && dstToStd)) {
+ delta = offsetBefore;
+ } else if ((DuplicatedTimeOpt & kFormerLatterMask) == kFormer) {
+ delta = offsetBefore;
+ } else {
+ // Interprets the time with rule after the transition,
+ // default for duplicated local time range
+ delta = offsetAfter;
+ }
+ }
+ return delta;
+}
+
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/simpletz.cpp b/icu4c/source/i18n/simpletz.cpp
index ca827f3..c9cdade 100644
--- a/icu4c/source/i18n/simpletz.cpp
+++ b/icu4c/source/i18n/simpletz.cpp
@@ -502,6 +502,55 @@
return result;
}
+void
+SimpleTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ int32_t& rawOffsetGMT, int32_t& savingsDST, UErrorCode& status) /*const*/ {
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ rawOffsetGMT = getRawOffset();
+ int32_t year, month, dom, dow;
+ double day = uprv_floor(date / U_MILLIS_PER_DAY);
+ int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
+
+ Grego::dayToFields(day, year, month, dom, dow);
+
+ savingsDST = getOffset(GregorianCalendar::AD, year, month, dom,
+ (uint8_t) dow, millis,
+ Grego::monthLength(year, month),
+ status) - rawOffsetGMT;
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ UBool recalc = FALSE;
+
+ // Now we need some adjustment
+ if (savingsDST > 0) {
+ if ((nonExistingTimeOpt & kStdDstMask) == kStandard
+ || (nonExistingTimeOpt & kStdDstMask) != kDaylight && (nonExistingTimeOpt & kFormerLatterMask) != kLatter) {
+ date -= getDSTSavings();
+ recalc = TRUE;
+ }
+ } else {
+ if ((duplicatedTimeOpt & kStdDstMask) == kDaylight
+ || (duplicatedTimeOpt & kStdDstMask) != kStandard && (duplicatedTimeOpt & kFormerLatterMask) == kFormer) {
+ date -= getDSTSavings();
+ recalc = TRUE;
+ }
+ }
+ if (recalc) {
+ day = uprv_floor(date / U_MILLIS_PER_DAY);
+ millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
+ Grego::dayToFields(day, year, month, dom, dow);
+ savingsDST = getOffset(GregorianCalendar::AD, year, month, dom,
+ (uint8_t) dow, millis,
+ Grego::monthLength(year, month),
+ status) - rawOffsetGMT;
+ }
+}
+
// -------------------------------------
/**
@@ -966,8 +1015,8 @@
return FALSE;
}
UDate stdDate, dstDate;
- UBool stdAvail = stdRule->getPreviousStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), false, stdDate);
- UBool dstAvail = dstRule->getPreviousStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), false, dstDate);
+ UBool stdAvail = stdRule->getPreviousStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate);
+ UBool dstAvail = dstRule->getPreviousStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate);
if (stdAvail && (!dstAvail || stdDate > dstDate)) {
result.setTime(stdDate);
result.setFrom((const TimeZoneRule&)*dstRule);
diff --git a/icu4c/source/i18n/smpdtfmt.cpp b/icu4c/source/i18n/smpdtfmt.cpp
index f9ac0cc..b52134d 100644
--- a/icu4c/source/i18n/smpdtfmt.cpp
+++ b/icu4c/source/i18n/smpdtfmt.cpp
@@ -41,10 +41,18 @@
#include "unicode/dcfmtsym.h"
#include "unicode/uchar.h"
#include "unicode/ustring.h"
+#include "unicode/basictz.h"
+#include "unicode/simpletz.h"
+#include "unicode/rbtz.h"
+#include "unicode/vtzone.h"
+#include "olsontz.h"
#include "util.h"
#include "gregoimp.h"
#include "cstring.h"
#include "uassert.h"
+#include "zstrfmt.h"
+#include "cmemory.h"
+#include "umutex.h"
#include <float.h>
#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
@@ -66,6 +74,19 @@
static const UChar gGmt[] = {0x0047, 0x004D, 0x0054, 0x0000}; // "GMT"
static const UChar gGmtPlus[] = {0x0047, 0x004D, 0x0054, 0x002B, 0x0000}; // "GMT+"
static const UChar gGmtMinus[] = {0x0047, 0x004D, 0x0054, 0x002D, 0x0000}; // "GMT-"
+static const UChar gDefGmtPat[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0x0000}; /* GMT{0} */
+static const UChar gDefGmtNegHmsPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* -HH:mm:ss */
+static const UChar gDefGmtNegHmPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* -HH:mm */
+static const UChar gDefGmtPosHmsPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* +HH:mm:ss */
+static const UChar gDefGmtPosHmPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* +HH:mm */
+typedef enum GmtPatSize {
+ kGmtLen = 3,
+ kGmtPatLen = 6,
+ kNegHmsLen = 9,
+ kNegHmLen = 6,
+ kPosHmsLen = 9,
+ kPosHmLen = 6
+} GmtPatSize;
// This is a pattern-of-last-resort used when we can't load a usable pattern out
// of a resource.
@@ -83,21 +104,36 @@
* These are the tags we expect to see in normal resource bundle files associated
* with a locale.
*/
-static const char kSUPPLEMENTAL[]="supplementalData";
-static const char gZoneFormattingTag[]="zoneFormatting";
-static const char gAliases[]="aliases";
static const char gDateTimePatternsTag[]="DateTimePatterns";
-UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat)
-
+static const UChar gEtcUTC[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x54, 0x43, 0x00}; // "Etc/UTC"
static const UChar QUOTE = 0x27; // Single quote
+enum {
+ kGMTNegativeHMS = 0,
+ kGMTNegativeHM,
+ kGMTPositiveHMS,
+ kGMTPositiveHM,
+
+ kNumGMTFormatters
+};
+
+static UMTX LOCK;
+
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat)
//----------------------------------------------------------------------
SimpleDateFormat::~SimpleDateFormat()
{
delete fSymbols;
- delete parsedTimeZone; // sanity check
+ if (fGMTFormatters) {
+ for (int32_t i = 0; i < kNumGMTFormatters; i++) {
+ if (fGMTFormatters[i]) {
+ delete fGMTFormatters[i];
+ }
+ }
+ uprv_free(fGMTFormatters);
+ }
}
//----------------------------------------------------------------------
@@ -105,7 +141,7 @@
SimpleDateFormat::SimpleDateFormat(UErrorCode& status)
: fLocale(Locale::getDefault()),
fSymbols(NULL),
- parsedTimeZone(NULL)
+ fGMTFormatters(NULL)
{
construct(kShort, (EStyle) (kShort + kDateOffset), fLocale, status);
initializeDefaultCentury();
@@ -118,7 +154,7 @@
: fPattern(pattern),
fLocale(Locale::getDefault()),
fSymbols(NULL),
- parsedTimeZone(NULL)
+ fGMTFormatters(NULL)
{
initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status);
initialize(fLocale, status);
@@ -132,7 +168,7 @@
UErrorCode& status)
: fPattern(pattern),
fLocale(locale),
- parsedTimeZone(NULL)
+ fGMTFormatters(NULL)
{
initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status);
initialize(fLocale, status);
@@ -147,7 +183,7 @@
: fPattern(pattern),
fLocale(Locale::getDefault()),
fSymbols(symbolsToAdopt),
- parsedTimeZone(NULL)
+ fGMTFormatters(NULL)
{
initializeCalendar(NULL,fLocale,status);
initialize(fLocale, status);
@@ -162,7 +198,7 @@
: fPattern(pattern),
fLocale(Locale::getDefault()),
fSymbols(new DateFormatSymbols(symbols)),
- parsedTimeZone(NULL)
+ fGMTFormatters(NULL)
{
initializeCalendar(NULL, fLocale, status);
initialize(fLocale, status);
@@ -178,7 +214,7 @@
UErrorCode& status)
: fLocale(locale),
fSymbols(NULL),
- parsedTimeZone(NULL)
+ fGMTFormatters(NULL)
{
construct(timeStyle, dateStyle, fLocale, status);
if(U_SUCCESS(status)) {
@@ -198,7 +234,7 @@
: fPattern(gDefaultPattern),
fLocale(locale),
fSymbols(NULL),
- parsedTimeZone(NULL)
+ fGMTFormatters(NULL)
{
if (U_FAILURE(status)) return;
initializeSymbols(fLocale, initializeCalendar(NULL, fLocale, status),status);
@@ -226,7 +262,7 @@
SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat& other)
: DateFormat(other),
fSymbols(NULL),
- parsedTimeZone(NULL)
+ fGMTFormatters(NULL)
{
*this = other;
}
@@ -243,8 +279,6 @@
delete fSymbols;
fSymbols = NULL;
- delete parsedTimeZone; parsedTimeZone = NULL;
-
if (other.fSymbols)
fSymbols = new DateFormatSymbols(*other.fSymbols);
@@ -585,20 +619,336 @@
}
//---------------------------------------------------------------------
-inline void SimpleDateFormat::appendGMT(UnicodeString &appendTo, Calendar& cal, UErrorCode& status) const{
- int32_t value = cal.get(UCAL_ZONE_OFFSET, status) +
- cal.get(UCAL_DST_OFFSET, status);
+void
+SimpleDateFormat::appendGMT(UnicodeString &appendTo, Calendar& cal, UErrorCode& status) const{
+ int32_t offset = cal.get(UCAL_ZONE_OFFSET, status) + cal.get(UCAL_DST_OFFSET, status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ if (isDefaultGMTFormat()) {
+ formatGMTDefault(appendTo, offset);
+ } else {
+ ((SimpleDateFormat*)this)->initGMTFormatters(status);
+ if (U_SUCCESS(status)) {
+ int32_t type;
+ if (offset < 0) {
+ offset = -offset;
+ type = (offset % U_MILLIS_PER_MINUTE) == 0 ? kGMTNegativeHM : kGMTNegativeHMS;
+ } else {
+ type = (offset % U_MILLIS_PER_MINUTE) == 0 ? kGMTPositiveHM : kGMTPositiveHMS;
+ }
+ Formattable param(offset, Formattable::kIsDate);
+ FieldPosition fpos(0);
+ fGMTFormatters[type]->format(¶m, 1, appendTo, fpos, status);
+ }
+ }
+}
- if (value < 0) {
+int32_t
+SimpleDateFormat::parseGMT(const UnicodeString &text, ParsePosition &pos) const {
+ if (!isDefaultGMTFormat()) {
+ int32_t start = pos.getIndex();
+
+ // Quick check
+ UBool prefixMatch = FALSE;
+ int32_t prefixLen = fSymbols->fGmtFormat.indexOf((UChar)0x007B /* '{' */);
+ if (prefixLen > 0 && text.compare(start, prefixLen, fSymbols->fGmtFormat, 0, prefixLen) == 0) {
+ prefixMatch = TRUE;
+ }
+ if (prefixMatch) {
+ // Prefix matched
+ UErrorCode status = U_ZERO_ERROR;
+ ((SimpleDateFormat*)this)->initGMTFormatters(status);
+ if (U_SUCCESS(status)) {
+ Formattable parsed;
+ int32_t parsedCount;
+
+ // Try negative Hms
+ fGMTFormatters[kGMTNegativeHMS]->parseObject(text, parsed, pos);
+ if (pos.getErrorIndex() == -1 && pos.getIndex() > start) {
+ parsed.getArray(parsedCount);
+ if (parsedCount == 1 && parsed[0].getType() == Formattable::kDate) {
+ return (int32_t)(-1 * parsed[0].getDate());
+ }
+ }
+
+ // Reset ParsePosition
+ pos.setIndex(start);
+ pos.setErrorIndex(-1);
+
+ // Try positive Hms
+ fGMTFormatters[kGMTPositiveHMS]->parseObject(text, parsed, pos);
+ if (pos.getErrorIndex() == -1 && pos.getIndex() > start) {
+ parsed.getArray(parsedCount);
+ if (parsedCount == 1 && parsed[0].getType() == Formattable::kDate) {
+ return (int32_t)parsed[0].getDate();
+ }
+ }
+
+ // Reset ParsePosition
+ pos.setIndex(start);
+ pos.setErrorIndex(-1);
+
+ // Try negative Hm
+ fGMTFormatters[kGMTNegativeHM]->parseObject(text, parsed, pos);
+ if (pos.getErrorIndex() == -1 && pos.getIndex() > start) {
+ parsed.getArray(parsedCount);
+ if (parsedCount == 1 && parsed[0].getType() == Formattable::kDate) {
+ return (int32_t)(-1 * parsed[0].getDate());
+ }
+ }
+
+ // Reset ParsePosition
+ pos.setIndex(start);
+ pos.setErrorIndex(-1);
+
+ // Try positive Hm
+ fGMTFormatters[kGMTPositiveHM]->parseObject(text, parsed, pos);
+ if (pos.getErrorIndex() == -1 && pos.getIndex() > start) {
+ parsed.getArray(parsedCount);
+ if (parsedCount == 1 && parsed[0].getType() == Formattable::kDate) {
+ return (int32_t)parsed[0].getDate();
+ }
+ }
+
+ // Reset ParsePosition
+ pos.setIndex(start);
+ pos.setErrorIndex(-1);
+ }
+ // fall through to the default GMT parsing method
+ }
+ }
+ return parseGMTDefault(text, pos);
+}
+
+void
+SimpleDateFormat::formatGMTDefault(UnicodeString &appendTo, int32_t offset) const {
+ if (offset < 0) {
appendTo += gGmtMinus;
- value = -value; // suppress the '-' sign for text display.
+ offset = -offset; // suppress the '-' sign for text display.
}else{
appendTo += gGmtPlus;
}
- zeroPaddingNumber(appendTo, (int32_t)(value/U_MILLIS_PER_HOUR), 2, 2);
+ offset /= U_MILLIS_PER_SECOND; // now in seconds
+ int32_t sec = offset % 60;
+ offset /= 60;
+ int32_t min = offset % 60;
+ int32_t hour = offset / 60;
+
+
+ zeroPaddingNumber(appendTo, hour, 2, 2);
appendTo += (UChar)0x003A /*':'*/;
- zeroPaddingNumber(appendTo, (int32_t)((value%U_MILLIS_PER_HOUR)/U_MILLIS_PER_MINUTE), 2, 2);
+ zeroPaddingNumber(appendTo, min, 2, 2);
+ if (sec != 0) {
+ appendTo += (UChar)0x003A /*':'*/;
+ zeroPaddingNumber(appendTo, sec, 2, 2);
+ }
+}
+
+int32_t
+SimpleDateFormat::parseGMTDefault(const UnicodeString &text, ParsePosition &pos) const {
+ int32_t start = pos.getIndex();
+
+ if (start + kGmtLen + 1 >= text.length()) {
+ pos.setErrorIndex(start);
+ return 0;
+ }
+
+ int32_t cur = start;
+ // "GMT"
+ if (text.compare(start, kGmtLen, gGmt) != 0) {
+ pos.setErrorIndex(start);
+ return 0;
+ }
+ cur += kGmtLen;
+ // Sign
+ UBool negative = FALSE;
+ if (text.charAt(cur) == (UChar)0x002D /* minus */) {
+ negative = TRUE;
+ } else if (text.charAt(cur) != (UChar)0x002B /* plus */) {
+ pos.setErrorIndex(cur);
+ return 0;
+ }
+ cur++;
+
+ // Numbers
+ int32_t numLen;
+ pos.setIndex(cur);
+
+ Formattable number;
+ parseInt(text, number, 6, pos, FALSE);
+ numLen = pos.getIndex() - cur;
+
+ if (numLen <= 0) {
+ pos.setIndex(start);
+ pos.setErrorIndex(cur);
+ return 0;
+ }
+
+ int32_t numVal = number.getLong();
+
+ int32_t hour = 0;
+ int32_t min = 0;
+ int32_t sec = 0;
+
+ if (numLen <= 2) {
+ // H[H][:mm[:ss]]
+ hour = numVal;
+ cur += numLen;
+ if (cur + 2 < text.length() && text.charAt(cur) == (UChar)0x003A /* colon */) {
+ cur++;
+ pos.setIndex(cur);
+ parseInt(text, number, 2, pos, FALSE);
+ numLen = pos.getIndex() - cur;
+ if (numLen == 2) {
+ // got minute field
+ min = number.getLong();
+ cur += numLen;
+ if (cur + 2 < text.length() && text.charAt(cur) == (UChar)0x003A /* colon */) {
+ cur++;
+ pos.setIndex(cur);
+ parseInt(text, number, 2, pos, FALSE);
+ numLen = pos.getIndex() - cur;
+ if (numLen == 2) {
+ // got second field
+ sec = number.getLong();
+ } else {
+ // reset position
+ pos.setIndex(cur - 1);
+ pos.setErrorIndex(-1);
+ }
+ }
+ } else {
+ // reset postion
+ pos.setIndex(cur - 1);
+ pos.setErrorIndex(-1);
+ }
+ }
+ } else if (numLen == 3 || numLen == 4) {
+ // Hmm or HHmm
+ hour = numVal / 100;
+ min = numVal % 100;
+ } else if (numLen == 5 || numLen == 6) {
+ // Hmmss or HHmmss
+ hour = numVal / 10000;
+ min = (numVal % 10000) / 100;
+ sec = numVal % 100;
+ } else {
+ // HHmmss followed by bogus numbers
+ pos.setIndex(cur + 6);
+
+ int32_t shift = numLen - 6;
+ while (shift > 0) {
+ numVal /= 10;
+ shift--;
+ }
+ hour = numVal / 10000;
+ min = (numVal % 10000) / 100;
+ sec = numVal % 100;
+ }
+
+ int32_t offset = ((hour*60 + min)*60 + sec)*1000;
+ if (negative) {
+ offset = -offset;
+ }
+ return offset;
+}
+
+UBool
+SimpleDateFormat::isDefaultGMTFormat() const {
+ // GMT pattern
+ if (fSymbols->fGmtFormat.length() == 0) {
+ // No GMT pattern is set
+ return TRUE;
+ } else if (fSymbols->fGmtFormat.compare(gDefGmtPat, kGmtPatLen) != 0) {
+ return FALSE;
+ }
+ // Hour patterns
+ if (fSymbols->fGmtHourFormats == NULL || fSymbols->fGmtHourFormatsCount != DateFormatSymbols::GMT_HOUR_COUNT) {
+ // No Hour pattern is set
+ return TRUE;
+ } else if ((fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_NEGATIVE_HMS].compare(gDefGmtNegHmsPat, kNegHmsLen) != 0)
+ || (fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_NEGATIVE_HM].compare(gDefGmtNegHmPat, kNegHmLen) != 0)
+ || (fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_POSITIVE_HMS].compare(gDefGmtPosHmsPat, kPosHmsLen) != 0)
+ || (fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_POSITIVE_HM].compare(gDefGmtPosHmPat, kPosHmLen) != 0)) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void
+SimpleDateFormat::formatRFC822TZ(UnicodeString &appendTo, int32_t offset) const {
+ UChar sign = 0x002B /* '+' */;
+ if (offset < 0) {
+ offset = -offset;
+ sign = 0x002D /* '-' */;
+ }
+ appendTo.append(sign);
+
+ int32_t offsetH = offset / U_MILLIS_PER_HOUR;
+ offset = offset % U_MILLIS_PER_HOUR;
+ int32_t offsetM = offset / U_MILLIS_PER_MINUTE;
+ offset = offset % U_MILLIS_PER_MINUTE;
+ int32_t offsetS = offset / U_MILLIS_PER_SECOND;
+
+ int32_t num = 0, denom = 0;
+ if (offsetS == 0) {
+ offset = offsetH*100 + offsetM; // HHmm
+ num = offset % 10000;
+ denom = 1000;
+ } else {
+ offset = offsetH*10000 + offsetM*100 + offsetS; // HHmmss
+ num = offset % 1000000;
+ denom = 100000;
+ }
+ while (denom >= 1) {
+ UChar digit = (UChar)0x0030 + (num / denom);
+ appendTo.append(digit);
+ num = num % denom;
+ denom /= 10;
+ }
+}
+
+void
+SimpleDateFormat::initGMTFormatters(UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ umtx_lock(&LOCK);
+ if (fGMTFormatters == NULL) {
+ fGMTFormatters = (MessageFormat**)uprv_malloc(kNumGMTFormatters * sizeof(MessageFormat*));
+ if (fGMTFormatters) {
+ for (int32_t i = 0; i < kNumGMTFormatters; i++) {
+ const UnicodeString *hourPattern;
+ switch (i) {
+ case kGMTNegativeHMS:
+ hourPattern = &(fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_NEGATIVE_HMS]);
+ break;
+ case kGMTNegativeHM:
+ hourPattern = &(fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_NEGATIVE_HM]);
+ break;
+ case kGMTPositiveHMS:
+ hourPattern = &(fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_POSITIVE_HMS]);
+ break;
+ case kGMTPositiveHM:
+ hourPattern = &(fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_POSITIVE_HM]);
+ break;
+ }
+ fGMTFormatters[i] = new MessageFormat(fSymbols->fGmtFormat, status);
+ if (U_FAILURE(status)) {
+ break;
+ }
+ SimpleDateFormat *sdf = (SimpleDateFormat*)this->clone();
+ sdf->adoptTimeZone(TimeZone::createTimeZone(UnicodeString(gEtcUTC)));
+ sdf->applyPattern(*hourPattern);
+ fGMTFormatters[i]->adoptFormat(0, sdf);
+ }
+ } else {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ }
+ }
+ umtx_unlock(&LOCK);
}
//---------------------------------------------------------------------
@@ -775,145 +1125,57 @@
// then the time zone shows up as "GMT+hh:mm" or "GMT-hh:mm" (where "hh:mm" is the
// offset from GMT) regardless of how many z's were in the pattern symbol
case UDAT_TIMEZONE_FIELD:
+ case UDAT_TIMEZONE_GENERIC_FIELD:
case UDAT_TIMEZONE_SPECIAL_FIELD:
- case UDAT_TIMEZONE_GENERIC_FIELD: {
- UnicodeString str;
- UnicodeString zid;
- UnicodeString mzid;
- UnicodeString displayString;
- zid = fSymbols->getZoneID(cal.getTimeZone().getID(str), zid, status);
- if(U_FAILURE(status)){
- break;
- }
- if (zid.length() == 0) {
- appendGMT(appendTo, cal, status);
- }
- else {
- zoneIDCanonicalize(zid);
- if (patternCharIndex == UDAT_TIMEZONE_GENERIC_FIELD) {
- if(count < 4){
- if (cal.getTimeZone().useDaylightTime()) {
- fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_GENERIC, displayString, status);
- if ( !fSymbols->isCommonlyUsed(zid)) {
- displayString.remove();
- }
- if(displayString.length()==0) {
- mzid = fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_GENERIC, cal, displayString, status);
- if ( !fSymbols->isCommonlyUsed(mzid)) {
- displayString.remove();
- }
- }
- }
- else {
- fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_STANDARD, displayString, status);
- if ( !fSymbols->isCommonlyUsed(zid)) {
- displayString.remove();
- }
- if(displayString.length()==0) {
- mzid = fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_STANDARD, cal, displayString, status);
- if ( !fSymbols->isCommonlyUsed(mzid)) {
- displayString.remove();
- }
- }
- }
- if(displayString.length()==0) {
- fSymbols->getFallbackString(zid, displayString, status);
- }
- }else{
- if (cal.getTimeZone().useDaylightTime()) {
- fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_LONG_GENERIC, displayString, status);
- if(displayString.length()==0) {
- mzid = fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_LONG_GENERIC, cal, displayString, status);
- }
- }
- else {
- fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_LONG_STANDARD, displayString, status);
- if(displayString.length()==0) {
- mzid = fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_LONG_STANDARD, cal, displayString, status);
- }
- }
-
- if(displayString.length()==0) {
- fSymbols->getFallbackString(zid, displayString, status);
- }
- }
- }
- else if (patternCharIndex == UDAT_TIMEZONE_SPECIAL_FIELD) {
- if(count == 4){ // VVVV format - always get fallback string.
- fSymbols->getFallbackString(zid, displayString, status);
- }
- else if (count == 1){ // V format - ignore commonlyUsed
- if (cal.get(UCAL_DST_OFFSET, status) != 0) {
- fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_DAYLIGHT, displayString, status);
- if(displayString.length()==0) {
- mzid = fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_DAYLIGHT, cal, displayString, status);
- }
- }
- else {
- fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_STANDARD, displayString, status);
- if(displayString.length()==0) {
- mzid = fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_STANDARD, cal, displayString, status);
- }
- }
- }
- } else {
- if (cal.get(UCAL_DST_OFFSET, status) != 0) {
- if(count < 4){
- fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_DAYLIGHT, displayString, status);
- if ( fSymbols->isCommonlyUsed(zid) == FALSE ) {
- displayString.remove();
- }
- if(displayString.length()==0) {
- mzid = fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_DAYLIGHT, cal, displayString, status);
- if ( fSymbols->isCommonlyUsed(mzid) == FALSE ) {
- displayString.remove();
- }
- }
- }else{
- fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_LONG_DAYLIGHT, displayString, status);
- if(displayString.length()==0)
- fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_LONG_DAYLIGHT, cal, displayString, status);
- }
- }else{
- if(count < 4){
- fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_STANDARD, displayString, status);
- if ( !fSymbols->isCommonlyUsed(zid)) {
- displayString.remove();
- }
- if(displayString.length()==0) {
- mzid = fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_STANDARD, cal, displayString, status);
- if ( fSymbols->isCommonlyUsed(mzid) == FALSE ) {
- displayString.remove();
- }
- }
- }else{
- fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_LONG_STANDARD, displayString, status);
- if(displayString.length()==0)
- fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_LONG_STANDARD, cal, displayString, status);
- }
- }
- }
- if(displayString.length()==0){
- appendGMT(appendTo, cal, status);
- }else{
- appendTo += displayString;
- }
- }
- }
- break;
-
- case 23: // 'Z' - TIMEZONE_RFC
{
- UChar sign = 43/*'+'*/;
- value = (cal.get(UCAL_ZONE_OFFSET, status) +
- cal.get(UCAL_DST_OFFSET, status)) / U_MILLIS_PER_MINUTE;
- if (value < 0) {
- value = -value;
- sign = 45/*'-'*/;
+ UnicodeString zoneString;
+ const ZoneStringFormat *zsf = fSymbols->getZoneStringFormat();
+ if (zsf) {
+ if (patternCharIndex == UDAT_TIMEZONE_FIELD) {
+ if (count < 4) {
+ // "z", "zz", "zzz"
+ zsf->getSpecificShortString(cal, TRUE /*commonly used only*/,
+ zoneString, status);
+ } else {
+ // "zzzz"
+ zsf->getSpecificLongString(cal, zoneString, status);
+ }
+ } else if (patternCharIndex == UDAT_TIMEZONE_GENERIC_FIELD) {
+ if (count == 1) {
+ // "v"
+ zsf->getGenericShortString(cal, TRUE /*commonly used only*/,
+ zoneString, status);
+ } else if (count == 4) {
+ // "vvvv"
+ zsf->getGenericLongString(cal, zoneString, status);
+ }
+ } else { // patternCharIndex == UDAT_TIMEZONE_SPECIAL_FIELD
+ if (count == 1) {
+ // "V"
+ zsf->getSpecificShortString(cal, FALSE /*ignore commonly used*/,
+ zoneString, status);
+ } else if (count == 4) {
+ // "VVVV"
+ zsf->getGenericLocationString(cal, zoneString, status);
+ }
+ }
}
- value = (value / 60) * 100 + (value % 60); // minutes => KKmm
- appendTo += sign;
- zeroPaddingNumber(appendTo, value, 4, 4);
+ if (zoneString.isEmpty()) {
+ appendGMT(appendTo, cal, status);
+ } else {
+ appendTo += zoneString;
+ }
+ }
+ break;
+
+ case UDAT_TIMEZONE_RFC_FIELD: // 'Z' - TIMEZONE_RFC
+ if (count < 4) {
+ // RFC822 format, must use ASCII digits
+ value = (cal.get(UCAL_ZONE_OFFSET, status) + cal.get(UCAL_DST_OFFSET, status));
+ formatRFC822TZ(appendTo, value);
+ } else {
+ // long form, localized GMT pattern
+ appendGMT(appendTo, cal, status);
}
break;
@@ -955,88 +1217,8 @@
pos.setEndIndex(appendTo.length());
}
}
+
//----------------------------------------------------------------------
-
-void
-SimpleDateFormat::zoneIDCanonicalize(UnicodeString &zid) const
-{
- UnicodeString colon = UNICODE_STRING_SIMPLE(":");
- UnicodeString solidus = UNICODE_STRING_SIMPLE("/");
- char zidkey[ZID_KEY_MAX];
- int32_t len;
-
- UErrorCode status = U_ZERO_ERROR;
- UResourceBundle* supplementalDataBundle = ures_openDirect(NULL, kSUPPLEMENTAL, &status);
- if (U_FAILURE(status)) {
- return;
- }
-
- UResourceBundle* zoneFormatting = ures_getByKey(supplementalDataBundle, gZoneFormattingTag, NULL, &status);
-
- // First try to lookup the zone itself, since most of the time the canonical ID and the ID itself
- // will be the same. This should save us a significant amount of time.
-
- len = zid.length();
- len = (len >= (ZID_KEY_MAX-1) ? ZID_KEY_MAX-1 : len);
- u_UCharsToChars(zid.getBuffer(), zidkey, len);
- zidkey[len] = 0; // NULL terminate
-
- // Replace / with : for zid
- len = (int32_t)uprv_strlen(zidkey);
- for (int i = 0; i < len; i++) {
- if (zidkey[i] == '/') {
- zidkey[i] = ':';
- }
- }
-
- UResourceBundle* tryThisZone = ures_getByKey(zoneFormatting,zidkey,NULL,&status);
- if (U_SUCCESS(status)) {
- ures_close(tryThisZone);
- ures_close(zoneFormatting);
- ures_close(supplementalDataBundle);
- return;
- }
- else {
- status = U_ZERO_ERROR;
- }
-
- // Didn't find it, so go searching for an alias
-
- while ( ures_hasNext(zoneFormatting)) {
- UResourceBundle *currentZone = ures_getNextResource(zoneFormatting,NULL,&status);
- if (U_FAILURE(status)) {
- break;
- }
-
- const char *currentZoneString= ures_getKey(currentZone);
-
- UResourceBundle *zoneAliases = ures_getByKey(currentZone,gAliases,NULL, &status);
- if (U_FAILURE(status)) {
- status = U_ZERO_ERROR;
- ures_close(currentZone);
- continue;
- }
- while ( ures_hasNext(zoneAliases)) {
- int32_t len;
- const UChar* alias = ures_getNextString(zoneAliases,&len,NULL,&status);
- if ( zid.compare(alias)==0 ) {
- zid.setTo(UnicodeString(currentZoneString, -1, US_INV));
- zid.findAndReplace(colon,solidus);
- ures_close(zoneAliases);
- ures_close(currentZone);
- ures_close(zoneFormatting);
- ures_close(supplementalDataBundle);
- return;
- }
- }
- ures_close(zoneAliases);
- ures_close(currentZone);
- }
- ures_close(zoneFormatting);
- ures_close(supplementalDataBundle);
-}
-//----------------------------------------------------------------------
-
void
SimpleDateFormat::zeroPaddingNumber(UnicodeString &appendTo, int32_t value, int32_t minDigits, int32_t maxDigits) const
{
@@ -1073,9 +1255,8 @@
UBool ambiguousYear[] = { FALSE };
int32_t count = 0;
- // hack, clear parsedTimeZone, cast away const
- delete parsedTimeZone;
- ((SimpleDateFormat*)this)->parsedTimeZone = NULL;
+ // hack, reset tztype, cast away const
+ ((SimpleDateFormat*)this)->tztype = TZTYPE_UNK;
// For parsing abutting numeric fields. 'abutPat' is the
// offset into 'pattern' of the first of 2 or more abutting
@@ -1271,39 +1452,127 @@
// front or the back of the default century. This only works because we adjust
// the year correctly to start with in other cases -- see subParse().
UErrorCode status = U_ZERO_ERROR;
- if (ambiguousYear[0] || parsedTimeZone != NULL) // If this is true then the two-digit year == the default start year
+ if (ambiguousYear[0] || tztype != TZTYPE_UNK) // If this is true then the two-digit year == the default start year
{
// We need a copy of the fields, and we need to avoid triggering a call to
// complete(), which will recalculate the fields. Since we can't access
// the fields[] array in Calendar, we clone the entire object. This will
// stop working if Calendar.clone() is ever rewritten to call complete().
- Calendar *copy = cal.clone();
+ Calendar *copy;
if (ambiguousYear[0]) {
+ copy = cal.clone();
UDate parsedDate = copy->getTime(status);
// {sfb} check internalGetDefaultCenturyStart
if (fHaveDefaultCentury && (parsedDate < fDefaultCenturyStart)) {
// We can't use add here because that does a complete() first.
cal.set(UCAL_YEAR, fDefaultCenturyStartYear + 100);
}
+ delete copy;
}
- if (parsedTimeZone != NULL) {
- TimeZone *tz = parsedTimeZone;
+ if (tztype != TZTYPE_UNK) {
+ copy = cal.clone();
+ const TimeZone & tz = cal.getTimeZone();
+ BasicTimeZone *btz = NULL;
- // the calendar represents the parse as gmt time
- // we need to turn this into local time, so we add the raw offset
- // then we ask the timezone to handle this local time
- int32_t rawOffset = 0;
- int32_t dstOffset = 0;
- tz->getOffset(copy->getTime(status)+tz->getRawOffset(), TRUE,
- rawOffset, dstOffset, status);
- if (U_SUCCESS(status)) {
- cal.set(UCAL_ZONE_OFFSET, rawOffset);
- cal.set(UCAL_DST_OFFSET, dstOffset);
+ if (tz.getDynamicClassID() == OlsonTimeZone::getStaticClassID()
+ || tz.getDynamicClassID() == SimpleTimeZone::getStaticClassID()
+ || tz.getDynamicClassID() == RuleBasedTimeZone::getStaticClassID()
+ || tz.getDynamicClassID() == VTimeZone::getStaticClassID()) {
+ btz = (BasicTimeZone*)&tz;
}
- }
- delete copy;
+ // Get local millis
+ copy->set(UCAL_ZONE_OFFSET, 0);
+ copy->set(UCAL_DST_OFFSET, 0);
+ UDate localMillis = copy->getTime(status);
+
+ // Make sure parsed time zone type (Standard or Daylight)
+ // matches the rule used by the parsed time zone.
+ int32_t raw, dst;
+ if (btz != NULL) {
+ if (tztype == TZTYPE_STD) {
+ btz->getOffsetFromLocal(localMillis,
+ BasicTimeZone::kStandard, BasicTimeZone::kStandard, raw, dst, status);
+ } else {
+ btz->getOffsetFromLocal(localMillis,
+ BasicTimeZone::kDaylight, BasicTimeZone::kDaylight, raw, dst, status);
+ }
+ } else {
+ // No good way to resolve ambiguous time at transition,
+ // but following code work in most case.
+ tz.getOffset(localMillis, TRUE, raw, dst, status);
+ }
+
+ // Now, compare the results with parsed type, either standard or daylight saving time
+ int32_t resolvedSavings = dst;
+ if (tztype == TZTYPE_STD) {
+ if (dst != 0) {
+ // Override DST_OFFSET = 0 in the result calendar
+ resolvedSavings = 0;
+ }
+ } else { // tztype == TZTYPE_DST
+ if (dst == 0) {
+ if (btz != NULL) {
+ UDate time = localMillis + raw;
+ // We use the nearest daylight saving time rule.
+ TimeZoneTransition beforeTrs, afterTrs;
+ UDate beforeT = time, afterT = time;
+ int32_t beforeSav = 0, afterSav = 0;
+ UBool beforeTrsAvail, afterTrsAvail;
+
+ // Search for DST rule before or on the time
+ while (TRUE) {
+ beforeTrsAvail = btz->getPreviousTransition(beforeT, TRUE, beforeTrs);
+ if (!beforeTrsAvail) {
+ break;
+ }
+ beforeT = beforeTrs.getTime() - 1;
+ beforeSav = beforeTrs.getFrom()->getDSTSavings();
+ if (beforeSav != 0) {
+ break;
+ }
+ }
+
+ // Search for DST rule after the time
+ while (TRUE) {
+ afterTrsAvail = btz->getNextTransition(afterT, FALSE, afterTrs);
+ if (!afterTrsAvail) {
+ break;
+ }
+ afterT = afterTrs.getTime();
+ afterSav = afterTrs.getTo()->getDSTSavings();
+ if (afterSav != 0) {
+ break;
+ }
+ }
+
+ if (beforeTrsAvail && afterTrsAvail) {
+ if (time - beforeT > afterT - time) {
+ resolvedSavings = afterSav;
+ } else {
+ resolvedSavings = beforeSav;
+ }
+ } else if (beforeTrsAvail && beforeSav != 0) {
+ resolvedSavings = beforeSav;
+ } else if (afterTrsAvail && afterSav != 0) {
+ resolvedSavings = afterSav;
+ } else {
+ resolvedSavings = btz->getDSTSavings();
+ }
+ } else {
+ resolvedSavings = tz.getDSTSavings();
+ }
+ if (resolvedSavings == 0) {
+ // final fallback
+ resolvedSavings = U_MILLIS_PER_HOUR;
+ }
+ }
+ }
+ cal.set(UCAL_ZONE_OFFSET, raw);
+ cal.set(UCAL_DST_OFFSET, resolvedSavings);
+ delete copy;
+ }
}
// If any Calendar calls failed, we pretend that we
@@ -1787,124 +2056,149 @@
case UDAT_TIMEZONE_GENERIC_FIELD:
case UDAT_TIMEZONE_SPECIAL_FIELD:
{
- // First try to parse generic forms such as GMT-07:00. Do this first
- // in case localized DateFormatZoneData contains the string "GMT"
- // for a zone; in that case, we don't want to match the first three
- // characters of GMT+/-HH:MM etc.
+ int32_t offset = 0;
+ UBool parsed = FALSE;
- int32_t sign = 0;
- int32_t offset;
- int32_t gmtLen = u_strlen(gGmt);
+ // Step 1
+ // Check if this is a long GMT offset string (either localized or default)
+ offset = parseGMT(text, pos);
+ if (pos.getIndex() - start > 0) {
+ parsed = TRUE;
+ }
+ if (!parsed) {
+ // Step 2
+ // Check if this is an RFC822 time zone offset.
+ // ICU supports the standard RFC822 format [+|-]HHmm
+ // and its extended form [+|-]HHmmSS.
+ do {
+ int32_t sign = 0;
+ UChar signChar = text.charAt(start);
+ if (signChar == (UChar)0x002B /* '+' */) {
+ sign = 1;
+ } else if (signChar == (UChar)0x002D /* '-' */) {
+ sign = -1;
+ } else {
+ // Not an RFC822 offset string
+ break;
+ }
- // For time zones that have no known names, look for strings
- // of the form:
- // GMT[+-]hours:minutes or
- // GMT[+-]hhmm or
- // GMT.
-
- if ((text.length() - start) >= gmtLen &&
- (text.caseCompare(start, gmtLen, gGmt, 0, gmtLen, U_FOLD_CASE_DEFAULT)) == 0)
- {
- cal.set(UCAL_DST_OFFSET, 0);
+ // Parse digits
+ int32_t orgPos = start + 1;
+ pos.setIndex(orgPos);
+ parseInt(text, number, 6, pos, FALSE);
+ int32_t numLen = pos.getIndex() - orgPos;
+ if (numLen <= 0) {
+ break;
+ }
- pos.setIndex(start + gmtLen);
+ // Followings are possible format (excluding sign char)
+ // HHmmSS
+ // HmmSS
+ // HHmm
+ // Hmm
+ // HH
+ // H
+ int32_t val = number.getLong();
+ int32_t hour = 0, min = 0, sec = 0;
+ switch(numLen) {
+ case 1: // H
+ case 2: // HH
+ hour = val;
+ break;
+ case 3: // Hmm
+ case 4: // HHmm
+ hour = val / 100;
+ min = val % 100;
+ break;
+ case 5: // Hmmss
+ case 6: // HHmmss
+ hour = val / 10000;
+ min = (val % 10000) / 100;
+ sec = val % 100;
+ break;
+ }
+ if (hour > 23 || min > 59 || sec > 59) {
+ // Invalid value range
+ break;
+ }
+ offset = (((hour * 60) + min) * 60 + sec) * 1000 * sign;
+ parsed = TRUE;
+ } while (FALSE);
- if( text[pos.getIndex()] == 0x002B /*'+'*/ )
- sign = 1;
- else if( text[pos.getIndex()] == 0x002D /*'-'*/ )
- sign = -1;
- else {
- cal.set(UCAL_ZONE_OFFSET, 0 );
- return pos.getIndex();
+ if (!parsed) {
+ // Failed to parse. Reset the position.
+ pos.setIndex(start);
+ }
}
- // Look for hours:minutes or hhmm.
- pos.setIndex(pos.getIndex() + 1);
- int32_t parseStart = pos.getIndex();
- Formattable tzNumber;
- fNumberFormat->parse(text, tzNumber, pos);
- if( pos.getIndex() == parseStart) {
- return -start;
- }
- if( text[pos.getIndex()] == 0x003A /*':'*/ ) {
- // This is the hours:minutes case
- offset = tzNumber.getLong() * 60;
- pos.setIndex(pos.getIndex() + 1);
- parseStart = pos.getIndex();
- fNumberFormat->parse(text, tzNumber, pos);
- if( pos.getIndex() == parseStart) {
- return -start;
- }
- offset += tzNumber.getLong();
- }
- else {
- // This is the hhmm case.
- offset = tzNumber.getLong();
- if( offset < 24 )
- offset *= 60;
- else
- offset = offset % 100 + offset / 100 * 60;
+ if (parsed) {
+ // offset was successfully parsed as either a long GMT string or RFC822 zone offset
+ // string. Create normalized zone ID for the offset.
+
+ UnicodeString tzID(gGmt);
+ formatRFC822TZ(tzID, offset);
+ //TimeZone *customTZ = TimeZone::createTimeZone(tzID);
+ TimeZone *customTZ = new SimpleTimeZone(offset, tzID); // faster than TimeZone::createTimeZone
+ cal.adoptTimeZone(customTZ);
+
+ return pos.getIndex();
}
- // Fall through for final processing below of 'offset' and 'sign'.
- }
- else {
+ // Step 3
// At this point, check for named time zones by looking through
// the locale data from the DateFormatZoneData strings.
// Want to be able to parse both short and long forms.
- // !!! side effect, might set parsedZoneString
- UErrorCode status = U_ZERO_ERROR;
- int32_t result = subParseZoneString(text, start, cal, status);
- if (result != 0) {
- return result;
+ // optimize for calendar's current time zone
+ const ZoneStringFormat *zsf = fSymbols->getZoneStringFormat();
+ if (zsf) {
+ UErrorCode status = U_ZERO_ERROR;
+ const ZoneStringInfo *zsinfo = NULL;
+ int32_t matchLen;
+
+ switch (patternCharIndex) {
+ case UDAT_TIMEZONE_FIELD: // 'z'
+ if (count < 4) {
+ zsinfo = zsf->findSpecificShort(text, start, matchLen, status);
+ } else {
+ zsinfo = zsf->findSpecificLong(text, start, matchLen, status);
+ }
+ break;
+ case UDAT_TIMEZONE_GENERIC_FIELD: // 'v'
+ if (count == 1) {
+ zsinfo = zsf->findGenericShort(text, start, matchLen, status);
+ } else if (count == 4) {
+ zsinfo = zsf->findGenericLong(text, start, matchLen, status);
+ }
+ break;
+ case UDAT_TIMEZONE_SPECIAL_FIELD: // 'V'
+ if (count == 1) {
+ zsinfo = zsf->findSpecificShort(text, start, matchLen, status);
+ } else if (count == 4) {
+ zsinfo = zsf->findGenericLocation(text, start, matchLen, status);
+ }
+ break;
+ }
+
+ if (U_SUCCESS(status) && zsinfo != NULL) {
+ if (zsinfo->isStandard()) {
+ ((SimpleDateFormat*)this)->tztype = TZTYPE_STD;
+ } else if (zsinfo->isDaylight()) {
+ ((SimpleDateFormat*)this)->tztype = TZTYPE_DST;
+ }
+ UnicodeString tzid;
+ zsinfo->getID(tzid);
+
+ UnicodeString current;
+ cal.getTimeZone().getID(current);
+ if (tzid != current) {
+ TimeZone *tz = TimeZone::createTimeZone(tzid);
+ cal.adoptTimeZone(tz);
+ }
+ return start + matchLen;
+ }
}
-
- // As a last resort, look for numeric timezones of the form
- // [+-]hhmm as specified by RFC 822. This code is actually
- // a little more permissive than RFC 822. It will try to do
- // its best with numbers that aren't strictly 4 digits long
- DecimalFormat fmt(UNICODE_STRING_SIMPLE("+####;-####"), status);
- if(U_FAILURE(status))
- return -start;
- fmt.setParseIntegerOnly(TRUE);
- int32_t parseStart = pos.getIndex();
- Formattable tzNumber;
- fmt.parse( text, tzNumber, pos );
- if( pos.getIndex() == parseStart) {
- return -start; // Wasn't actually a number.
- }
- offset = tzNumber.getLong();
- sign = 1;
- if( offset < 0 ) {
- sign = -1;
- offset = -offset;
- }
- if( offset < 24 )
- offset = offset * 60;
- else
- offset = offset % 100 + offset / 100 * 60;
-
- // Fall through for final processing below of 'offset' and 'sign'.
- }
-
- // Do the final processing for both of the above cases. We only
- // arrive here if the form GMT+/-... or an RFC 822 form was seen.
- if (sign != 0)
- {
- offset *= U_MILLIS_PER_MINUTE * sign;
-
- if (cal.getTimeZone().useDaylightTime())
- {
- cal.set(UCAL_DST_OFFSET, U_MILLIS_PER_HOUR);
- offset -= U_MILLIS_PER_HOUR;
- }
- cal.set(UCAL_ZONE_OFFSET, offset);
-
- return pos.getIndex();
- }
-
- // All efforts to parse a zone failed.
- return -start;
+ // complete failure
+ return -start;
}
default:
@@ -1929,61 +2223,6 @@
}
}
-int32_t
-SimpleDateFormat::subParseZoneString(const UnicodeString& text, int32_t start, Calendar& cal, UErrorCode& status) const
-{
- // At this point, check for named time zones by looking through
- // the locale data from the DateFormatZoneData strings.
- // Want to be able to parse both short and long forms.
-
- // optimize for calendar's current time zone
- TimeZone *tz = NULL;
- UnicodeString id;
- UnicodeString zid, value;
- DateFormatSymbols::TimeZoneTranslationType type = DateFormatSymbols::TIMEZONE_COUNT;
- fSymbols->getZoneID(getTimeZone().getID(id), zid, status);
- if(zid.length() > 0){
- fSymbols->findZoneIDTypeValue(zid, text, start, type, value, status);
- if(type != DateFormatSymbols::TIMEZONE_COUNT) {
- tz = TimeZone::createTimeZone(zid);
- }
- }
-
- if(U_FAILURE(status)){
- return 0;
- }
- if (tz != NULL) { // Matched any ?
- // always set zone offset, needed to get correct hour in wall time
- // when checking daylight savings
- cal.set(UCAL_ZONE_OFFSET, tz->getRawOffset());
- if (type==DateFormatSymbols::TIMEZONE_SHORT_STANDARD || type==DateFormatSymbols::TIMEZONE_LONG_STANDARD ) {
- // standard time
- cal.set(UCAL_DST_OFFSET, 0);
- delete tz; tz = NULL;
- } else if (type==DateFormatSymbols::TIMEZONE_SHORT_DAYLIGHT || type==DateFormatSymbols::TIMEZONE_LONG_DAYLIGHT ) {
- // daylight time
- // !!! todo - no getDSTSavings() in ICU's timezone
- // use the correct DST SAVINGS for the zone.
- // cal.set(UCAL_DST_OFFSET, tz->getDSTSavings());
- cal.set(UCAL_DST_OFFSET, U_MILLIS_PER_HOUR);
- delete tz; tz = NULL;
- } else {
- // either standard or daylight
- // need to finish getting the date, then compute dst offset as appropriate
-
- // !!! hack for api compatibility, can't modify subParse(...) so can't
- // pass this back any other way. cast away const.
- ((SimpleDateFormat*)this)->parsedTimeZone = tz;
- }
-
- return start + value.length();
- }
-
-
- // complete failure
- return 0;
-}
-
/**
* Parse an integer using fNumberFormat. This method is semantically
* const, but actually may modify fNumberFormat.
@@ -1992,6 +2231,17 @@
Formattable& number,
ParsePosition& pos,
UBool allowNegative) const {
+ parseInt(text, number, -1, pos, allowNegative);
+}
+
+/**
+ * Parse an integer using fNumberFormat up to maxDigits.
+ */
+void SimpleDateFormat::parseInt(const UnicodeString& text,
+ Formattable& number,
+ int32_t maxDigits,
+ ParsePosition& pos,
+ UBool allowNegative) const {
UnicodeString oldPrefix;
DecimalFormat* df = NULL;
if (!allowNegative &&
@@ -2000,10 +2250,27 @@
df->getNegativePrefix(oldPrefix);
df->setNegativePrefix(SUPPRESS_NEGATIVE_PREFIX);
}
+ int32_t oldPos = pos.getIndex();
fNumberFormat->parse(text, number, pos);
if (df != NULL) {
df->setNegativePrefix(oldPrefix);
}
+
+ if (maxDigits > 0) {
+ // adjust the result to fit into
+ // the maxDigits and move the position back
+ int32_t nDigits = pos.getIndex() - oldPos;
+ if (nDigits > maxDigits) {
+ int32_t val = number.getLong();
+ nDigits -= maxDigits;
+ while (nDigits > 0) {
+ val /= 10;
+ nDigits--;
+ }
+ pos.setIndex(oldPos + maxDigits);
+ number.setLong(val);
+ }
+ }
}
//----------------------------------------------------------------------
diff --git a/icu4c/source/i18n/timezone.cpp b/icu4c/source/i18n/timezone.cpp
index df4b024..5665611 100644
--- a/icu4c/source/i18n/timezone.cpp
+++ b/icu4c/source/i18n/timezone.cpp
@@ -88,6 +88,7 @@
#define kDEFAULT "Default"
#define kMAX_CUSTOM_HOUR 23
#define kMAX_CUSTOM_MIN 59
+#define kMAX_CUSTOM_SEC 59
#define MINUS 0x002D
#define PLUS 0x002B
#define ZERO_DIGIT 0x0030
@@ -687,36 +688,40 @@
}
rawOffset = getRawOffset();
-
- // Convert to local wall millis if necessary
if (!local) {
date += rawOffset; // now in local standard millis
}
- // When local==FALSE, we might have to recompute. This loop is
- // executed once, unless a recomputation is required; then it is
- // executed twice.
+ // When local == TRUE, date might not be in local standard
+ // millis. getOffset taking 7 parameters used here assume
+ // the given time in day is local standard time.
+ // At STD->DST transition, there is a range of time which
+ // does not exist. When 'date' is in this time range
+ // (and local == TRUE), this method interprets the specified
+ // local time as DST. At DST->STD transition, there is a
+ // range of time which occurs twice. In this case, this
+ // method interprets the specified local time as STD.
+ // To support the behavior above, we need to call getOffset
+ // (with 7 args) twice when local == true and DST is
+ // detected in the initial call.
for (int32_t pass=0; ; ++pass) {
int32_t year, month, dom, dow;
double day = uprv_floor(date / U_MILLIS_PER_DAY);
int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
-
+
Grego::dayToFields(day, year, month, dom, dow);
-
+
dstOffset = getOffset(GregorianCalendar::AD, year, month, dom,
(uint8_t) dow, millis,
Grego::monthLength(year, month),
ec) - rawOffset;
- // Recompute if local==FALSE, dstOffset!=0, and addition of
- // the dstOffset puts us in a different day.
- if (pass!=0 || local || dstOffset==0) {
+ // Recompute if local==TRUE, dstOffset!=0.
+ if (pass!=0 || !local || dstOffset == 0) {
break;
}
- date += dstOffset;
- if (uprv_floor(date / U_MILLIS_PER_DAY) == day) {
- break;
- }
+ // adjust to local standard millis
+ date -= dstOffset;
}
}
@@ -1093,6 +1098,33 @@
// ---------------------------------------
+UnicodeString&
+TimeZone::getOlsonCanonicalID(const UnicodeString &id, UnicodeString &canonical) {
+ UErrorCode ec = U_ZERO_ERROR;
+ canonical.remove();
+ UResourceBundle *top = ures_openDirect(0, kZONEINFO, &ec);
+ UResourceBundle *res = getZoneByName(top, id, NULL, ec);
+ if (U_SUCCESS(ec)) {
+ if (ures_getSize(res) == 1) {
+ int32_t deref = ures_getInt(res, &ec);
+ UResourceBundle *nres = ures_getByKey(top, kNAMES, NULL, &ec); // dereference Names section
+ int32_t len;
+ const UChar* tmp = ures_getStringByIndex(nres, deref, &len, &ec);
+ if (U_SUCCESS(ec)) {
+ canonical.setTo(tmp, len);
+ }
+ ures_close(nres);
+ } else {
+ canonical.setTo(id);
+ }
+ }
+ ures_close(res);
+ ures_close(top);
+ return canonical;
+}
+
+// ---------------------------------------
+
UnicodeString&
TimeZone::getDisplayName(UnicodeString& result) const
@@ -1192,11 +1224,12 @@
UBool negative = FALSE;
int32_t hour = 0;
int32_t min = 0;
+ int32_t sec = 0;
if (id[pos.getIndex()] == MINUS /*'-'*/)
negative = TRUE;
else if (id[pos.getIndex()] != PLUS /*'+'*/)
- return 0;
+ return NULL;
pos.setIndex(pos.getIndex() + 1);
UErrorCode success = U_ZERO_ERROR;
@@ -1215,75 +1248,125 @@
numberFormat->parse(id, n, pos);
if (pos.getIndex() == start) {
delete numberFormat;
- return 0;
+ return NULL;
}
hour = n.getLong();
- if (pos.getIndex() < id.length() &&
- id[pos.getIndex()] == 0x003A /*':'*/)
- {
+ if (pos.getIndex() < id.length()) {
+ if (pos.getIndex() - start > 2
+ || id[pos.getIndex()] != 0x003A /*':'*/) {
+ delete numberFormat;
+ return NULL;
+ }
// hh:mm
pos.setIndex(pos.getIndex() + 1);
int32_t oldPos = pos.getIndex();
n.setLong(kParseFailed);
numberFormat->parse(id, n, pos);
- if (pos.getIndex() == oldPos) {
+ if ((pos.getIndex() - oldPos) != 2) {
+ // must be 2 digits
delete numberFormat;
- return 0;
+ return NULL;
}
min = n.getLong();
- }
- else
- {
- // hhmm or hh
+ if (pos.getIndex() < id.length()) {
+ if (id[pos.getIndex()] != 0x003A /*':'*/) {
+ delete numberFormat;
+ return NULL;
+ }
+ // [:ss]
+ pos.setIndex(pos.getIndex() + 1);
+ oldPos = pos.getIndex();
+ n.setLong(kParseFailed);
+ numberFormat->parse(id, n, pos);
+ if (pos.getIndex() != id.length()
+ || (pos.getIndex() - oldPos) != 2) {
+ delete numberFormat;
+ return NULL;
+ }
+ sec = n.getLong();
+ }
+ } else {
+ // Supported formats are below -
+ //
+ // HHmmss
+ // Hmmss
+ // HHmm
+ // Hmm
+ // HH
+ // H
- // Be strict about interpreting something as hh; it must be
- // an offset < 23, and it must be one or two digits. Thus
- // 0010 is interpreted as 00:10, but 10 is interpreted as
- // 10:00.
- if (hour > kMAX_CUSTOM_HOUR || (pos.getIndex() - start) > 2) {
- min = hour % 100;
- hour /= 100;
+ int32_t length = pos.getIndex() - start;
+ if (length <= 0 || 6 < length) {
+ // invalid length
+ delete numberFormat;
+ return NULL;
+ }
+ switch (length) {
+ case 1:
+ case 2:
+ // already set to hour
+ break;
+ case 3:
+ case 4:
+ min = hour % 100;
+ hour /= 100;
+ break;
+ case 5:
+ case 6:
+ sec = hour % 100;
+ min = (hour/100) % 100;
+ hour /= 10000;
+ break;
}
}
delete numberFormat;
- if (hour > kMAX_CUSTOM_HOUR || min > kMAX_CUSTOM_MIN) {
+ if (hour > kMAX_CUSTOM_HOUR || min > kMAX_CUSTOM_MIN || sec > kMAX_CUSTOM_SEC) {
return 0;
}
- // Create time zone ID in RFC822 format - GMT[+|-]hhmm
- UnicodeString tzRFC(GMT_ID);
- if (hour|min) {
+ // Create time zone ID - GMT[+|-]hhmm[ss]
+ UnicodeString tzID(GMT_ID);
+ if (hour | min | sec) {
if (negative) {
- tzRFC += (UChar)MINUS;
+ tzID += (UChar)MINUS;
} else {
- tzRFC += (UChar)PLUS;
+ tzID += (UChar)PLUS;
}
if (hour < 10) {
- tzRFC += (UChar)ZERO_DIGIT;
+ tzID += (UChar)ZERO_DIGIT;
} else {
- tzRFC += (UChar)(ZERO_DIGIT + hour/10);
+ tzID += (UChar)(ZERO_DIGIT + hour/10);
}
- tzRFC += (UChar)(ZERO_DIGIT + hour%10);
+ tzID += (UChar)(ZERO_DIGIT + hour%10);
if (min < 10) {
- tzRFC += (UChar)ZERO_DIGIT;
+ tzID += (UChar)ZERO_DIGIT;
} else {
- tzRFC += (UChar)(ZERO_DIGIT + min/10);
+ tzID += (UChar)(ZERO_DIGIT + min/10);
}
- tzRFC += (UChar)(ZERO_DIGIT + min%10);
+ tzID += (UChar)(ZERO_DIGIT + min%10);
+
+ if (sec) {
+ if (sec < 10) {
+ tzID += (UChar)ZERO_DIGIT;
+ } else {
+ tzID += (UChar)(ZERO_DIGIT + sec/10);
+ }
+ tzID += (UChar)(ZERO_DIGIT + sec%10);
+ }
}
- int32_t offset = (hour * 60 + min) * 60 * 1000;
- if(negative) {
+ int32_t offset = ((hour * 60 + min) * 60 + sec) * 1000;
+ if (negative) {
offset = -offset;
}
- return new SimpleTimeZone(offset, tzRFC);
+ return new SimpleTimeZone(offset, tzID);
}
- return 0;
+ return NULL;
}
diff --git a/icu4c/source/i18n/ucln_in.h b/icu4c/source/i18n/ucln_in.h
index 5fdf4cb..65f3d0a 100644
--- a/icu4c/source/i18n/ucln_in.h
+++ b/icu4c/source/i18n/ucln_in.h
@@ -40,6 +40,8 @@
UCLN_I18N_UCOL,
UCLN_I18N_UCOL_BLD,
UCLN_I18N_CSDET,
+ UCLN_I18N_ZONEMETA,
+ UCLN_I18N_ZSFORMAT,
UCLN_I18N_COUNT /* This must be last */
} ECleanupI18NType;
diff --git a/icu4c/source/i18n/unicode/basictz.h b/icu4c/source/i18n/unicode/basictz.h
index 61e05b6..a49effd 100644
--- a/icu4c/source/i18n/unicode/basictz.h
+++ b/icu4c/source/i18n/unicode/basictz.h
@@ -140,7 +140,36 @@
virtual void getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial,
AnnualTimeZoneRule*& std, AnnualTimeZoneRule*& dst, UErrorCode& status) /*const*/;
+
+ /**
+ * The time type option bit flags used by getOffsetFromLocal
+ * @internal
+ */
+ enum {
+ kStandard = 0x01,
+ kDaylight = 0x03,
+ kFormer = 0x04,
+ kLatter = 0x0C
+ };
+
+ /**
+ * Get time zone offsets from local wall time.
+ * @internal
+ */
+ virtual void getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) /*const*/;
+
protected:
+
+ /**
+ * The time type option bit masks used by getOffsetFromLocal
+ * @internal
+ */
+ enum {
+ kStdDstMask = kDaylight,
+ kFormerLatterMask = kLatter
+ };
+
/**
* Default constructor.
* @draft ICU 3.8
diff --git a/icu4c/source/i18n/unicode/dtfmtsym.h b/icu4c/source/i18n/unicode/dtfmtsym.h
index 5377723..5bc73fe 100644
--- a/icu4c/source/i18n/unicode/dtfmtsym.h
+++ b/icu4c/source/i18n/unicode/dtfmtsym.h
@@ -37,6 +37,8 @@
/* forward declaration */
class SimpleDateFormat;
class Hashtable;
+class ZoneStringFormat;
+class SafeZoneStringFormatPtr;
/**
* DateFormatSymbols is a public class for encapsulating localizable date-time
@@ -445,100 +447,6 @@
*/
static UClassID U_EXPORT2 getStaticClassID();
- /**
- * The translation type of the translated zone strings
- * @internal ICU 3.6
- */
- enum TimeZoneTranslationType {
- TIMEZONE_SHORT_GENERIC,
- TIMEZONE_SHORT_STANDARD,
- TIMEZONE_SHORT_DAYLIGHT,
- TIMEZONE_LONG_GENERIC,
- TIMEZONE_LONG_STANDARD,
- TIMEZONE_LONG_DAYLIGHT,
- TIMEZONE_EXEMPLAR_CITY,
- TIMEZONE_COUNT
- };
-
- /**
- * Creates an enumeration of time zone IDs. The object is owned by the caller and should delete it after use.
- * The time zone IDs are just for programmatic lookup. NOT LOCALIZED!!!
- * @param status Input/output parameter, set to success or
- * failure code upon return.
- * @return A new StringEnumeration object
- * @internal ICU 3.6
- */
- virtual StringEnumeration* createZoneStringIDs(UErrorCode &status);
-
- /**
- * Gets timezone string give the key and translation type
- * @param ID The ID of zone strings, e.g: "America/Los_Angeles".
- * The time zone ID is for programmatic lookup.
- * @param type The translation type requested
- * @param result Output parameter to recieve the translation string
- * @param status Input/output parameter, set to success or
- * failure code upon return.
- * @return the input UnicodeString parameter for chaining
- * @internal ICU 3.8
- */
- UnicodeString& getZoneString(const UnicodeString &ID, const TimeZoneTranslationType type, UnicodeString &result, UErrorCode &status);
-
- /**
- * Gets metazone string given the key and translation type and calendar
- * @param ID The ID of zone strings, e.g: "America/Los_Angeles".
- * The time zone ID is for programmatic lookup.
- * @param type The translation type requested
- * @param cal The calendar
- * @param result Output parameter to recieve the translation string
- * @param status Input/output parameter, set to success or
- * failure code upon return.
- * @return the input UnicodeString parameter for chaining
- * @internal ICU 3.8
- */
- UnicodeString getMetazoneString(const UnicodeString &ID, const TimeZoneTranslationType type, Calendar &cal, UnicodeString &result, UErrorCode &status);
-
- /**
- * Gets fallback string given the key
- * @param ID The ID of zone strings, e.g: "America/Los_Angeles".
- * The time zone ID is for programmatic lookup.
- * @param result Output parameter to recieve the translation string
- * @param status Input/output parameter, set to success or
- * failure code upon return.
- * @return the input UnicodeString parameter for chaining
- * @internal ICU 3.8
- */
- UnicodeString& getFallbackString(const UnicodeString &ID, UnicodeString &result, UErrorCode &status);
-
- /**
- * Sets timezone string for the given the ID and translation type
- * @param ID The ID of zone strings, e.g: "America/Los_Angeles".
- * The time zone ID is for programmatic lookup.
- * @param type The translation type to set the value for
- * @param value The string with which current translation needs to be replaced
- * @param status Input/output parameter, set to success or
- * @internal ICU 3.6
- */
-
- /**
- * Determines if the Commonly Used flag is set for this zone
- * @param zid The ID of zone strings, e.g: "America/Los_Angeles".
- * The time zone ID is for programmatic lookup.
- * @return A boolean value indicating if the zone is commonlyUsed or not.
- * @internal ICU 3.8
- */
- UBool isCommonlyUsed(const UnicodeString &zid);
-
- /**
- * Sets timezone string for the given the ID and translation type
- * @param ID The ID of zone strings, e.g: "America/Los_Angeles".
- * The time zone ID is for programmatic lookup.
- * @param type The translation type to set the value for
- * @param value The string with which current translation needs to be replaced
- * @param status Input/output parameter, set to success or
- * @internal ICU 3.6
- */
- void setZoneString(const UnicodeString &ID, const TimeZoneTranslationType type, const UnicodeString &value, UErrorCode &status);
-
private:
friend class SimpleDateFormat;
@@ -661,13 +569,34 @@
/**
* The format data of all the timezones in this locale.
*/
- UnicodeString** fZoneStrings;
+ UnicodeString **fZoneStrings; // Zone string array set by setZoneStrings
+ UnicodeString **fLocaleZoneStrings; // Zone string array created by the locale
int32_t fZoneStringsRowCount;
int32_t fZoneStringsColCount;
- StringEnumeration* fZoneIDEnumeration;
- Hashtable* fZoneStringsHash;
- UResourceBundle* fResourceBundle;
- const char* fCountry;
+
+ const ZoneStringFormat *fZoneStringFormat;
+ ZoneStringFormat *fZSFLocal; // Local ZoneStringFormat instance
+ SafeZoneStringFormatPtr *fZSFCachePtr; // Cached ZoneStringFormat
+ Locale fZSFLocale; // Locale used for getting ZoneStringFormat
+
+ /**
+ * Pattern string used for localized time zone GMT format. For example, "GMT{0}"
+ */
+ UnicodeString fGmtFormat;
+
+ /**
+ * Pattern strings used for formatting zone offset in a localized time zone GMT string.
+ */
+ UnicodeString *fGmtHourFormats;
+ int32_t fGmtHourFormatsCount;
+
+ enum GMTHourType {
+ GMT_NEGATIVE_HMS = 0,
+ GMT_NEGATIVE_HM,
+ GMT_POSITIVE_HMS,
+ GMT_POSITIVE_HM,
+ GMT_HOUR_COUNT
+ };
/**
* Localized date-time pattern characters. For example: use 'u' as 'y'.
@@ -729,21 +658,6 @@
void createZoneStrings(const UnicodeString *const * otherStrings);
/**
- * Package private: used by SimpleDateFormat
- * Gets the index for the given time zone ID to obtain the timezone
- * strings for formatting. The time zone ID is just for programmatic
- * lookup. NOT LOCALIZED!!!
- * @param ID the given time zone ID.
- * @return the index of the given time zone ID. Returns -1 if
- * the given time zone ID can't be located in the DateFormatSymbols object.
- * @see java.util.SimpleTimeZone
- */
- int32_t getZoneIndex(const UnicodeString& ID) const;
-
- // Internal method; see source for documentation
- int32_t _getZoneIndex(const UnicodeString& id) const;
-
- /**
* Delete all the storage owned by this object.
*/
void dispose(void);
@@ -754,67 +668,26 @@
*/
void copyData(const DateFormatSymbols& other);
+
+ /**
+ * Returns a ZoneStringFormat, used only by SimpleDateFormat for now.
+ */
+ const ZoneStringFormat* getZoneStringFormat(void) const;
+
+ /**
+ * Create a ZoneStringFormat by locale if not yet availble
+ */
+ void initZoneStringFormat(void);
+
+ /**
+ * Create zone strings array by locale if not yet available
+ */
+ void initZoneStringsArray(void);
+
/**
* Delete just the zone strings.
*/
void disposeZoneStrings(void);
-
- /**
- * Initializes the zoneStrings hash and keys StringEnumeration after reading the zoneStrings resource
- */
- void initZoneStrings(UErrorCode &status);
- /**
- * initialzes the zoneStrings has and keys enumeration after reading the strings[][]. Required for backwards
- * compatibility of setZoneStrings method
- */
- void initZoneStrings(const UnicodeString** strings, int32_t rowCount, int32_t collumnCount, UErrorCode& status);
- /**
- * initialization of the fZoneStrings data member
- */
- void initZoneStringsArray(UErrorCode& status);
- /**
- * Creates a deep clone of the Hashtable
- */
- Hashtable* createZoneStringsHash(const Hashtable* otherHash);
-
- /**
- * Fetches the key from the hashtable for a given ID.
- * e.g: for a given ID such as PST returns "Americal/Los_Angeles"
- * Used by SimpleDateFormat class.
- * @param ID The id of the time zone for which the key needs to be fetched
- * @param result Output parameter to recieve the key.
- * @return the input UnicodeString object for chaining
- */
- UnicodeString& getZoneID(const UnicodeString& zid, UnicodeString& result, UErrorCode& status);
-
- /**
- * Fetches the zone type and zone string from the hashtable for a given key.
- * e.g: for key: "Americal/Los_Angeles", text: "2004/1/1 PT 1:00" and start:9
- * returns TIMEZONE_SHORT_GENERIC and "PT".
- * Used by SimpleDateFormat class.
- * @param ID the name of the timezone
- * @param text the string containing the time zone translation
- * @param start The position in string where time zone string starts
- * @param type output parameter to recieve the type of time zone string
- * @param value output parameter to recieve the the acutal time zone string
- */
- void getZoneType(const UnicodeString& zid, const UnicodeString& text, int32_t start, TimeZoneTranslationType& type, UnicodeString& value, UErrorCode& status);
-
- /**
- * Fetches the zone type and zone string from the hashtable by cycling through all elements in the hashtable.
- * e.g: text: "2004/1/1 PT 1:00" and start:9
- * returns "Americal/Los_Angeles", TIMEZONE_SHORT_GENERIC and "PT". Used by SimpleDateFormat class.
- * Used by SimpleDateFormat class.
- * @param ID output parameter to recieve the key name of the time zone
- * @param text the string containing the time zone translation
- * @param start The position in string where time zone string starts
- * @param type output parameter to recieve the type of time zone string
- * @param value output parameter to recieve the the acutal time zone string
- * @param status output parameter to recive the error information
- */
- void findZoneIDTypeValue(UnicodeString& zid, const UnicodeString& text, int32_t start, TimeZoneTranslationType& type, UnicodeString& value, UErrorCode& status);
-
- UnicodeString resolveParsedMetazone(const UnicodeString& zid);
};
U_NAMESPACE_END
diff --git a/icu4c/source/i18n/unicode/rbtz.h b/icu4c/source/i18n/unicode/rbtz.h
index 791f985..d7f342d 100644
--- a/icu4c/source/i18n/unicode/rbtz.h
+++ b/icu4c/source/i18n/unicode/rbtz.h
@@ -23,6 +23,7 @@
// forward declaration
class UVector;
+struct Transition;
class U_I18N_API RuleBasedTimeZone : public BasicTimeZone {
public:
@@ -289,13 +290,27 @@
virtual void getTimeZoneRules(const InitialTimeZoneRule*& initial,
const TimeZoneRule* trsrules[], int32_t& trscount, UErrorCode& status) /*const*/;
+ /**
+ * Get time zone offsets from local wall time.
+ * @internal
+ */
+ virtual void getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) /*const*/;
+
private:
void deleteRules(void);
void deleteTransitions(void);
UVector* copyRules(UVector* source);
- TimeZoneRule* findRuleInFinal(UDate date, UBool local) const;
+ TimeZoneRule* findRuleInFinal(UDate date, UBool local,
+ int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const;
UBool findNext(UDate base, UBool inclusive, UDate& time, TimeZoneRule*& from, TimeZoneRule*& to) const;
UBool findPrev(UDate base, UBool inclusive, UDate& time, TimeZoneRule*& from, TimeZoneRule*& to) const;
+ int32_t getLocalDelta(int32_t rawBefore, int32_t dstBefore, int32_t rawAfter, int32_t dstAfter,
+ int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const;
+ UDate getTransitionTime(Transition* transition, UBool local,
+ int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const;
+ void getOffsetInternal(UDate date, UBool local, int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt,
+ int32_t& rawOffset, int32_t& dstOffset, UErrorCode& ec) const;
InitialTimeZoneRule *fInitialRule;
UVector *fHistoricRules;
diff --git a/icu4c/source/i18n/unicode/simpletz.h b/icu4c/source/i18n/unicode/simpletz.h
index c541132..08fdd20 100644
--- a/icu4c/source/i18n/unicode/simpletz.h
+++ b/icu4c/source/i18n/unicode/simpletz.h
@@ -617,6 +617,13 @@
int32_t& dstOffset, UErrorCode& ec) const;
/**
+ * Get time zone offsets from local wall time.
+ * @internal
+ */
+ virtual void getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) /*const*/;
+
+ /**
* Returns the TimeZone's raw GMT offset (i.e., the number of milliseconds to add
* to GMT to get local time, before taking daylight savings time into account).
*
diff --git a/icu4c/source/i18n/unicode/smpdtfmt.h b/icu4c/source/i18n/unicode/smpdtfmt.h
index b0bbc41..2fb7c8e 100644
--- a/icu4c/source/i18n/unicode/smpdtfmt.h
+++ b/icu4c/source/i18n/unicode/smpdtfmt.h
@@ -38,6 +38,7 @@
class DateFormatSymbols;
class DateFormat;
+class MessageFormat;
/**
*
@@ -650,13 +651,6 @@
UErrorCode& status) const; // in case of illegal argument
/**
- * Used to resolve Time Zone aliases
- *
- * @param zid Time Zone ID to Canonicalize ( resolve aliases )
- */
- void zoneIDCanonicalize( UnicodeString & ) const;
-
- /**
* Used by subFormat() to format a numeric value.
* Appends to toAppendTo a string representation of "value"
* having a number of digits between "minDigits" and
@@ -767,6 +761,12 @@
ParsePosition& pos,
UBool allowNegative) const;
+ void parseInt(const UnicodeString& text,
+ Formattable& number,
+ int32_t maxDigits,
+ ParsePosition& pos,
+ UBool allowNegative) const;
+
/**
* Translate a pattern, mapping each character in the from string to the
* corresponding character in the to string. Return an error if the original
@@ -793,25 +793,22 @@
* if the operation succeeds.
*/
void parseAmbiguousDatesAsAfter(UDate startDate, UErrorCode& status);
-
- /**
- * Given text, a start in the text, and a row index, return the column index that
- * of the zone name that matches (case insensitive) at start, or 0 if none matches.
- *
- int32_t matchZoneString(const UnicodeString& text, int32_t start, int32_t zi) const;
- */
-
- /**
- * Given text, a start in the text, and a calendar, return the next offset in the text
- * after matching the zone string. If we fail to match, return 0. Update the calendar
- * as appropriate.
- */
- int32_t subParseZoneString(const UnicodeString& text, int32_t start, Calendar& cal, UErrorCode& status) const;
/**
- * append the gmt string
+ * Private methods for formatting/parsing GMT string
*/
- inline void appendGMT(UnicodeString &appendTo, Calendar& cal, UErrorCode& status) const;
+ void appendGMT(UnicodeString &appendTo, Calendar& cal, UErrorCode& status) const;
+ void formatGMTDefault(UnicodeString &appendTo, int32_t offset) const;
+ int32_t parseGMT(const UnicodeString &text, ParsePosition &pos) const;
+ int32_t parseGMTDefault(const UnicodeString &text, ParsePosition &pos) const;
+ UBool isDefaultGMTFormat() const;
+
+ void formatRFC822TZ(UnicodeString &appendTo, int32_t offset) const;
+
+ /**
+ * Initialize MessageFormat instances used for GMT formatting/parsing
+ */
+ void initGMTFormatters(UErrorCode &status);
/**
* Used to map pattern characters to Calendar field identifiers.
@@ -854,7 +851,18 @@
*/
/*transient*/ int32_t fDefaultCenturyStartYear;
- /*transient*/ TimeZone* parsedTimeZone; // here to avoid api change
+ enum ParsedTZType {
+ TZTYPE_UNK,
+ TZTYPE_STD,
+ TZTYPE_DST
+ };
+
+ ParsedTZType tztype; // here to avoid api change
+
+ /*
+ * MessageFormat instances used for localized GMT format
+ */
+ MessageFormat **fGMTFormatters;
UBool fHaveDefaultCentury;
};
diff --git a/icu4c/source/i18n/unicode/timezone.h b/icu4c/source/i18n/unicode/timezone.h
index aed21e0..e771cb9 100644
--- a/icu4c/source/i18n/unicode/timezone.h
+++ b/icu4c/source/i18n/unicode/timezone.h
@@ -670,6 +670,15 @@
static UResourceBundle* loadRule(const UResourceBundle* top, const UnicodeString& ruleid, UResourceBundle* oldbundle, UErrorCode&status);
private:
+ friend class ZoneMeta;
+
+ /**
+ * Get a canonical Olson zone ID for the given ID. If the given ID is not valid,
+ * this method returns empty string as the result. If the given ID is a link, then the
+ * referenced ID (canonical ID) is returned.
+ */
+ static UnicodeString& getOlsonCanonicalID(const UnicodeString &id, UnicodeString &canonical);
+
static TimeZone* createCustomTimeZone(const UnicodeString&); // Creates a time zone based on the string.
/**
diff --git a/icu4c/source/i18n/zonemeta.cpp b/icu4c/source/i18n/zonemeta.cpp
new file mode 100644
index 0000000..940e746
--- /dev/null
+++ b/icu4c/source/i18n/zonemeta.cpp
@@ -0,0 +1,930 @@
+/*
+*******************************************************************************
+* Copyright (C) 2007, International Business Machines Corporation and *
+* others. All Rights Reserved. *
+*******************************************************************************
+*/
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "zonemeta.h"
+
+#include "unicode/timezone.h"
+#include "unicode/ustring.h"
+#include "unicode/putil.h"
+
+#include "umutex.h"
+#include "uvector.h"
+#include "cmemory.h"
+#include "gregoimp.h"
+#include "cstring.h"
+#include "ucln_in.h"
+
+static UBool gZoneMetaInitialized = FALSE;
+
+// Metazone mapping tables
+static UMTX gZoneMetaLock = NULL;
+static U_NAMESPACE_QUALIFIER Hashtable *gCanonicalMap = NULL;
+static U_NAMESPACE_QUALIFIER Hashtable *gOlsonToMeta = NULL;
+static U_NAMESPACE_QUALIFIER Hashtable *gMetaToOlson = NULL;
+
+U_CDECL_BEGIN
+/**
+ * Cleanup callback func
+ */
+static UBool U_CALLCONV zoneMeta_cleanup(void)
+{
+ umtx_destroy(&gZoneMetaLock);
+
+ if (gCanonicalMap != NULL) {
+ delete (U_NAMESPACE_QUALIFIER Hashtable*) gCanonicalMap;
+ gCanonicalMap = NULL;
+ }
+
+ if (gOlsonToMeta != NULL) {
+ delete (U_NAMESPACE_QUALIFIER Hashtable*) gOlsonToMeta;
+ gOlsonToMeta = NULL;
+ }
+
+ if (gMetaToOlson != NULL) {
+ delete (U_NAMESPACE_QUALIFIER Hashtable*) gMetaToOlson;
+ gMetaToOlson = NULL;
+ }
+
+ gZoneMetaInitialized = FALSE;
+
+ return TRUE;
+}
+
+/**
+ * Deleter for UVector
+ */
+static void U_CALLCONV
+deleteUVector(void *obj) {
+ delete (U_NAMESPACE_QUALIFIER UVector*) obj;
+}
+
+/**
+ * Deleter for CanonicalMapEntry
+ */
+static void U_CALLCONV
+deleteCanonicalMapEntry(void *obj) {
+ U_NAMESPACE_QUALIFIER CanonicalMapEntry *entry = (U_NAMESPACE_QUALIFIER CanonicalMapEntry*)obj;
+ uprv_free(entry->id);
+ if (entry->country != NULL) {
+ uprv_free(entry->country);
+ }
+ uprv_free(entry);
+}
+
+/**
+ * Deleter for OlsonToMetaMappingEntry
+ */
+static void U_CALLCONV
+deleteOlsonToMetaMappingEntry(void *obj) {
+ U_NAMESPACE_QUALIFIER OlsonToMetaMappingEntry *entry = (U_NAMESPACE_QUALIFIER OlsonToMetaMappingEntry*)obj;
+ uprv_free(entry->mzid);
+ uprv_free(entry);
+}
+
+/**
+ * Deleter for MetaToOlsonMappingEntry
+ */
+static void U_CALLCONV
+deleteMetaToOlsonMappingEntry(void *obj) {
+ U_NAMESPACE_QUALIFIER MetaToOlsonMappingEntry *entry = (U_NAMESPACE_QUALIFIER MetaToOlsonMappingEntry*)obj;
+ uprv_free(entry->id);
+ uprv_free(entry->territory);
+ uprv_free(entry);
+}
+U_CDECL_END
+
+U_NAMESPACE_BEGIN
+
+#define ZID_KEY_MAX 128
+static const char gZoneStringsTag[] = "zoneStrings";
+static const char gUseMetazoneTag[] = "um";
+
+static const char gSupplementalData[] = "supplementalData";
+static const char gMapTimezonesTag[] = "mapTimezones";
+static const char gMetazonesTag[] = "metazones";
+static const char gZoneFormattingTag[] = "zoneFormatting";
+static const char gTerritoryTag[] = "territory";
+static const char gAliasesTag[] = "aliases";
+static const char gMultizoneTag[] = "multizone";
+
+static const char gMetazoneInfo[] = "metazoneInfo";
+static const char gMetazoneMappings[] = "metazoneMappings";
+
+#define MZID_PREFIX_LEN 5
+static const char gMetazoneIdPrefix[] = "meta:";
+
+static const UChar gWorld[] = {0x30, 0x30, 0x31, 0x00}; // "001"
+
+#define ASCII_DIGIT(c) (((c)>=0x30 && (c)<=0x39) ? (c)-0x30 : -1)
+
+/*
+ * Convert a date string used by metazone mappings to UDate.
+ * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
+ */
+ static UDate parseDate (const UChar *text, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return 0;
+ }
+ int32_t year = 0, month = 0, day = 0, hour = 0, min = 0, n;
+ int32_t idx;
+
+ // "yyyy" (0 - 3)
+ for (idx = 0; idx <= 3 && U_SUCCESS(status); idx++) {
+ n = ASCII_DIGIT(text[idx]);
+ if (n >= 0) {
+ year = 10*year + n;
+ } else {
+ status = U_INVALID_FORMAT_ERROR;
+ }
+ }
+ // "MM" (5 - 6)
+ for (idx = 5; idx <= 6 && U_SUCCESS(status); idx++) {
+ n = ASCII_DIGIT(text[idx]);
+ if (n >= 0) {
+ month = 10*month + n;
+ } else {
+ status = U_INVALID_FORMAT_ERROR;
+ }
+ }
+ // "dd" (8 - 9)
+ for (idx = 8; idx <= 9 && U_SUCCESS(status); idx++) {
+ n = ASCII_DIGIT(text[idx]);
+ if (n >= 0) {
+ day = 10*day + n;
+ } else {
+ status = U_INVALID_FORMAT_ERROR;
+ }
+ }
+ // "HH" (11 - 12)
+ for (idx = 11; idx <= 12 && U_SUCCESS(status); idx++) {
+ n = ASCII_DIGIT(text[idx]);
+ if (n >= 0) {
+ hour = 10*hour + n;
+ } else {
+ status = U_INVALID_FORMAT_ERROR;
+ }
+ }
+ // "mm" (14 - 15)
+ for (idx = 14; idx <= 15 && U_SUCCESS(status); idx++) {
+ n = ASCII_DIGIT(text[idx]);
+ if (n >= 0) {
+ min = 10*min + n;
+ } else {
+ status = U_INVALID_FORMAT_ERROR;
+ }
+ }
+
+ if (U_SUCCESS(status)) {
+ UDate date = Grego::fieldsToDay(year, month - 1, day) * U_MILLIS_PER_DAY
+ + hour * U_MILLIS_PER_HOUR + min * U_MILLIS_PER_MINUTE;
+ return date;
+ }
+ return 0;
+}
+
+/*
+ * Initialize global objects
+ */
+void
+ZoneMeta::initialize(void) {
+ UBool initialized;
+ UMTX_CHECK(&gZoneMetaLock, gZoneMetaInitialized, initialized);
+ if (initialized) {
+ return;
+ }
+ UErrorCode status = U_ZERO_ERROR;
+
+ // Initialize hash tables
+ Hashtable *tmpCanonicalMap = createCanonicalMap();
+ Hashtable *tmpOlsonToMeta = createOlsonToMetaMap();
+ if (tmpOlsonToMeta == NULL) {
+ // With ICU 3.8 data
+ createOlsonToMetaMapOld();
+ }
+ Hashtable *tmpMetaToOlson = createMetaToOlsonMap();
+
+ umtx_lock(&gZoneMetaLock);
+ if (gZoneMetaInitialized) {
+ // Another thread already created mappings
+ delete tmpCanonicalMap;
+ delete tmpOlsonToMeta;
+ delete tmpMetaToOlson;
+ } else {
+ gZoneMetaInitialized = TRUE;
+ gCanonicalMap = tmpCanonicalMap;
+ gOlsonToMeta = tmpOlsonToMeta;
+ gMetaToOlson = tmpMetaToOlson;
+ ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
+ }
+ umtx_unlock(&gZoneMetaLock);
+}
+
+Hashtable*
+ZoneMeta::createCanonicalMap(void) {
+ UErrorCode status = U_ZERO_ERROR;
+
+ Hashtable *canonicalMap = NULL;
+ UResourceBundle *supplementalDataBundle = NULL;
+ UResourceBundle *zoneFormatting = NULL;
+ UResourceBundle *tzitem = NULL;
+ UResourceBundle *aliases = NULL;
+
+ canonicalMap = new Hashtable(uhash_compareUnicodeString, NULL, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ canonicalMap->setValueDeleter(deleteCanonicalMapEntry);
+
+ supplementalDataBundle = ures_openDirect(NULL, gSupplementalData, &status);
+ zoneFormatting = ures_getByKey(supplementalDataBundle, gZoneFormattingTag, NULL, &status);
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+
+ while (ures_hasNext(zoneFormatting)) {
+ tzitem = ures_getNextResource(zoneFormatting, NULL, &status);
+ if (U_FAILURE(status)) {
+ ures_close(tzitem);
+ tzitem = NULL;
+ status = U_ZERO_ERROR;
+ continue;
+ }
+ if (ures_getType(tzitem) != URES_TABLE) {
+ ures_close(tzitem);
+ tzitem = NULL;
+ continue;
+ }
+
+ int32_t territoryLen;
+ const UChar *territory = ures_getStringByKey(tzitem, gTerritoryTag, &territoryLen, &status);
+ if (U_FAILURE(status)) {
+ territory = NULL;
+ status = U_ZERO_ERROR;
+ }
+
+ int32_t tzidLen = 0;
+ char tzid[ZID_KEY_MAX];
+ const char *tzkey = ures_getKey(tzitem);
+ uprv_strcpy(tzid, tzkey);
+ // Replace ':' with '/'
+ char *p = tzid;
+ while (*p) {
+ if (*p == ':') {
+ *p = '/';
+ }
+ p++;
+ tzidLen++;
+ }
+
+ // Create canonical map entry
+ CanonicalMapEntry *entry = (CanonicalMapEntry*)uprv_malloc(sizeof(CanonicalMapEntry));
+ if (entry == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ goto error_cleanup;
+ }
+ entry->id = (UChar*)uprv_malloc((tzidLen + 1) * sizeof(UChar));
+ if (entry->id == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ uprv_free(entry);
+ goto error_cleanup;
+ }
+ u_charsToUChars(tzid, entry->id, tzidLen + 1);
+
+ if (territory == NULL || u_strcmp(territory, gWorld) == 0) {
+ entry->country = NULL;
+ } else {
+ entry->country = (UChar*)uprv_malloc((territoryLen + 1) * sizeof(UChar));
+ if (entry->country == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ deleteCanonicalMapEntry(entry);
+ goto error_cleanup;
+ }
+ u_strcpy(entry->country, territory);
+ }
+
+ // Put this entry to the table
+ canonicalMap->put(UnicodeString(tzid), entry, status);
+ if (U_FAILURE(status)) {
+ deleteCanonicalMapEntry(entry);
+ goto error_cleanup;
+ }
+
+ // Get aliases
+ aliases = ures_getByKey(tzitem, gAliasesTag, NULL, &status);
+ if (U_FAILURE(status)) {
+ // No aliases
+ status = U_ZERO_ERROR;
+ ures_close(tzitem);
+ continue;
+ }
+
+ while (ures_hasNext(aliases)) {
+ int32_t aliasLen;
+ const UChar* alias = ures_getNextString(aliases, &aliasLen, NULL, &status);
+ if (U_FAILURE(status)) {
+ status = U_ZERO_ERROR;
+ continue;
+ }
+ // Create canonical map entry for this alias
+ entry = (CanonicalMapEntry*)uprv_malloc(sizeof(CanonicalMapEntry));
+ if (entry == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ goto error_cleanup;
+ }
+ entry->id = (UChar*)uprv_malloc((tzidLen + 1) * sizeof(UChar));
+ if (entry->id == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ uprv_free(entry);
+ goto error_cleanup;
+ }
+ u_charsToUChars(tzid, entry->id, tzidLen + 1);
+
+ if (territory == NULL || u_strcmp(territory, gWorld) == 0) {
+ entry->country = NULL;
+ } else {
+ entry->country = (UChar*)uprv_malloc((territoryLen + 1) * sizeof(UChar));
+ if (entry->country == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ deleteCanonicalMapEntry(entry);
+ goto error_cleanup;
+ }
+ u_strcpy(entry->country, territory);
+ }
+ canonicalMap->put(UnicodeString(alias), entry, status);
+ if (U_FAILURE(status)) {
+ deleteCanonicalMapEntry(entry);
+ goto error_cleanup;
+ }
+ }
+
+ ures_close(aliases);
+ ures_close(tzitem);
+ }
+
+ ures_close(zoneFormatting);
+ ures_close(supplementalDataBundle);
+ return canonicalMap;
+
+error_cleanup:
+ ures_close(aliases);
+ ures_close(tzitem);
+ ures_close(zoneFormatting);
+ ures_close(supplementalDataBundle);
+ delete canonicalMap;
+
+ return NULL;
+}
+
+/*
+ * Creating Olson tzid to metazone mappings from resource (3.8.1 and beyond)
+ */
+Hashtable*
+ZoneMeta::createOlsonToMetaMap(void) {
+ UErrorCode status = U_ZERO_ERROR;
+
+ Hashtable *olsonToMeta = NULL;
+ UResourceBundle *metazoneInfoBundle = NULL;
+ UResourceBundle *metazoneMappings = NULL;
+ StringEnumeration *tzids = NULL;
+
+ olsonToMeta = new Hashtable(uhash_compareUnicodeString, NULL, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ olsonToMeta->setValueDeleter(deleteUVector);
+
+ // Read metazone mappings from metazoneInfo bundle
+ metazoneInfoBundle = ures_openDirect(NULL, gMetazoneInfo, &status);
+ metazoneMappings = ures_getByKey(metazoneInfoBundle, gMetazoneMappings, NULL, &status);
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+
+ // Walk through all canonical tzids
+ char zidkey[ZID_KEY_MAX];
+
+ tzids = TimeZone::createEnumeration();
+ const char *tzid;
+ while ((tzid = tzids->next(NULL, status))) {
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+ // We may skip aliases, because the bundle
+ // contains only canonical IDs. For now, try
+ // all of them.
+ uprv_strcpy(zidkey, tzid);
+
+ // Replace '/' with ':'
+ UBool foundSep = FALSE;
+ char *p = zidkey;
+ while (*p) {
+ if (*p == '/') {
+ *p = ':';
+ foundSep = TRUE;
+ }
+ p++;
+ }
+ if (!foundSep) {
+ // A valid time zone key has at least one separator
+ continue;
+ }
+
+ UResourceBundle *zoneItem = ures_getByKey(metazoneMappings, zidkey, NULL, &status);
+ if (U_FAILURE(status)) {
+ status = U_ZERO_ERROR;
+ ures_close(zoneItem);
+ continue;
+ }
+
+ UVector *mzMappings = NULL;
+ while (ures_hasNext(zoneItem)) {
+ UResourceBundle *mz = ures_getNextResource(zoneItem, NULL, &status);
+ int32_t len;
+ const UChar *mz_name = ures_getStringByIndex(mz, 0, &len, &status);
+ const UChar *mz_from = ures_getStringByIndex(mz, 1, &len, &status);
+ const UChar *mz_to = ures_getStringByIndex(mz, 2, &len, &status);
+ ures_close(mz);
+
+ if(U_FAILURE(status)){
+ status = U_ZERO_ERROR;
+ continue;
+ }
+ // We do not want to use SimpleDateformat to parse boundary dates,
+ // because this code could be triggered by the initialization code
+ // used by SimpleDateFormat.
+ UDate from = parseDate(mz_from, status);
+ UDate to = parseDate(mz_to, status);
+ if (U_FAILURE(status)) {
+ status = U_ZERO_ERROR;
+ continue;
+ }
+
+ OlsonToMetaMappingEntry *entry = (OlsonToMetaMappingEntry*)uprv_malloc(sizeof(OlsonToMetaMappingEntry));
+ if (entry == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ break;
+ }
+ entry->mzid = (UChar*)uprv_malloc((u_strlen(mz_name) + 1) * sizeof(UChar));
+ if (entry->mzid == NULL) {
+ uprv_free(entry);
+ status = U_MEMORY_ALLOCATION_ERROR;
+ break;
+ }
+ u_strcpy(entry->mzid, mz_name);
+ entry->from = from;
+ entry->to = to;
+
+ if (mzMappings == NULL) {
+ mzMappings = new UVector(deleteOlsonToMetaMappingEntry, NULL, status);
+ if (U_FAILURE(status)) {
+ delete mzMappings;
+ deleteOlsonToMetaMappingEntry(entry);
+ uprv_free(entry);
+ break;
+ }
+ }
+
+ mzMappings->addElement(entry, status);
+ if (U_FAILURE(status)) {
+ break;
+ }
+ }
+
+ ures_close(zoneItem);
+
+ if (U_FAILURE(status)) {
+ if (mzMappings != NULL) {
+ delete mzMappings;
+ }
+ goto error_cleanup;
+ }
+ if (mzMappings != NULL) {
+ olsonToMeta->put(UnicodeString(tzid), mzMappings, status);
+ if (U_FAILURE(status)) {
+ delete mzMappings;
+ goto error_cleanup;
+ }
+ }
+ }
+
+ delete tzids;
+ ures_close(metazoneMappings);
+ ures_close(metazoneInfoBundle);
+ return olsonToMeta;
+
+error_cleanup:
+ if (tzids != NULL) {
+ delete tzids;
+ }
+ ures_close(metazoneMappings);
+ ures_close(metazoneInfoBundle);
+ if (olsonToMeta != NULL) {
+ delete olsonToMeta;
+ }
+ return NULL;
+}
+
+/*
+ * Creating Olson tzid to metazone mappings from ICU resource (3.8)
+ */
+Hashtable*
+ZoneMeta::createOlsonToMetaMapOld(void) {
+ UErrorCode status = U_ZERO_ERROR;
+
+ Hashtable *olsonToMeta = NULL;
+ UResourceBundle *rootBundle = NULL;
+ UResourceBundle *zoneStringsArray = NULL;
+ StringEnumeration *tzids = NULL;
+
+ olsonToMeta = new Hashtable(uhash_compareUnicodeString, NULL, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ olsonToMeta->setValueDeleter(deleteUVector);
+
+ // Read metazone mappings from root bundle
+ rootBundle = ures_openDirect(NULL, "", &status);
+ zoneStringsArray = ures_getByKey(rootBundle, gZoneStringsTag, NULL, &status);
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+
+ // Walk through all canonical tzids
+ char zidkey[ZID_KEY_MAX];
+
+ tzids = TimeZone::createEnumeration();
+ const char *tzid;
+ while ((tzid = tzids->next(NULL, status))) {
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+ // We may skip aliases, because the bundle
+ // contains only canonical IDs. For now, try
+ // all of them.
+ uprv_strcpy(zidkey, tzid);
+
+ // Replace '/' with ':'
+ UBool foundSep = FALSE;
+ char *p = zidkey;
+ while (*p) {
+ if (*p == '/') {
+ *p = ':';
+ foundSep = TRUE;
+ }
+ p++;
+ }
+ if (!foundSep) {
+ // A valid time zone key has at least one separator
+ continue;
+ }
+
+ UResourceBundle *zoneItem = ures_getByKey(zoneStringsArray, zidkey, NULL, &status);
+ UResourceBundle *useMZ = ures_getByKey(zoneItem, gUseMetazoneTag, NULL, &status);
+ if (U_FAILURE(status)) {
+ status = U_ZERO_ERROR;
+ ures_close(zoneItem);
+ ures_close(useMZ);
+ continue;
+ }
+
+ UVector *mzMappings = NULL;
+ while (ures_hasNext(useMZ)) {
+ UResourceBundle *mz = ures_getNextResource(useMZ, NULL, &status);
+ int32_t len;
+ const UChar *mz_name = ures_getStringByIndex(mz, 0, &len, &status);
+ const UChar *mz_from = ures_getStringByIndex(mz, 1, &len, &status);
+ const UChar *mz_to = ures_getStringByIndex(mz, 2, &len, &status);
+ ures_close(mz);
+
+ if(U_FAILURE(status)){
+ status = U_ZERO_ERROR;
+ continue;
+ }
+ // We do not want to use SimpleDateformat to parse boundary dates,
+ // because this code could be triggered by the initialization code
+ // used by SimpleDateFormat.
+ UDate from = parseDate(mz_from, status);
+ UDate to = parseDate(mz_to, status);
+ if (U_FAILURE(status)) {
+ status = U_ZERO_ERROR;
+ continue;
+ }
+
+ OlsonToMetaMappingEntry *entry = (OlsonToMetaMappingEntry*)uprv_malloc(sizeof(OlsonToMetaMappingEntry));
+ if (entry == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ break;
+ }
+ entry->mzid = (UChar*)uprv_malloc((u_strlen(mz_name) + 1) * sizeof(UChar));
+ if (entry->mzid == NULL) {
+ uprv_free(entry);
+ status = U_MEMORY_ALLOCATION_ERROR;
+ break;
+ }
+ u_strcpy(entry->mzid, mz_name);
+ entry->from = from;
+ entry->to = to;
+
+ if (mzMappings == NULL) {
+ mzMappings = new UVector(deleteOlsonToMetaMappingEntry, NULL, status);
+ if (U_FAILURE(status)) {
+ delete mzMappings;
+ deleteOlsonToMetaMappingEntry(entry);
+ uprv_free(entry);
+ break;
+ }
+ }
+
+ mzMappings->addElement(entry, status);
+ if (U_FAILURE(status)) {
+ break;
+ }
+ }
+
+ ures_close(zoneItem);
+ ures_close(useMZ);
+
+ if (U_FAILURE(status)) {
+ if (mzMappings != NULL) {
+ delete mzMappings;
+ }
+ goto error_cleanup;
+ }
+ if (mzMappings != NULL) {
+ olsonToMeta->put(UnicodeString(tzid), mzMappings, status);
+ if (U_FAILURE(status)) {
+ delete mzMappings;
+ goto error_cleanup;
+ }
+ }
+ }
+
+ delete tzids;
+ ures_close(zoneStringsArray);
+ ures_close(rootBundle);
+ return olsonToMeta;
+
+error_cleanup:
+ if (tzids != NULL) {
+ delete tzids;
+ }
+ ures_close(zoneStringsArray);
+ ures_close(rootBundle);
+ if (olsonToMeta != NULL) {
+ delete olsonToMeta;
+ }
+ return NULL;
+}
+
+Hashtable*
+ZoneMeta::createMetaToOlsonMap(void) {
+ UErrorCode status = U_ZERO_ERROR;
+
+ Hashtable *metaToOlson = NULL;
+ UResourceBundle *supplementalDataBundle = NULL;
+ UResourceBundle *mapTimezones = NULL;
+ UResourceBundle *metazones = NULL;
+
+ metaToOlson = new Hashtable(uhash_compareUnicodeString, NULL, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ metaToOlson->setValueDeleter(deleteUVector);
+
+ supplementalDataBundle = ures_openDirect(NULL, gSupplementalData, &status);
+ mapTimezones = ures_getByKey(supplementalDataBundle, gMapTimezonesTag, NULL, &status);
+ metazones = ures_getByKey(mapTimezones, gMetazonesTag, NULL, &status);
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+
+ while (ures_hasNext(metazones)) {
+ UResourceBundle *mz = ures_getNextResource(metazones, NULL, &status);
+ if (U_FAILURE(status)) {
+ ures_close(mz);
+ status = U_ZERO_ERROR;
+ continue;
+ }
+ const char *mzkey = ures_getKey(mz);
+ if (uprv_strncmp(mzkey, gMetazoneIdPrefix, MZID_PREFIX_LEN) == 0) {
+ const char *mzid = mzkey + MZID_PREFIX_LEN;
+ const char *territory = uprv_strrchr(mzid, '_');
+ int32_t mzidLen = 0;
+ int32_t territoryLen = 0;
+ if (territory) {
+ mzidLen = territory - mzid;
+ territory++;
+ territoryLen = uprv_strlen(territory);
+ }
+ if (mzidLen > 0 && territoryLen > 0) {
+ int32_t tzidLen;
+ const UChar *tzid = ures_getStringByIndex(mz, 0, &tzidLen, &status);
+ if (U_SUCCESS(status)) {
+ // Create MetaToOlsonMappingEntry
+ MetaToOlsonMappingEntry *entry = (MetaToOlsonMappingEntry*)uprv_malloc(sizeof(MetaToOlsonMappingEntry));
+ if (entry == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ ures_close(mz);
+ goto error_cleanup;
+ }
+ entry->id = (UChar*)uprv_malloc((tzidLen + 1) * sizeof(UChar));
+ if (entry->id == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ uprv_free(entry);
+ ures_close(mz);
+ goto error_cleanup;
+ }
+ u_strcpy(entry->id, tzid);
+
+ entry->territory = (UChar*)uprv_malloc((territoryLen + 1) * sizeof(UChar));
+ if (entry->territory == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ uprv_free(entry->id);
+ uprv_free(entry);
+ ures_close(mz);
+ goto error_cleanup;
+ }
+ u_charsToUChars(territory, entry->territory, territoryLen + 1);
+
+ // Check if mapping entries for metazone is already available
+ UnicodeString mzidStr(mzid, mzidLen);
+ UVector *tzMappings = (UVector*)metaToOlson->get(mzidStr);
+ if (tzMappings == NULL) {
+ // Create new UVector and put it into the hashtable
+ tzMappings = new UVector(deleteMetaToOlsonMappingEntry, NULL, status);
+ metaToOlson->put(mzidStr, tzMappings, status);
+ if (U_FAILURE(status)) {
+ if (tzMappings != NULL) {
+ delete tzMappings;
+ }
+ deleteMetaToOlsonMappingEntry(entry);
+ ures_close(mz);
+ goto error_cleanup;
+ }
+ }
+ tzMappings->addElement(entry, status);
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+ } else {
+ status = U_ZERO_ERROR;
+ }
+ }
+ }
+ ures_close(mz);
+ }
+
+ ures_close(metazones);
+ ures_close(mapTimezones);
+ ures_close(supplementalDataBundle);
+ return metaToOlson;
+
+error_cleanup:
+ ures_close(metazones);
+ ures_close(mapTimezones);
+ ures_close(supplementalDataBundle);
+ if (metaToOlson != NULL) {
+ delete metaToOlson;
+ }
+ return NULL;
+}
+
+UnicodeString&
+ZoneMeta::getCanonicalID(const UnicodeString &tzid, UnicodeString &canonicalID) {
+ const CanonicalMapEntry *entry = getCanonicalInfo(tzid);
+ if (entry != NULL) {
+ canonicalID.setTo(entry->id);
+ } else {
+ // Use the input tzid
+ canonicalID.setTo(tzid);
+ }
+ return canonicalID;
+}
+
+UnicodeString&
+ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &canonicalCountry) {
+ const CanonicalMapEntry *entry = getCanonicalInfo(tzid);
+ if (entry != NULL && entry->country != NULL) {
+ canonicalCountry.setTo(entry->country);
+ } else {
+ // Use the input tzid
+ canonicalCountry.remove();
+ }
+ return canonicalCountry;
+}
+
+const CanonicalMapEntry*
+ZoneMeta::getCanonicalInfo(const UnicodeString &tzid) {
+ initialize();
+ CanonicalMapEntry *entry = NULL;
+ UnicodeString canonicalOlsonId;
+ TimeZone::getOlsonCanonicalID(tzid, canonicalOlsonId);
+ if (!canonicalOlsonId.isEmpty()) {
+ if (gCanonicalMap != NULL) {
+ entry = (CanonicalMapEntry*)gCanonicalMap->get(tzid);
+ }
+ }
+ return entry;
+}
+
+UnicodeString&
+ZoneMeta::getSingleCountry(const UnicodeString &tzid, UnicodeString &country) {
+ UErrorCode status = U_ZERO_ERROR;
+
+ // Get canonical country for the zone
+ getCanonicalCountry(tzid, country);
+
+ if (!country.isEmpty()) {
+ UResourceBundle *supplementalDataBundle = ures_openDirect(NULL, gSupplementalData, &status);
+ UResourceBundle *zoneFormatting = ures_getByKey(supplementalDataBundle, gZoneFormattingTag, NULL, &status);
+ UResourceBundle *multizone = ures_getByKey(zoneFormatting, gMultizoneTag, NULL, &status);
+
+ if (U_SUCCESS(status)) {
+ while (ures_hasNext(multizone)) {
+ int32_t len;
+ const UChar* multizoneCountry = ures_getNextString(multizone, &len, NULL, &status);
+ if (country.compare(multizoneCountry, len) == 0) {
+ // Included in the multizone country list
+ country.remove();
+ break;
+ }
+ }
+ }
+
+ ures_close(multizone);
+ ures_close(zoneFormatting);
+ ures_close(supplementalDataBundle);
+ }
+
+ return country;
+}
+
+UnicodeString&
+ZoneMeta::getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &result) {
+ UBool isSet = FALSE;
+ const UVector *mappings = getMetazoneMappings(tzid);
+ if (mappings != NULL) {
+ for (int32_t i = 0; i < mappings->size(); i++) {
+ OlsonToMetaMappingEntry *mzm = (OlsonToMetaMappingEntry*)mappings->elementAt(i);
+ if (mzm->from <= date && mzm->to > date) {
+ result.setTo(mzm->mzid, -1);
+ isSet = TRUE;
+ break;
+ }
+ }
+ }
+ if (!isSet) {
+ result.remove();
+ }
+ return result;
+}
+
+const UVector*
+ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) {
+ initialize();
+ const UVector *result;
+ if (gOlsonToMeta != NULL) {
+ result = (UVector*)gOlsonToMeta->get(tzid);
+ }
+ return result;
+}
+
+UnicodeString&
+ZoneMeta::getZoneIdByMetazone(const UnicodeString &mzid, const UnicodeString ®ion, UnicodeString &result) {
+ initialize();
+ UBool isSet = FALSE;
+ if (gMetaToOlson != NULL) {
+ UVector *mappings = (UVector*)gMetaToOlson->get(mzid);
+ if (mappings != NULL) {
+ // Find a preferred time zone for the given region.
+ for (int32_t i = 0; i < mappings->size(); i++) {
+ MetaToOlsonMappingEntry *olsonmap = (MetaToOlsonMappingEntry*)mappings->elementAt(i);
+ if (region.compare(olsonmap->territory, -1) == 0) {
+ result.setTo(olsonmap->id);
+ isSet = TRUE;
+ break;
+ } else if (u_strcmp(olsonmap->territory, gWorld) == 0) {
+ result.setTo(olsonmap->id);
+ isSet = TRUE;
+ }
+ }
+ }
+ }
+ if (!isSet) {
+ result.remove();
+ }
+ return result;
+}
+
+
+U_NAMESPACE_END
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/zonemeta.h b/icu4c/source/i18n/zonemeta.h
new file mode 100644
index 0000000..edba73b
--- /dev/null
+++ b/icu4c/source/i18n/zonemeta.h
@@ -0,0 +1,84 @@
+/*
+*******************************************************************************
+* Copyright (C) 2007, International Business Machines Corporation and *
+* others. All Rights Reserved. *
+*******************************************************************************
+*/
+#ifndef ZONEMETA_H
+#define ZONEMETA_H
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "unicode/unistr.h"
+#include "hash.h"
+
+U_NAMESPACE_BEGIN
+
+typedef struct CanonicalMapEntry {
+ UChar *id;
+ UChar *country;
+} CanonicalMapEntry;
+
+typedef struct OlsonToMetaMappingEntry {
+ UChar *mzid;
+ UDate from;
+ UDate to;
+} OlsonToMetaMappingEntry;
+
+typedef struct MetaToOlsonMappingEntry {
+ UChar *id;
+ UChar *territory;
+} MetaToOlsonMappingEntry;
+
+class UVector;
+
+class U_I18N_API ZoneMeta {
+public:
+ /**
+ * Return the canonical id for this tzid, which might be the id itself.
+ * If there is no canonical id for it, return the passed-in id.
+ */
+ static UnicodeString& getCanonicalID(const UnicodeString &tzid, UnicodeString &canonicalID);
+
+ /**
+ * Return the canonical country code for this tzid. If we have none, or if the time zone
+ * is not associated with a country, return null.
+ */
+ static UnicodeString& getCanonicalCountry(const UnicodeString &tzid, UnicodeString &canonicalCountry);
+
+ /**
+ * Return the country code if this is a 'single' time zone that can fallback to just
+ * the country, otherwise return empty string. (Note, one must also check the locale data
+ * to see that there is a localization for the country in order to implement
+ * tr#35 appendix J step 5.)
+ */
+ static UnicodeString& getSingleCountry(const UnicodeString &tzid, UnicodeString &country);
+
+ /**
+ * Returns a CLDR metazone ID for the given Olson tzid and time.
+ */
+ static UnicodeString& getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &result);
+ /**
+ * Returns an Olson ID for the ginve metazone and region
+ */
+ static UnicodeString& getZoneIdByMetazone(const UnicodeString &mzid, const UnicodeString ®ion, UnicodeString &result);
+
+ static const UVector* getMetazoneMappings(const UnicodeString &tzid);
+
+private:
+ static void initialize(void);
+
+ static const CanonicalMapEntry* getCanonicalInfo(const UnicodeString &tzid);
+
+ static Hashtable* createCanonicalMap(void);
+ static Hashtable* createOlsonToMetaMapOld(void);
+ static Hashtable* createOlsonToMetaMap(void);
+ static Hashtable* createMetaToOlsonMap(void);
+};
+
+U_NAMESPACE_END
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
+#endif // ZONEMETA_H
diff --git a/icu4c/source/i18n/zstrfmt.cpp b/icu4c/source/i18n/zstrfmt.cpp
new file mode 100644
index 0000000..ee605b8
--- /dev/null
+++ b/icu4c/source/i18n/zstrfmt.cpp
@@ -0,0 +1,1658 @@
+/*
+*******************************************************************************
+* Copyright (C) 2007, International Business Machines Corporation and *
+* others. All Rights Reserved. *
+*******************************************************************************
+*/
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "zstrfmt.h"
+
+#include "unicode/ustring.h"
+#include "unicode/putil.h"
+#include "unicode/msgfmt.h"
+#include "unicode/basictz.h"
+#include "unicode/simpletz.h"
+#include "unicode/rbtz.h"
+#include "unicode/vtzone.h"
+
+#include "uvector.h"
+#include "cstring.h"
+#include "cmemory.h"
+#include "uresimp.h"
+#include "zonemeta.h"
+#include "olsontz.h"
+#include "umutex.h"
+#include "ucln_in.h"
+
+/**
+ * global ZoneStringFormatCache stuffs
+ */
+static UMTX gZSFCacheLock = NULL;
+static U_NAMESPACE_QUALIFIER ZSFCache *gZoneStringFormatCache = NULL;
+
+U_CDECL_BEGIN
+/**
+ * ZoneStringFormatCache cleanup callback func
+ */
+static UBool U_CALLCONV zoneStringFormat_cleanup(void)
+{
+ umtx_destroy(&gZSFCacheLock);
+ if (gZoneStringFormatCache != NULL) {
+ delete (U_NAMESPACE_QUALIFIER ZSFCache*) gZoneStringFormatCache;
+ }
+ return TRUE;
+}
+
+/**
+ * Deleter for ZoneStringInfo
+ */
+static void U_CALLCONV
+deleteZoneStringInfo(void *obj) {
+ delete (U_NAMESPACE_QUALIFIER ZoneStringInfo*)obj;
+}
+
+/**
+ * Deleter for ZoneStrings
+ */
+static void U_CALLCONV
+deleteZoneStrings(void *obj) {
+ delete (U_NAMESPACE_QUALIFIER ZoneStrings*)obj;
+}
+U_CDECL_END
+
+U_NAMESPACE_BEGIN
+
+#define ZID_KEY_MAX 128
+
+static const char gCountriesTag[] = "Countries";
+static const char gZoneStringsTag[] = "zoneStrings";
+static const char gShortGenericTag[] = "sg";
+static const char gShortStandardTag[] = "ss";
+static const char gShortDaylightTag[] = "sd";
+static const char gLongGenericTag[] = "lg";
+static const char gLongStandardTag[] = "ls";
+static const char gLongDaylightTag[] = "ld";
+static const char gExemplarCityTag[] = "ec";
+static const char gCommonlyUsedTag[] = "cu";
+static const char gFallbackFormatTag[] = "fallbackFormat";
+static const char gRegionFormatTag[] = "regionFormat";
+
+#define MZID_PREFIX_LEN 5
+static const char gMetazoneIdPrefix[] = "meta:";
+
+#define MAX_METAZONES_PER_ZONE 10
+
+static const UChar gDefFallbackPattern[] = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})"
+static const UChar gDefRegionPattern[] = {0x7B, 0x30, 0x7D, 0x00}; // "{0}"
+static const UChar gCommonlyUsedTrue[] = {0x31, 0x00}; // "1"
+
+static const double kDstCheckRange = (double)184*U_MILLIS_PER_DAY;
+
+static int32_t
+getTimeZoneTranslationTypeIndex(TimeZoneTranslationType type) {
+ int32_t typeIdx = 0;
+ switch (type) {
+ case LOCATION:
+ typeIdx = ZSIDX_LOCATION;
+ break;
+ case GENERIC_LONG:
+ typeIdx = ZSIDX_LONG_GENERIC;
+ break;
+ case GENERIC_SHORT:
+ typeIdx = ZSIDX_SHORT_GENERIC;
+ break;
+ case STANDARD_LONG:
+ typeIdx = ZSIDX_LONG_STANDARD;
+ break;
+ case STANDARD_SHORT:
+ typeIdx = ZSIDX_SHORT_STANDARD;
+ break;
+ case DAYLIGHT_LONG:
+ typeIdx = ZSIDX_LONG_DAYLIGHT;
+ break;
+ case DAYLIGHT_SHORT:
+ typeIdx = ZSIDX_SHORT_DAYLIGHT;
+ break;
+ }
+ return typeIdx;
+}
+
+static int32_t
+getTimeZoneTranslationType(TimeZoneTranslationTypeIndex typeIdx) {
+ int32_t type = 0;
+ switch (typeIdx) {
+ case ZSIDX_LOCATION:
+ type = LOCATION;
+ break;
+ case ZSIDX_LONG_GENERIC:
+ type = GENERIC_LONG;
+ break;
+ case ZSIDX_SHORT_GENERIC:
+ type = GENERIC_SHORT;
+ break;
+ case ZSIDX_LONG_STANDARD:
+ type = STANDARD_LONG;
+ break;
+ case ZSIDX_SHORT_STANDARD:
+ type = STANDARD_SHORT;
+ break;
+ case ZSIDX_LONG_DAYLIGHT:
+ type = DAYLIGHT_LONG;
+ break;
+ case ZSIDX_SHORT_DAYLIGHT:
+ type = DAYLIGHT_SHORT;
+ break;
+ }
+ return type;
+}
+
+// ----------------------------------------------------------------------------
+CharacterNode::CharacterNode(UChar32 c, UObjectDeleter *valueDeleterFunc)
+: UMemory(), fCharacter(c), fChildren(NULL), fValues(NULL),
+ fValueDeleter(valueDeleterFunc) {
+}
+
+CharacterNode::~CharacterNode() {
+ if (fValues != NULL) {
+ delete fValues;
+ }
+ if (fChildren != NULL) {
+ while (!fChildren->isEmpty()) {
+ CharacterNode *node = (CharacterNode*)fChildren->orphanElementAt(0);
+ delete node;
+ }
+ delete fChildren;
+ }
+}
+
+void
+CharacterNode::addValue(void *value, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ if (fValues == NULL) {
+ fValues = new UVector(fValueDeleter, NULL, status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ }
+ fValues->addElement(value, status);
+}
+
+CharacterNode*
+CharacterNode::addChildNode(UChar32 c, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ CharacterNode *result = NULL;
+ if (fChildren == NULL) {
+ fChildren = new UVector(status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ } else {
+ for (int32_t i = 0; i < fChildren->size(); i++) {
+ CharacterNode *node = (CharacterNode*)fChildren->elementAt(i);
+ if (node->getCharacter() == c) {
+ result = node;
+ break;
+ }
+ }
+ }
+ if (result == NULL) {
+ result = new CharacterNode(c, fValueDeleter);
+ fChildren->addElement(result, status);
+ }
+
+ return result;
+}
+
+CharacterNode*
+CharacterNode::getChildNode(UChar32 c) const {
+ if (fChildren == NULL) {
+ return NULL;
+ }
+ CharacterNode *result = NULL;
+ for (int32_t i = 0; i < fChildren->size(); i++) {
+ CharacterNode *node = (CharacterNode*)fChildren->elementAt(i);
+ if (node->getCharacter() == c) {
+ result = node;
+ break;
+ }
+ }
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+TextTrieMap::TextTrieMap(UBool ignoreCase, UObjectDeleter *valueDeleterFunc)
+: UMemory(), fIgnoreCase(ignoreCase), fValueDeleter(valueDeleterFunc), fRoot(NULL) {
+}
+
+TextTrieMap::~TextTrieMap() {
+ if (fRoot != NULL) {
+ delete fRoot;
+ }
+}
+
+void
+TextTrieMap::put(const UnicodeString &key, void *value, UErrorCode &status) {
+ if (fRoot == NULL) {
+ fRoot = new CharacterNode(0, fValueDeleter);
+ }
+
+ UnicodeString keyString(key);
+ if (fIgnoreCase) {
+ keyString.foldCase();
+ }
+
+ CharacterNode *node = fRoot;
+ int32_t index = 0;
+ while (index < keyString.length()) {
+ UChar32 c = keyString.char32At(index);
+ node = node->addChildNode(c, status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ index = keyString.moveIndex32(index, 1);
+ }
+ node->addValue(value, status);
+}
+
+void
+TextTrieMap::search(const UnicodeString &text, int32_t start,
+ TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
+ if (fRoot == NULL) {
+ return;
+ }
+ search(fRoot, text, start, start, handler, status);
+}
+
+void
+TextTrieMap::search(CharacterNode *node, const UnicodeString &text, int32_t start,
+ int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ const UVector *values = node->getValues();
+ if (values != NULL) {
+ if (!handler->handleMatch(index - start, values, status)) {
+ return;
+ }
+ if (U_FAILURE(status)) {
+ return;
+ }
+ }
+ UChar32 c = text.char32At(index);
+ if (fIgnoreCase) {
+ // size of character may grow after fold operation
+ UnicodeString tmp(c);
+ tmp.foldCase();
+ int32_t tmpidx = 0;
+ while (tmpidx < tmp.length()) {
+ c = tmp.char32At(tmpidx);
+ node = node->getChildNode(c);
+ if (node == NULL) {
+ break;
+ }
+ tmpidx = tmp.moveIndex32(tmpidx, 1);
+ }
+ } else {
+ node = node->getChildNode(c);
+ }
+ if (node != NULL) {
+ search(node, text, start, index+1, handler, status);
+ }
+}
+
+// ----------------------------------------------------------------------------
+ZoneStringInfo::ZoneStringInfo(const UnicodeString &id, const UnicodeString &str,
+ TimeZoneTranslationType type)
+: UMemory(), fId(id), fStr(str), fType(type) {
+}
+
+ZoneStringInfo::~ZoneStringInfo() {
+}
+// ----------------------------------------------------------------------------
+ZoneStringSearchResultHandler::ZoneStringSearchResultHandler()
+: UMemory(), fResults(NULL) {
+ clear();
+}
+
+ZoneStringSearchResultHandler::~ZoneStringSearchResultHandler() {
+ clear();
+}
+
+UBool
+ZoneStringSearchResultHandler::handleMatch(int32_t matchLength, const UVector *values, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return FALSE;
+ }
+ if (values != NULL) {
+ if (fResults == NULL) {
+ fResults = new UVector(status);
+ if (U_FAILURE(status)) {
+ return FALSE;
+ }
+ }
+ for (int32_t i = 0; values->size(); i++) {
+ ZoneStringInfo *zsinfo = (ZoneStringInfo*)values->elementAt(i);
+ if (zsinfo == NULL) {
+ break;
+ }
+ // Update the results
+ UBool foundType = FALSE;
+ for (int32_t j = 0; j < fResults->size(); j++) {
+ ZoneStringInfo *tmp = (ZoneStringInfo*)fResults->elementAt(j);
+ if (zsinfo->fType == tmp->fType) {
+ int32_t lenidx = getTimeZoneTranslationTypeIndex(tmp->fType);
+ if (matchLength > fMatchLen[lenidx]) {
+ // Same type, longer match
+ fResults->setElementAt(zsinfo, j);
+ fMatchLen[lenidx] = matchLength;
+ }
+ foundType = TRUE;
+ break;
+ }
+ }
+ if (!foundType) {
+ // not found in the current list
+ fResults->addElement(zsinfo, status);
+ fMatchLen[getTimeZoneTranslationTypeIndex(zsinfo->fType)] = matchLength;
+ }
+ }
+ }
+ return TRUE;
+}
+
+int32_t
+ZoneStringSearchResultHandler::countMatches(void) {
+ if (fResults == NULL) {
+ return 0;
+ }
+ return fResults->size();
+}
+
+const ZoneStringInfo*
+ZoneStringSearchResultHandler::getMatch(int32_t index, int32_t &matchLength) {
+ ZoneStringInfo *zsinfo = NULL;
+ if (fResults != NULL && index < fResults->size()) {
+ zsinfo = (ZoneStringInfo*)fResults->elementAt(index);
+ matchLength = fMatchLen[getTimeZoneTranslationTypeIndex(zsinfo->fType)];
+ }
+ return zsinfo;
+}
+
+void
+ZoneStringSearchResultHandler::clear(void) {
+ if (fResults != NULL) {
+ delete fResults;
+ }
+ for (int32_t i = 0; i < sizeof(fMatchLen)/sizeof(int32_t); i++) {
+ fMatchLen[i] = 0;
+ }
+}
+// ----------------------------------------------------------------------------
+ZoneStringFormat::ZoneStringFormat(const UnicodeString* const* strings,
+ int32_t rowCount, int32_t columnCount, UErrorCode &status)
+: UMemory(), fTzidToStrings(NULL), fMzidToStrings(NULL), fZoneStringsTrie(NULL) {
+ fLocale.setToBogus();
+ if (strings == NULL || columnCount <= 0 || rowCount <= 0) {
+ return;
+ }
+
+ fTzidToStrings = new Hashtable(uhash_compareUnicodeString, NULL, status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ fTzidToStrings->setValueDeleter(deleteZoneStrings);
+
+ fZoneStringsTrie = new TextTrieMap(TRUE, deleteZoneStringInfo);
+
+ for (int32_t row = 0; row < rowCount; row++) {
+ if (strings[row][0].isEmpty()) {
+ continue;
+ }
+ UnicodeString *names = new UnicodeString[ZSIDX_COUNT];
+ for (int32_t col = 1; col < columnCount; col++) {
+ if (!strings[row][col].isEmpty()) {
+ int32_t typeIdx = -1;
+ switch (col) {
+ case 1:
+ typeIdx = ZSIDX_LONG_STANDARD;
+ break;
+ case 2:
+ typeIdx = ZSIDX_SHORT_STANDARD;
+ break;
+ case 3:
+ typeIdx = ZSIDX_LONG_DAYLIGHT;
+ break;
+ case 4:
+ typeIdx = ZSIDX_SHORT_DAYLIGHT;
+ break;
+ case 5:
+ typeIdx = ZSIDX_LOCATION;
+ break;
+ case 6:
+ typeIdx = ZSIDX_LONG_GENERIC;
+ break;
+ case 7:
+ typeIdx = ZSIDX_SHORT_GENERIC;
+ break;
+ }
+ if (typeIdx != -1) {
+ names[typeIdx].setTo(strings[row][col]);
+
+ // Put the name into the trie
+ int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)typeIdx);
+ ZoneStringInfo *zsinf = new ZoneStringInfo(strings[row][0], strings[row][col], (TimeZoneTranslationType)type);
+ fZoneStringsTrie->put(strings[row][col], zsinf, status);
+ if (U_FAILURE(status)) {
+ delete zsinf;
+ goto error_cleanup;
+ }
+ }
+ }
+ }
+ ZoneStrings *zstrings = new ZoneStrings(names, ZSIDX_COUNT, TRUE, NULL, 0, 0);
+ fTzidToStrings->put(strings[row][0], zstrings, status);
+ if (U_FAILURE(status)) {
+ delete zstrings;
+ goto error_cleanup;
+ }
+ }
+ return;
+
+error_cleanup:
+ delete fZoneStringsTrie;
+ fZoneStringsTrie = NULL;
+
+ delete fTzidToStrings;
+ fTzidToStrings = NULL;
+}
+
+ZoneStringFormat::ZoneStringFormat(const Locale &locale, UErrorCode &status)
+: UMemory(), fLocale(locale), fTzidToStrings(NULL), fMzidToStrings(NULL),
+ fZoneStringsTrie(NULL) {
+
+ fTzidToStrings = new Hashtable(uhash_compareUnicodeString, NULL, status);
+ fMzidToStrings = new Hashtable(uhash_compareUnicodeString, NULL, status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ fTzidToStrings->setValueDeleter(deleteZoneStrings);
+ fMzidToStrings->setValueDeleter(deleteZoneStrings);
+
+ fZoneStringsTrie = new TextTrieMap(TRUE, deleteZoneStringInfo);
+
+ UResourceBundle *localeBundle = ures_open(NULL, locale.getName(), &status);
+ UResourceBundle *zoneStringsArray = ures_getByKeyWithFallback(localeBundle, gZoneStringsTag, NULL, &status);
+ if (U_FAILURE(status)) {
+ // If no locale bundles are available, zoneStrings will be null.
+ // We still want to go through the rest of zone strings initialization,
+ // because generic location format is generated from tzid for the case.
+ // The rest of code should work even zoneStrings is null.
+ status = U_ZERO_ERROR;
+ if (localeBundle != NULL) {
+ ures_close(localeBundle);
+ }
+ localeBundle = NULL;
+ zoneStringsArray = NULL;
+ }
+
+ StringEnumeration *tzids = NULL;
+ MessageFormat *fallbackFmt = NULL;
+ MessageFormat *regionFmt = NULL;
+
+ UResourceBundle *zoneItem = NULL;
+ UResourceBundle *metazoneItem = NULL;
+
+ char zidkey[ZID_KEY_MAX];
+ const UChar *zstrarray[ZSIDX_COUNT];
+ const UChar *mzstrarray[ZSIDX_COUNT];
+ UnicodeString mzPartialLoc[MAX_METAZONES_PER_ZONE][4];
+
+ UnicodeString region;
+ getRegion(region);
+
+ fallbackFmt = getFallbackFormat(locale, status);
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+ regionFmt = getRegionFormat(locale, status);
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+
+ tzids = TimeZone::createEnumeration();
+ const char *tzid;
+ while ((tzid = tzids->next(NULL, status))) {
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+ // Skip non-canonical IDs
+ UnicodeString utzid(tzid);
+ UnicodeString canonicalID;
+ ZoneMeta::getCanonicalID(utzid, canonicalID);
+ if (utzid != canonicalID) {
+ continue;
+ }
+
+ uprv_strcpy(zidkey, tzid);
+
+ // Replace '/' with ':'
+ char *pCity = NULL;
+ char *p = zidkey;
+ while (*p) {
+ if (*p == '/') {
+ *p = ':';
+ pCity = p + 1;
+ }
+ p++;
+ }
+
+ if (zoneStringsArray != NULL) {
+ zoneItem = ures_getByKeyWithFallback(zoneStringsArray, zidkey, NULL, &status);
+ if (U_FAILURE(status)) {
+ // If failed to open the zone item, create only location string
+ ures_close(zoneItem);
+ zoneItem = NULL;
+ status = U_ZERO_ERROR;
+ }
+ }
+ zstrarray[ZSIDX_LONG_STANDARD] = getZoneStringFromBundle(zoneItem, gLongStandardTag);
+ zstrarray[ZSIDX_SHORT_STANDARD] = getZoneStringFromBundle(zoneItem, gShortStandardTag);
+ zstrarray[ZSIDX_LONG_DAYLIGHT] = getZoneStringFromBundle(zoneItem, gLongDaylightTag);
+ zstrarray[ZSIDX_SHORT_DAYLIGHT] = getZoneStringFromBundle(zoneItem, gShortDaylightTag);
+ zstrarray[ZSIDX_LONG_GENERIC] = getZoneStringFromBundle(zoneItem, gLongGenericTag);
+ zstrarray[ZSIDX_SHORT_GENERIC] = getZoneStringFromBundle(zoneItem, gShortGenericTag);
+
+ // Compose location format string
+ UnicodeString location;
+ UnicodeString country;
+ UnicodeString city;
+ UnicodeString countryCode;
+ ZoneMeta::getCanonicalCountry(utzid, countryCode);
+ if (countryCode.isEmpty()) {
+ zstrarray[ZSIDX_LOCATION] = NULL;
+ } else {
+ const UChar* tmpCity = getZoneStringFromBundle(zoneItem, gExemplarCityTag);
+ if (tmpCity != NULL) {
+ city.setTo(tmpCity, -1);
+ } else {
+ city.setTo(pCity, -1);
+ // Replace '_' with ' '
+ for (int32_t i = 0; i < city.length(); i++) {
+ if (city.charAt(i) == (UChar)0x5F /*'_'*/) {
+ city.setCharAt(i, (UChar)0x20 /*' '*/);
+ }
+ }
+ }
+ getLocalizedCountry(countryCode, locale, country);
+ UnicodeString singleCountry;
+ ZoneMeta::getSingleCountry(utzid, singleCountry);
+ FieldPosition fpos;
+ if (singleCountry.isEmpty()) {
+ Formattable params [] = {
+ Formattable(city),
+ Formattable(country)
+ };
+ fallbackFmt->format(params, 2, location, fpos, status);
+ } else {
+ // If the zone is only one zone in the country, do not add city
+ Formattable params [] = {
+ Formattable(country)
+ };
+ regionFmt->format(params, 1, location, fpos, status);
+ }
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+ zstrarray[ZSIDX_LOCATION] = location.getTerminatedBuffer();
+ }
+
+ UBool commonlyUsed = isCommonlyUsed(zoneItem);
+
+ // Resolve metazones used by this zone
+ int32_t mzPartialLocIdx = 0;
+ const UVector *metazoneMappings = ZoneMeta::getMetazoneMappings(utzid);
+ if (metazoneMappings != NULL) {
+ for (int32_t i = 0; i < metazoneMappings->size(); i++) {
+ const OlsonToMetaMappingEntry *mzmap = (const OlsonToMetaMappingEntry*)metazoneMappings->elementAt(i);
+ UnicodeString mzid(mzmap->mzid);
+ const ZoneStrings *mzStrings = (const ZoneStrings*)fMzidToStrings->get(mzid);
+ if (mzStrings == NULL) {
+ // If the metazone strings are not yet processed, do it now.
+ char mzidkey[ZID_KEY_MAX];
+ uprv_strcpy(mzidkey, gMetazoneIdPrefix);
+ u_UCharsToChars(mzmap->mzid, mzidkey + MZID_PREFIX_LEN, u_strlen(mzmap->mzid) + 1);
+ metazoneItem = ures_getByKeyWithFallback(zoneStringsArray, mzidkey, NULL, &status);
+ if (U_FAILURE(status)) {
+ // No resources available for this metazone
+ ures_close(metazoneItem);
+ metazoneItem = NULL;
+ status = U_ZERO_ERROR;
+ continue;
+ }
+ UBool mzCommonlyUsed = isCommonlyUsed(metazoneItem);
+ mzstrarray[ZSIDX_LONG_STANDARD] = getZoneStringFromBundle(metazoneItem, gLongStandardTag);
+ mzstrarray[ZSIDX_SHORT_STANDARD] = getZoneStringFromBundle(metazoneItem, gShortStandardTag);
+ mzstrarray[ZSIDX_LONG_DAYLIGHT] = getZoneStringFromBundle(metazoneItem, gLongDaylightTag);
+ mzstrarray[ZSIDX_SHORT_DAYLIGHT] = getZoneStringFromBundle(metazoneItem, gShortDaylightTag);
+ mzstrarray[ZSIDX_LONG_GENERIC] = getZoneStringFromBundle(metazoneItem, gLongGenericTag);
+ mzstrarray[ZSIDX_SHORT_GENERIC] = getZoneStringFromBundle(metazoneItem, gShortGenericTag);
+ mzstrarray[ZSIDX_LOCATION] = NULL;
+
+ int32_t lastNonNullIdx = ZSIDX_COUNT - 1;
+ while (lastNonNullIdx >= 0) {
+ if (mzstrarray[lastNonNullIdx] != NULL) {
+ break;
+ }
+ lastNonNullIdx--;
+ }
+ UnicodeString *strings_mz = NULL;
+ ZoneStrings *tmp_mzStrings = NULL;
+ if (lastNonNullIdx >= 0) {
+ // Create UnicodeString array and put strings to the zone string trie
+ strings_mz = new UnicodeString[lastNonNullIdx + 1];
+
+ UnicodeString preferredIdForLocale;
+ ZoneMeta::getZoneIdByMetazone(mzid, region, preferredIdForLocale);
+
+ for (int32_t typeidx = 0; typeidx <= lastNonNullIdx; typeidx++) {
+ if (mzstrarray[typeidx] != NULL) {
+ strings_mz[typeidx].setTo(mzstrarray[typeidx], -1);
+
+ // Add a metazone string to the zone string trie
+ int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)typeidx);
+ ZoneStringInfo *zsinfo = new ZoneStringInfo(preferredIdForLocale, strings_mz[typeidx], (TimeZoneTranslationType)type);
+ fZoneStringsTrie->put(strings_mz[typeidx], zsinfo, status);
+ if (U_FAILURE(status)) {
+ delete zsinfo;
+ delete strings_mz;
+ goto error_cleanup;
+ }
+ }
+ }
+ tmp_mzStrings = new ZoneStrings(strings_mz, lastNonNullIdx + 1, mzCommonlyUsed, NULL, 0, 0);
+ } else {
+ // Create ZoneStrings with empty contents
+ tmp_mzStrings = new ZoneStrings(NULL, 0, FALSE, NULL, 0, 0);
+ }
+
+ fMzidToStrings->put(mzid, tmp_mzStrings, status);
+ if (U_FAILURE(status)) {
+ delete tmp_mzStrings;
+ goto error_cleanup;
+ }
+
+ ures_close(metazoneItem);
+ metazoneItem = NULL;
+
+ mzStrings = tmp_mzStrings;
+ }
+
+ // Compose generic partial location format
+ UnicodeString lg;
+ UnicodeString sg;
+
+ mzStrings->getString(ZSIDX_LONG_GENERIC, lg);
+ mzStrings->getString(ZSIDX_SHORT_GENERIC, sg);
+
+ if (!lg.isEmpty() || !sg.isEmpty()) {
+ UBool addMzPartialLocationNames = TRUE;
+ for (int32_t j = 0; j < mzPartialLocIdx; j++) {
+ if (mzPartialLoc[j][0] == mzid) {
+ // already processed
+ addMzPartialLocationNames = FALSE;
+ break;
+ }
+ }
+ if (addMzPartialLocationNames) {
+ UnicodeString *locationPart = NULL;
+ // Check if the zone is the preferred zone for the territory associated with the zone
+ UnicodeString preferredID;
+ ZoneMeta::getZoneIdByMetazone(mzid, countryCode, preferredID);
+ if (utzid == preferredID) {
+ // Use country for the location
+ locationPart = &country;
+ } else {
+ // Use city for the location
+ locationPart = &city;
+ }
+ // Reset the partial location string array
+ mzPartialLoc[mzPartialLocIdx][0].setTo(mzid);
+ mzPartialLoc[mzPartialLocIdx][1].remove();
+ mzPartialLoc[mzPartialLocIdx][2].remove();
+ mzPartialLoc[mzPartialLocIdx][3].remove();
+
+ if (locationPart != NULL) {
+ FieldPosition fpos;
+ if (!lg.isEmpty()) {
+ Formattable params [] = {
+ Formattable(*locationPart),
+ Formattable(lg)
+ };
+ fallbackFmt->format(params, 2, mzPartialLoc[mzPartialLocIdx][1], fpos, status);
+ }
+ if (!sg.isEmpty()) {
+ Formattable params [] = {
+ Formattable(*locationPart),
+ Formattable(sg)
+ };
+ fallbackFmt->format(params, 2, mzPartialLoc[mzPartialLocIdx][2], fpos, status);
+ if (mzStrings->isShortFormatCommonlyUsed()) {
+ mzPartialLoc[mzPartialLocIdx][3].setTo(gCommonlyUsedTrue, -1);
+ }
+ }
+ if (U_FAILURE(status)) {
+ goto error_cleanup;
+ }
+ }
+ mzPartialLocIdx++;
+ }
+ }
+ }
+ }
+ // Collected names for a zone
+
+ // Create UnicodeString array for localized zone strings
+ int32_t lastIdx = ZSIDX_COUNT - 1;
+ while (lastIdx >= 0) {
+ if (zstrarray[lastIdx] != NULL) {
+ break;
+ }
+ lastIdx--;
+ }
+ UnicodeString *strings = NULL;
+ int32_t stringsCount = lastIdx + 1;
+
+ if (stringsCount > 0) {
+ strings = new UnicodeString[stringsCount];
+ for (int32_t i = 0; i < stringsCount; i++) {
+ if (zstrarray[i] != NULL) {
+ strings[i].setTo(zstrarray[i], -1);
+
+ // Add names to the trie
+ int32_t type = getTimeZoneTranslationType((TimeZoneTranslationTypeIndex)i);
+ ZoneStringInfo *zsinfo = new ZoneStringInfo(utzid, strings[i], (TimeZoneTranslationType)type);
+ fZoneStringsTrie->put(strings[i], zsinfo, status);
+ if (U_FAILURE(status)) {
+ delete zsinfo;
+ delete[] strings;
+ goto error_cleanup;
+ }
+ }
+ }
+ }
+
+ // Create UnicodeString array for generic partial location strings
+ UnicodeString **genericPartialLocationNames = NULL;
+ int32_t genericPartialRowCount = mzPartialLocIdx;
+ int32_t genericPartialColCount = 4;
+
+ if (genericPartialRowCount != 0) {
+ genericPartialLocationNames = (UnicodeString**)uprv_malloc(genericPartialRowCount * sizeof(UnicodeString*));
+ if (genericPartialLocationNames == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ delete[] strings;
+ goto error_cleanup;
+ }
+ for (int32_t i = 0; i < genericPartialRowCount; i++) {
+ genericPartialLocationNames[i] = new UnicodeString[genericPartialColCount];
+ for (int32_t j = 0; j < genericPartialColCount; j++) {
+ genericPartialLocationNames[i][j].setTo(mzPartialLoc[i][j]);
+ // Add names to the trie
+ if ((j == 1 || j == 2) &&!genericPartialLocationNames[i][j].isEmpty()) {
+ ZoneStringInfo *zsinfo;
+ TimeZoneTranslationType type = (j == 1) ? GENERIC_LONG : GENERIC_SHORT;
+ zsinfo = new ZoneStringInfo(utzid, genericPartialLocationNames[i][j], type);
+ fZoneStringsTrie->put(genericPartialLocationNames[i][j], zsinfo, status);
+ if (U_FAILURE(status)) {
+ delete[] genericPartialLocationNames[i];
+ uprv_free(genericPartialLocationNames);
+ delete[] strings;
+ goto error_cleanup;
+ }
+ }
+ }
+ }
+ }
+
+ // Finally, create ZoneStrings instance and put it into the tzidToStinrgs map
+ ZoneStrings *zstrings = new ZoneStrings(strings, stringsCount, commonlyUsed,
+ genericPartialLocationNames, genericPartialRowCount, genericPartialColCount);
+
+ fTzidToStrings->put(utzid, zstrings, status);
+ if (U_FAILURE(status)) {
+ delete zstrings;
+ goto error_cleanup;
+ }
+ ures_close(zoneItem);
+ zoneItem = NULL;
+ }
+
+ delete tzids;
+ delete fallbackFmt;
+ delete regionFmt;
+ ures_close(zoneStringsArray);
+ ures_close(localeBundle);
+
+ return;
+
+error_cleanup:
+ if (fallbackFmt != NULL) {
+ delete fallbackFmt;
+ }
+ if (regionFmt != NULL) {
+ delete regionFmt;
+ }
+ if (tzids != NULL) {
+ delete tzids;
+ }
+ ures_close(metazoneItem);
+ ures_close(zoneItem);
+ ures_close(zoneStringsArray);
+ ures_close(localeBundle);
+
+ delete fTzidToStrings;
+ fTzidToStrings = NULL;
+
+ delete fMzidToStrings;
+ fMzidToStrings = NULL;
+
+ delete fZoneStringsTrie;
+ fZoneStringsTrie = NULL;
+
+ return;
+}
+
+ZoneStringFormat::~ZoneStringFormat() {
+ if (fTzidToStrings != NULL) {
+ delete fTzidToStrings;
+ }
+ if (fMzidToStrings != NULL) {
+ delete fMzidToStrings;
+ }
+ if (fZoneStringsTrie != NULL) {
+ delete fZoneStringsTrie;
+ }
+ return;
+}
+
+SafeZoneStringFormatPtr*
+ZoneStringFormat::getZoneStringFormat(const Locale& locale, UErrorCode &status) {
+ umtx_lock(&gZSFCacheLock);
+ if (gZoneStringFormatCache == NULL) {
+ gZoneStringFormatCache = new ZSFCache(10 /* capacity */);
+ ucln_i18n_registerCleanup(UCLN_I18N_ZSFORMAT, zoneStringFormat_cleanup);
+ }
+ umtx_unlock(&gZSFCacheLock);
+
+ return gZoneStringFormatCache->get(locale, status);
+}
+
+
+UnicodeString**
+ZoneStringFormat::createZoneStringsArray(UDate date, int32_t &rowCount, int32_t &colCount, UErrorCode &status) const {
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ UnicodeString **result = NULL;
+ rowCount = 0;
+ colCount = 0;
+
+ // Collect canonical time zone IDs
+ UVector canonicalIDs(uhash_deleteUnicodeString, uhash_compareUnicodeString, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ StringEnumeration *tzids = TimeZone::createEnumeration();
+ const char *tzid;
+ while ((tzid = tzids->next(NULL, status))) {
+ if (U_FAILURE(status)) {
+ delete tzids;
+ return NULL;
+ }
+ UnicodeString utzid(tzid);
+ UnicodeString canonicalID;
+ ZoneMeta::getCanonicalID(UnicodeString(tzid), canonicalID);
+ if (utzid == canonicalID) {
+ canonicalIDs.addElement(new UnicodeString(utzid), status);
+ if (U_FAILURE(status)) {
+ delete tzids;
+ return NULL;
+ }
+ }
+ }
+ delete tzids;
+
+ // Allocate array
+ result = (UnicodeString**)uprv_malloc(canonicalIDs.size() * sizeof(UnicodeString*));
+ if (result == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
+ }
+ for (int32_t i = 0; i < canonicalIDs.size(); i++) {
+ result[i] = new UnicodeString[8];
+ UnicodeString *id = (UnicodeString*)canonicalIDs.elementAt(i);
+ result[i][0].setTo(*id);
+ getLongStandard(*id, date, result[i][1]);
+ getShortStandard(*id, date, FALSE, result[i][2]);
+ getLongDaylight(*id, date, result[i][3]);
+ getShortDaylight(*id, date, FALSE, result[i][4]);
+ getGenericLocation(*id, result[i][5]);
+ getLongGenericNonLocation(*id, date, result[i][6]);
+ getShortGenericNonLocation(*id, date, FALSE, result[i][7]);
+ }
+
+ rowCount = canonicalIDs.size();
+ colCount = 8;
+ return result;
+}
+
+UnicodeString&
+ZoneStringFormat::getSpecificLongString(const Calendar &cal, UnicodeString &result,
+ UErrorCode &status) const {
+ result.remove();
+ if (U_FAILURE(status)) {
+ return result;
+ }
+ UnicodeString tzid;
+ cal.getTimeZone().getID(tzid);
+ UDate date = cal.getTime(status);
+ if (cal.get(UCAL_DST_OFFSET, status) == 0) {
+ return getString(tzid, ZSIDX_LONG_STANDARD, date, FALSE /*not used*/, result);
+ } else {
+ return getString(tzid, ZSIDX_LONG_DAYLIGHT, date, FALSE /*not used*/, result);
+ }
+}
+
+UnicodeString&
+ZoneStringFormat::getSpecificShortString(const Calendar &cal, UBool commonlyUsedOnly,
+ UnicodeString &result, UErrorCode &status) const {
+ result.remove();
+ if (U_FAILURE(status)) {
+ return result;
+ }
+ UnicodeString tzid;
+ cal.getTimeZone().getID(tzid);
+ UDate date = cal.getTime(status);
+ if (cal.get(UCAL_DST_OFFSET, status) == 0) {
+ return getString(tzid, ZSIDX_SHORT_STANDARD, date, commonlyUsedOnly, result);
+ } else {
+ return getString(tzid, ZSIDX_SHORT_DAYLIGHT, date, commonlyUsedOnly, result);
+ }
+}
+
+UnicodeString&
+ZoneStringFormat::getGenericLongString(const Calendar &cal, UnicodeString &result,
+ UErrorCode &status) const {
+ return getGenericString(cal, FALSE /*long*/, FALSE /* not used */, result, status);
+}
+
+UnicodeString&
+ZoneStringFormat::getGenericShortString(const Calendar &cal, UBool commonlyUsedOnly,
+ UnicodeString &result, UErrorCode &status) const {
+ return getGenericString(cal, TRUE /*short*/, commonlyUsedOnly, result, status);
+}
+
+UnicodeString&
+ZoneStringFormat::getGenericLocationString(const Calendar &cal, UnicodeString &result,
+ UErrorCode &status) const {
+ UnicodeString tzid;
+ cal.getTimeZone().getID(tzid);
+ UDate date = cal.getTime(status);
+ return getString(tzid, ZSIDX_LOCATION, date, FALSE /*not used*/, result);
+}
+
+const ZoneStringInfo*
+ZoneStringFormat::findSpecificLong(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const {
+ return find(text, start, STANDARD_LONG | DAYLIGHT_LONG, matchLength, status);
+}
+
+const ZoneStringInfo*
+ZoneStringFormat::findSpecificShort(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const {
+ return find(text, start, STANDARD_SHORT | DAYLIGHT_SHORT, matchLength, status);
+}
+
+const ZoneStringInfo*
+ZoneStringFormat::findGenericLong(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const {
+ return find(text, start, GENERIC_LONG | STANDARD_LONG | LOCATION, matchLength, status);
+}
+
+const ZoneStringInfo*
+ZoneStringFormat::findGenericShort(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const {
+ return find(text, start, GENERIC_SHORT | STANDARD_SHORT | LOCATION, matchLength, status);
+}
+
+const ZoneStringInfo*
+ZoneStringFormat::findGenericLocation(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const {
+ return find(text, start, LOCATION, matchLength, status);
+}
+
+UnicodeString&
+ZoneStringFormat::getString(const UnicodeString &tzid, TimeZoneTranslationTypeIndex typeIdx, UDate date,
+ UBool commonlyUsedOnly, UnicodeString& result) const {
+ result.remove();
+
+ // ICU's own array does not have entries for aliases
+ UnicodeString canonicalID;
+ ZoneMeta::getCanonicalID(tzid, canonicalID);
+
+ if (fTzidToStrings != NULL) {
+ ZoneStrings *zstrings = (ZoneStrings*)fTzidToStrings->get(canonicalID);
+ if (zstrings != NULL) {
+ switch (typeIdx) {
+ case ZSIDX_LONG_STANDARD:
+ case ZSIDX_LONG_DAYLIGHT:
+ case ZSIDX_LONG_GENERIC:
+ case ZSIDX_LOCATION:
+ zstrings->getString(typeIdx, result);
+ break;
+ case ZSIDX_SHORT_STANDARD:
+ case ZSIDX_SHORT_DAYLIGHT:
+ case ZSIDX_SHORT_GENERIC:
+ if (!commonlyUsedOnly || zstrings->isShortFormatCommonlyUsed()) {
+ zstrings->getString(typeIdx, result);
+ }
+ break;
+ }
+ }
+ }
+ if (result.isEmpty() && fMzidToStrings != NULL && typeIdx != ZSIDX_LOCATION) {
+ // Try metazone
+ UnicodeString mzid;
+ ZoneMeta::getMetazoneID(canonicalID, date, mzid);
+ if (!mzid.isEmpty()) {
+ ZoneStrings *mzstrings = (ZoneStrings*)fMzidToStrings->get(mzid);
+ if (mzstrings != NULL) {
+ switch (typeIdx) {
+ case ZSIDX_LONG_STANDARD:
+ case ZSIDX_LONG_DAYLIGHT:
+ case ZSIDX_LONG_GENERIC:
+ case ZSIDX_LOCATION:
+ mzstrings->getString(typeIdx, result);
+ break;
+ case ZSIDX_SHORT_STANDARD:
+ case ZSIDX_SHORT_DAYLIGHT:
+ case ZSIDX_SHORT_GENERIC:
+ if (!commonlyUsedOnly || mzstrings->isShortFormatCommonlyUsed()) {
+ mzstrings->getString(typeIdx, result);
+ }
+ break;
+ }
+ }
+ }
+ }
+ return result;
+}
+
+UnicodeString&
+ZoneStringFormat::getGenericString(const Calendar &cal, UBool isShort, UBool commonlyUsedOnly,
+ UnicodeString &result, UErrorCode &status) const {
+ result.remove();
+ UDate time = cal.getTime(status);
+ if (U_FAILURE(status)) {
+ return result;
+ }
+ const TimeZone &tz = cal.getTimeZone();
+ UnicodeString tzid;
+ tz.getID(tzid);
+
+ // ICU's own array does not have entries for aliases
+ UnicodeString canonicalID;
+ ZoneMeta::getCanonicalID(tzid, canonicalID);
+
+ ZoneStrings *zstrings;
+ if (fTzidToStrings != NULL) {
+ zstrings = (ZoneStrings*)fTzidToStrings->get(canonicalID);
+ if (zstrings != NULL) {
+ if (isShort) {
+ if (!commonlyUsedOnly || zstrings->isShortFormatCommonlyUsed()) {
+ zstrings->getString(ZSIDX_SHORT_GENERIC, result);
+ }
+ } else {
+ zstrings->getString(ZSIDX_LONG_GENERIC, result);
+ }
+ }
+ }
+ if (result.isEmpty() && fMzidToStrings != NULL) {
+ // try metazone
+ int32_t raw, sav;
+ UnicodeString mzid;
+ ZoneMeta::getMetazoneID(canonicalID, time, mzid);
+ if (!mzid.isEmpty()) {
+ UBool useStandard = FALSE;
+ sav = cal.get(UCAL_DST_OFFSET, status);
+ if (U_FAILURE(status)) {
+ return result;
+ }
+ if (sav == 0) {
+ useStandard = TRUE;
+ // Check if the zone actually uses daylight saving time around the time
+ TimeZone *tmptz = tz.clone();
+ BasicTimeZone *btz = NULL;
+ if (tmptz->getDynamicClassID() == OlsonTimeZone::getStaticClassID()
+ || tmptz->getDynamicClassID() == SimpleTimeZone::getStaticClassID()
+ || tmptz->getDynamicClassID() == RuleBasedTimeZone::getStaticClassID()
+ || tmptz->getDynamicClassID() == VTimeZone::getStaticClassID()) {
+ btz = (BasicTimeZone*)tmptz;
+ }
+
+ if (btz != NULL) {
+ TimeZoneTransition before;
+ UBool beforTrs = btz->getPreviousTransition(time, TRUE, before);
+ if (beforTrs
+ && (time - before.getTime() < kDstCheckRange)
+ && before.getFrom()->getDSTSavings() != 0) {
+ useStandard = FALSE;
+ } else {
+ TimeZoneTransition after;
+ UBool afterTrs = btz->getNextTransition(time, FALSE, after);
+ if (afterTrs
+ && (after.getTime() - time < kDstCheckRange)
+ && after.getTo()->getDSTSavings() != 0) {
+ useStandard = FALSE;
+ }
+ }
+ } else {
+ // If not BasicTimeZone... only if the instance is not an ICU's implementation.
+ // We may get a wrong answer in edge case, but it should practically work OK.
+ tmptz->getOffset(time - kDstCheckRange, FALSE, raw, sav, status);
+ if (sav != 0) {
+ useStandard = FALSE;
+ } else {
+ tmptz->getOffset(time + kDstCheckRange, FALSE, raw, sav, status);
+ if (sav != 0){
+ useStandard = FALSE;
+ }
+ }
+ if (U_FAILURE(status)) {
+ delete tmptz;
+ result.remove();
+ return result;
+ }
+ }
+ delete tmptz;
+ }
+ if (useStandard) {
+ getString(canonicalID, (isShort ? ZSIDX_SHORT_STANDARD : ZSIDX_LONG_STANDARD),
+ time, commonlyUsedOnly, result);
+
+ // Note:
+ // In CLDR 1.5.1, a same localization is used for both generic and standard
+ // for some metazones in some locales. This is actually data bugs and should
+ // be resolved in later versions of CLDR. For now, we check if the standard
+ // name is different from its generic name below.
+ if (!result.isEmpty()) {
+ UnicodeString genericNonLocation;
+ getString(canonicalID, (isShort ? ZSIDX_SHORT_GENERIC : ZSIDX_LONG_GENERIC),
+ time, commonlyUsedOnly, genericNonLocation);
+ if (!genericNonLocation.isEmpty() && result == genericNonLocation) {
+ result.remove();
+ }
+ }
+ }
+ if (result.isEmpty()) {
+ ZoneStrings *mzstrings = (ZoneStrings*)fMzidToStrings->get(mzid);
+ if (mzstrings != NULL) {
+ if (isShort) {
+ if (!commonlyUsedOnly || mzstrings->isShortFormatCommonlyUsed()) {
+ mzstrings->getString(ZSIDX_SHORT_GENERIC, result);
+ }
+ } else {
+ mzstrings->getString(ZSIDX_LONG_GENERIC, result);
+ }
+ }
+ if (!result.isEmpty()) {
+ // Check if the offsets at the given time matches the preferred zone's offsets
+ UnicodeString preferredId;
+ UnicodeString region;
+ ZoneMeta::getZoneIdByMetazone(mzid, getRegion(region), preferredId);
+ if (canonicalID != preferredId) {
+ // Check if the offsets at the given time are identical with the preferred zone
+ raw = cal.get(UCAL_ZONE_OFFSET, status);
+ if (U_FAILURE(status)) {
+ result.remove();
+ return result;
+ }
+ TimeZone *preferredZone = TimeZone::createTimeZone(preferredId);
+ int32_t prfRaw, prfSav;
+ // Check offset in preferred time zone with wall time.
+ // With getOffset(time, false, preferredOffsets),
+ // you may get incorrect results because of time overlap at DST->STD
+ // transition.
+ preferredZone->getOffset(time + raw + sav, TRUE, prfRaw, prfSav, status);
+ delete preferredZone;
+
+ if (U_FAILURE(status)) {
+ result.remove();
+ return result;
+ }
+ if ((raw != prfRaw || sav != prfSav) && zstrings != NULL) {
+ // Use generic partial location string as fallback
+ zstrings->getGenericPartialLocationString(mzid, isShort, commonlyUsedOnly, result);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (result.isEmpty()) {
+ // Use location format as the final fallback
+ getString(canonicalID, ZSIDX_LOCATION, time, FALSE /*not used*/, result);
+ }
+
+ return result;
+}
+
+UnicodeString&
+ZoneStringFormat::getGenericPartialLocationString(const UnicodeString &tzid, UBool isShort,
+ UDate date, UBool commonlyUsedOnly, UnicodeString &result) const {
+ result.remove();
+ if (fTzidToStrings == NULL) {
+ return result;
+ }
+
+ UnicodeString canonicalID;
+ ZoneMeta::getCanonicalID(tzid, canonicalID);
+
+ UnicodeString mzid;
+ ZoneMeta::getMetazoneID(canonicalID, date, mzid);
+
+ if (!mzid.isEmpty()) {
+ ZoneStrings *zstrings = (ZoneStrings*)fTzidToStrings->get(canonicalID);
+ if (zstrings != NULL) {
+ zstrings->getGenericPartialLocationString(mzid, isShort, commonlyUsedOnly, result);
+ }
+ }
+ return result;
+}
+
+const ZoneStringInfo*
+ZoneStringFormat::find(const UnicodeString &text, int32_t start, int32_t types,
+ int32_t &matchLength, UErrorCode &status) const {
+ matchLength = 0;
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ if (fZoneStringsTrie == NULL) {
+ return NULL;
+ }
+ const ZoneStringInfo *result = NULL;
+ const ZoneStringInfo *fallback = NULL;
+ int32_t fallbackMatchLen = 0;
+
+ ZoneStringSearchResultHandler handler;
+ fZoneStringsTrie->search(text, start, (TextTrieMapSearchResultHandler*)&handler, status);
+ if (U_SUCCESS(status)) {
+ int32_t numMatches = handler.countMatches();
+ for (int32_t i = 0; i < numMatches; i++) {
+ int32_t tmpMatchLen;
+ const ZoneStringInfo *tmp = handler.getMatch(i, tmpMatchLen);
+ if ((types & tmp->fType) != 0) {
+ if (result == NULL || matchLength < tmpMatchLen) {
+ result = tmp;
+ matchLength = tmpMatchLen;
+ } else if (matchLength == tmpMatchLen) {
+ // Tie breaker - there are some examples that a
+ // long standard name is identical with a location
+ // name - for example, "Uruguay Time". In this case,
+ // we interpret it as generic, not specific.
+ if (tmp->isGeneric() && !result->isGeneric()) {
+ result = tmp;
+ }
+ }
+ } else if (result == NULL) {
+ if (fallback == NULL || fallbackMatchLen < tmpMatchLen) {
+ fallback = tmp;
+ fallbackMatchLen = tmpMatchLen;
+ } else if (fallbackMatchLen == tmpMatchLen) {
+ if (tmp->isGeneric() && !fallback->isGeneric()) {
+ fallback = tmp;
+ }
+ }
+ }
+ }
+ if (result == NULL && fallback != NULL) {
+ result = fallback;
+ matchLength = fallbackMatchLen;
+ }
+ }
+ return result;
+}
+
+
+UnicodeString&
+ZoneStringFormat::getRegion(UnicodeString ®ion) const {
+ const char* country = fLocale.getCountry();
+ // TODO: Utilize addLikelySubtag in Locale to resolve default region
+ // when the implementation is ready.
+ region.setTo(country, -1);
+ return region;
+}
+
+MessageFormat*
+ZoneStringFormat::getFallbackFormat(const Locale &locale, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ UnicodeString pattern(gDefFallbackPattern);
+ UResourceBundle *localeBundle = ures_open(NULL, locale.getName(), &status);
+ UResourceBundle *zoneStringsArray = ures_getByKeyWithFallback(localeBundle, gZoneStringsTag, NULL, &status);
+ int32_t len;
+ const UChar *flbkfmt = ures_getStringByKeyWithFallback(zoneStringsArray, gFallbackFormatTag, &len, &status);
+ if (U_SUCCESS(status)) {
+ pattern.setTo(flbkfmt);
+ } else {
+ status = U_ZERO_ERROR;
+ }
+ ures_close(zoneStringsArray);
+ ures_close(localeBundle);
+
+ MessageFormat *fallbackFmt = new MessageFormat(pattern, status);
+ return fallbackFmt;
+}
+
+MessageFormat*
+ZoneStringFormat::getRegionFormat(const Locale& locale, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ UnicodeString pattern(gDefRegionPattern);
+ UResourceBundle *localeBundle = ures_open(NULL, locale.getName(), &status);
+ UResourceBundle *zoneStringsArray = ures_getByKeyWithFallback(localeBundle, gZoneStringsTag, NULL, &status);
+ int32_t len;
+ const UChar *regionfmt = ures_getStringByKeyWithFallback(zoneStringsArray, gRegionFormatTag, &len, &status);
+ if (U_SUCCESS(status)) {
+ pattern.setTo(regionfmt);
+ } else {
+ status = U_ZERO_ERROR;
+ }
+ ures_close(zoneStringsArray);
+ ures_close(localeBundle);
+
+ MessageFormat *regionFmt = new MessageFormat(pattern, status);
+ return regionFmt;
+}
+
+const UChar*
+ZoneStringFormat::getZoneStringFromBundle(const UResourceBundle *zoneitem, const char *key) {
+ const UChar *str = NULL;
+ if (zoneitem != NULL) {
+ UErrorCode status = U_ZERO_ERROR;
+ int32_t len;
+ str = ures_getStringByKeyWithFallback(zoneitem, key, &len, &status);
+ if (U_FAILURE(status)) {
+ str = NULL;
+ }
+ }
+ return str;
+}
+
+UBool
+ZoneStringFormat::isCommonlyUsed(const UResourceBundle *zoneitem) {
+ if (zoneitem == NULL) {
+ return TRUE;
+ }
+
+ UBool commonlyUsed = FALSE;
+ UErrorCode status = U_ZERO_ERROR;
+ UResourceBundle *cuRes = ures_getByKey(zoneitem, gCommonlyUsedTag, NULL, &status);
+ int32_t cuValue = ures_getInt(cuRes, &status);
+ if (U_SUCCESS(status)) {
+ if (cuValue == 1) {
+ commonlyUsed = TRUE;
+ }
+ }
+ ures_close(cuRes);
+ return commonlyUsed;
+}
+
+UnicodeString&
+ZoneStringFormat::getLocalizedCountry(const UnicodeString &countryCode, const Locale &locale, UnicodeString &displayCountry) {
+ // We do not want to use display country names only from the target language bundle
+ // Note: we should do this in better way.
+ displayCountry.remove();
+ int32_t ccLen = countryCode.length();
+ if (ccLen > 0 && ccLen < ULOC_COUNTRY_CAPACITY) {
+ UErrorCode status = U_ZERO_ERROR;
+ UResourceBundle *localeBundle = ures_open(NULL, locale.getName(), &status);
+ if (U_SUCCESS(status)) {
+ const char *bundleLocStr = ures_getLocale(localeBundle, &status);
+ if (U_SUCCESS(status) && uprv_strlen(bundleLocStr) > 0) {
+ Locale bundleLoc(bundleLocStr);
+ if (uprv_strcmp(bundleLocStr, "root") != 0 && uprv_strcmp(bundleLoc.getLanguage(), locale.getLanguage()) == 0) {
+ // Create a fake locale strings
+ char tmpLocStr[ULOC_COUNTRY_CAPACITY + 3];
+ uprv_strcpy(tmpLocStr, "xx_");
+ u_UCharsToChars(countryCode.getBuffer(), &tmpLocStr[3], ccLen);
+ tmpLocStr[3 + ccLen] = 0;
+
+ Locale tmpLoc(tmpLocStr);
+ tmpLoc.getDisplayCountry(locale, displayCountry);
+ }
+ }
+ }
+ ures_close(localeBundle);
+ }
+ if (displayCountry.isEmpty()) {
+ // Use the country code as the fallback
+ displayCountry.setTo(countryCode);
+ }
+ return displayCountry;
+}
+
+// ----------------------------------------------------------------------------
+/*
+ * This constructor adopts the input UnicodeString arrays.
+ */
+ZoneStrings::ZoneStrings(UnicodeString *strings, int32_t stringsCount, UBool commonlyUsed,
+ UnicodeString **genericPartialLocationStrings, int32_t genericRowCount, int32_t genericColCount)
+: UMemory(), fStrings(strings), fStringsCount(stringsCount), fIsCommonlyUsed(commonlyUsed),
+ fGenericPartialLocationStrings(genericPartialLocationStrings),
+ fGenericPartialLocationRowCount(genericRowCount), fGenericPartialLocationColCount(genericColCount) {
+}
+
+ZoneStrings::~ZoneStrings() {
+ if (fStrings != NULL) {
+ delete[] fStrings;
+ }
+ if (fGenericPartialLocationStrings != NULL) {
+ for (int32_t i = 0; i < fGenericPartialLocationRowCount; i++) {
+ delete[] fGenericPartialLocationStrings[i];
+ }
+ uprv_free(fGenericPartialLocationStrings);
+ }
+}
+
+
+UnicodeString&
+ZoneStrings::getString(int32_t typeIdx, UnicodeString &result) const {
+ if (typeIdx >= 0 && typeIdx < fStringsCount) {
+ result.setTo(fStrings[typeIdx]);
+ } else {
+ result.remove();
+ }
+ return result;
+}
+
+UnicodeString&
+ZoneStrings::getGenericPartialLocationString(const UnicodeString &mzid, UBool isShort,
+ UBool commonlyUsedOnly, UnicodeString &result) const {
+ UBool isSet = FALSE;
+ if (fGenericPartialLocationColCount >= 2) {
+ for (int32_t i = 0; i < fGenericPartialLocationRowCount; i++) {
+ if (fGenericPartialLocationStrings[i][0] == mzid) {
+ if (isShort) {
+ if (fGenericPartialLocationColCount >= 3) {
+ if (!commonlyUsedOnly ||
+ fGenericPartialLocationColCount == 3 || fGenericPartialLocationStrings[i][3].length() != 0) {
+ result.setTo(fGenericPartialLocationStrings[i][2]);
+ isSet = TRUE;
+ }
+ }
+ } else {
+ result.setTo(fGenericPartialLocationStrings[i][1]);
+ isSet = TRUE;
+ }
+ break;
+ }
+ }
+ }
+ if (!isSet) {
+ result.remove();
+ }
+ return result;
+}
+
+// --------------------------------------------------------------
+SafeZoneStringFormatPtr::SafeZoneStringFormatPtr(ZSFCacheEntry *cacheEntry)
+: UMemory(), fCacheEntry(cacheEntry) {
+}
+
+SafeZoneStringFormatPtr::~SafeZoneStringFormatPtr() {
+ fCacheEntry->delRef();
+}
+
+const ZoneStringFormat*
+SafeZoneStringFormatPtr::get() const {
+ return fCacheEntry->getZoneStringFormat();
+}
+
+ZSFCacheEntry::ZSFCacheEntry(const Locale &locale, ZoneStringFormat *zsf, ZSFCacheEntry *next)
+: UMemory(), fRefCount(1), fLocale(locale),
+ fZoneStringFormat(zsf), fNext(next) {
+}
+
+ZSFCacheEntry::~ZSFCacheEntry () {
+ delete fZoneStringFormat;
+}
+
+const ZoneStringFormat*
+ZSFCacheEntry::getZoneStringFormat(void) {
+ return (const ZoneStringFormat*)fZoneStringFormat;
+}
+
+void
+ZSFCacheEntry::delRef(void) {
+ umtx_lock(&gZSFCacheLock);
+ --fRefCount;
+ umtx_unlock(&gZSFCacheLock);
+}
+
+ZSFCache::ZSFCache(int32_t capacity)
+: UMemory(), fCapacity(capacity), fFirst(NULL) {
+}
+
+ZSFCache::~ZSFCache() {
+ ZSFCacheEntry *entry = fFirst;
+ while (entry) {
+ ZSFCacheEntry *next = entry->fNext;
+ delete entry;
+ entry = next;
+ }
+}
+
+SafeZoneStringFormatPtr*
+ZSFCache::get(const Locale &locale, UErrorCode &status) {
+ SafeZoneStringFormatPtr *result = NULL;
+
+ // Search the cache entry list
+ ZSFCacheEntry *entry = NULL;
+ ZSFCacheEntry *next, *prev;
+
+ umtx_lock(&gZSFCacheLock);
+ entry = fFirst;
+ prev = NULL;
+ while (entry) {
+ next = entry->fNext;
+ if (entry->fLocale == locale) {
+ // Add reference count
+ entry->fRefCount++;
+
+ // move the entry to the top
+ if (prev != NULL) {
+ prev->fNext = next;
+ }
+ fFirst = entry;
+ break;
+ }
+ prev = entry;
+ entry = next;
+ }
+ umtx_unlock(&gZSFCacheLock);
+
+ // Create a new ZoneStringFormat
+ if (entry == NULL) {
+ ZoneStringFormat *zsf = new ZoneStringFormat(locale, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ // Now add the new entry
+ umtx_lock(&gZSFCacheLock);
+ // Make sure no other threads already creaded the one for the same locale
+ entry = fFirst;
+ prev = NULL;
+ while (entry) {
+ next = entry->fNext;
+ if (entry->fLocale == locale) {
+ // Add reference count
+ entry->fRefCount++;
+
+ // move the entry to the top
+ if (prev != NULL) {
+ prev->fNext = next;
+ }
+ fFirst = entry;
+ break;
+ }
+ prev = entry;
+ entry = next;
+ }
+ if (entry == NULL) {
+ // Add the new one to the top
+ next = fFirst;
+ entry = new ZSFCacheEntry(locale, zsf, next);
+ fFirst = entry;
+ }
+ umtx_unlock(&gZSFCacheLock);
+ }
+
+ result = new SafeZoneStringFormatPtr(entry);
+
+ // Now, delete unused cache entries beyond the capacity
+ umtx_lock(&gZSFCacheLock);
+ entry = fFirst;
+ prev = NULL;
+ int32_t idx = 1;
+ while (entry) {
+ next = entry->fNext;
+ if (idx >= fCapacity && entry->fRefCount == 0) {
+ if (prev->fNext) {
+ prev->fNext = next;
+ }
+ delete entry;
+ } else {
+ prev = entry;
+ }
+ entry = next;
+ idx++;
+ }
+ umtx_unlock(&gZSFCacheLock);
+
+ return result;
+}
+
+U_NAMESPACE_END
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/zstrfmt.h b/icu4c/source/i18n/zstrfmt.h
new file mode 100644
index 0000000..6430034
--- /dev/null
+++ b/icu4c/source/i18n/zstrfmt.h
@@ -0,0 +1,439 @@
+/*
+*******************************************************************************
+* Copyright (C) 2007, International Business Machines Corporation and *
+* others. All Rights Reserved. *
+*******************************************************************************
+*/
+#ifndef ZSTRFMT_H
+#define ZSTRFMT_H
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "unicode/uobject.h"
+#include "unicode/unistr.h"
+#include "unicode/calendar.h"
+#include "hash.h"
+
+U_NAMESPACE_BEGIN
+
+class UVector;
+
+/*
+ * Character node used by TextTrieMap
+ */
+class CharacterNode : public UMemory {
+public:
+ CharacterNode(UChar32 c, UObjectDeleter *fn);
+ virtual ~CharacterNode();
+
+ inline UChar32 getCharacter(void) const;
+ inline UVector* getValues(void) const;
+ inline UVector* getChildNodes(void) const;
+
+ void addValue(void *value, UErrorCode &status);
+ CharacterNode* addChildNode(UChar32 c, UErrorCode &status);
+ CharacterNode* getChildNode(UChar32 c) const;
+
+private:
+ UChar32 fCharacter;
+ UVector *fChildren;
+ UVector *fValues;
+ UObjectDeleter *fValueDeleter;
+};
+
+inline UChar32 CharacterNode::getCharacter(void) const {
+ return fCharacter;
+}
+
+inline UVector* CharacterNode::getValues(void) const {
+ return fValues;
+}
+
+inline UVector* CharacterNode::getChildNodes(void) const {
+ return fChildren;
+}
+
+/*
+ * Search result handler callback interface used by TextTrieMap search.
+ */
+class TextTrieMapSearchResultHandler {
+public:
+ virtual UBool handleMatch(int32_t matchLength,
+ const UVector *values, UErrorCode& status) = 0;
+};
+
+/**
+ * TextTrieMap is a trie implementation for supporting
+ * fast prefix match for the string key.
+ */
+class TextTrieMap : public UMemory {
+public:
+ TextTrieMap(UBool ignoreCase, UObjectDeleter *valueDeleterFunc);
+ virtual ~TextTrieMap();
+
+ void put(const UnicodeString &key, void *value, UErrorCode &status);
+ void search(const UnicodeString &text, int32_t start,
+ TextTrieMapSearchResultHandler *handler, UErrorCode& status) const;
+
+private:
+ UBool fIgnoreCase;
+ UObjectDeleter *fValueDeleter;
+ CharacterNode *fRoot;
+
+ void search(CharacterNode *node, const UnicodeString &text, int32_t start,
+ int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const;
+};
+
+// Name types, these bit flag are used for zone string lookup
+enum TimeZoneTranslationType {
+ LOCATION = 0x0001,
+ GENERIC_LONG = 0x0002,
+ GENERIC_SHORT = 0x0004,
+ STANDARD_LONG = 0x0008,
+ STANDARD_SHORT = 0x0010,
+ DAYLIGHT_LONG = 0x0020,
+ DAYLIGHT_SHORT = 0x0040
+};
+
+// Name type index, these constants are used for index in the zone strings array.
+enum TimeZoneTranslationTypeIndex {
+ ZSIDX_LOCATION = 0,
+ ZSIDX_LONG_STANDARD,
+ ZSIDX_SHORT_STANDARD,
+ ZSIDX_LONG_DAYLIGHT,
+ ZSIDX_SHORT_DAYLIGHT,
+ ZSIDX_LONG_GENERIC,
+ ZSIDX_SHORT_GENERIC,
+
+ ZSIDX_COUNT
+};
+
+class MessageFormat;
+
+/*
+ * ZoneStringInfo is a class holding a localized zone string
+ * information.
+ */
+class ZoneStringInfo : public UMemory {
+public:
+ virtual ~ZoneStringInfo();
+
+ inline UnicodeString& getID(UnicodeString &result) const;
+ inline UnicodeString& getString(UnicodeString &result) const;
+ inline UBool isStandard(void) const;
+ inline UBool isDaylight(void) const;
+ inline UBool isGeneric(void) const;
+
+private:
+ friend class ZoneStringFormat;
+ friend class ZoneStringSearchResultHandler;
+
+ ZoneStringInfo(const UnicodeString &id, const UnicodeString &str, TimeZoneTranslationType type);
+
+ UnicodeString fId;
+ UnicodeString fStr;
+ TimeZoneTranslationType fType;
+};
+
+inline UnicodeString& ZoneStringInfo::getID(UnicodeString &result) const {
+ return result.setTo(fId);
+}
+
+inline UnicodeString& ZoneStringInfo::getString(UnicodeString &result) const {
+ return result.setTo(fStr);
+}
+
+inline UBool ZoneStringInfo::isStandard(void) const {
+ return (fType == STANDARD_LONG || fType == STANDARD_SHORT);
+}
+
+inline UBool ZoneStringInfo::isDaylight(void) const {
+ return (fType == DAYLIGHT_LONG || fType == DAYLIGHT_SHORT);
+}
+
+inline UBool ZoneStringInfo::isGeneric(void) const {
+ return (fType == LOCATION || fType == GENERIC_LONG || fType == GENERIC_SHORT);
+}
+
+class SafeZoneStringFormatPtr;
+
+class ZoneStringFormat : public UMemory {
+public:
+ ZoneStringFormat(const UnicodeString* const* strings, int32_t rowCount, int32_t columnCount, UErrorCode &status);
+ ZoneStringFormat(const Locale& locale, UErrorCode &status);
+ virtual ~ZoneStringFormat();
+
+ static SafeZoneStringFormatPtr* getZoneStringFormat(const Locale& locale, UErrorCode &status);
+
+ /*
+ * Create a snapshot of old zone strings array for the given date
+ */
+ UnicodeString** createZoneStringsArray(UDate date, int32_t &rowCount, int32_t &colCount, UErrorCode &status) const;
+
+ const UnicodeString** getZoneStrings(int32_t &rowCount, int32_t &columnCount) const;
+
+ UnicodeString& getSpecificLongString(const Calendar &cal,
+ UnicodeString &result, UErrorCode &status) const;
+
+ UnicodeString& getSpecificShortString(const Calendar &cal,
+ UBool commonlyUsedOnly, UnicodeString &result, UErrorCode &status) const;
+
+ UnicodeString& getGenericLongString(const Calendar &cal,
+ UnicodeString &result, UErrorCode &status) const;
+
+ UnicodeString& getGenericShortString(const Calendar &cal,
+ UBool commonlyUsedOnly, UnicodeString &result, UErrorCode &status) const;
+
+ UnicodeString& getGenericLocationString(const Calendar &cal,
+ UnicodeString &result, UErrorCode &status) const;
+
+ const ZoneStringInfo* findSpecificLong(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const;
+ const ZoneStringInfo* findSpecificShort(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const;
+ const ZoneStringInfo* findGenericLong(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const;
+ const ZoneStringInfo* findGenericShort(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const;
+ const ZoneStringInfo* findGenericLocation(const UnicodeString &text, int32_t start,
+ int32_t &matchLength, UErrorCode &status) const;
+
+ // Following APIs are not used by SimpleDateFormat, but public for testing purpose
+ inline UnicodeString& getLongStandard(const UnicodeString &tzid, UDate date,
+ UnicodeString &result) const;
+ inline UnicodeString& getLongDaylight(const UnicodeString &tzid, UDate date,
+ UnicodeString &result) const;
+ inline UnicodeString& getLongGenericNonLocation(const UnicodeString &tzid, UDate date,
+ UnicodeString &result) const;
+ inline UnicodeString& getLongGenericPartialLocation(const UnicodeString &tzid, UDate date,
+ UnicodeString &result) const;
+ inline UnicodeString& getShortStandard(const UnicodeString &tzid, UDate date, UBool commonlyUsedOnly,
+ UnicodeString &result) const;
+ inline UnicodeString& getShortDaylight(const UnicodeString &tzid, UDate date, UBool commonlyUsedOnly,
+ UnicodeString &result) const;
+ inline UnicodeString& getShortGenericNonLocation(const UnicodeString &tzid, UDate date, UBool commonlyUsedOnly,
+ UnicodeString &result) const;
+ inline UnicodeString& getShortGenericPartialLocation(const UnicodeString &tzid, UDate date, UBool commonlyUsedOnly,
+ UnicodeString &result) const;
+ inline UnicodeString& getGenericLocation(const UnicodeString &tzid, UnicodeString &result) const;
+
+private:
+ Locale fLocale;
+ Hashtable *fTzidToStrings;
+ Hashtable *fMzidToStrings;
+ TextTrieMap *fZoneStringsTrie;
+
+ /*
+ * Private method to get a zone string except generic partial location types.
+ */
+ UnicodeString& getString(const UnicodeString &tzid, TimeZoneTranslationTypeIndex typeIdx, UDate date,
+ UBool commonlyUsedOnly, UnicodeString& result) const;
+
+ /*
+ * Private method to get a generic string, with fallback logic involved,
+ * that is,
+ *
+ * 1. If a generic non-location string is avaiable for the zone, return it.
+ * 2. If a generic non-location string is associated with a metazone and
+ * the zone never use daylight time around the given date, use the standard
+ * string (if available).
+ *
+ * Note: In CLDR1.5.1, the same localization is used for generic and standard.
+ * In this case, we do not use the standard string and do the rest.
+ *
+ * 3. If a generic non-location string is associated with a metazone and
+ * the offset at the given time is different from the preferred zone for the
+ * current locale, then return the generic partial location string (if avaiable)
+ * 4. If a generic non-location string is not available, use generic location
+ * string.
+ */
+ UnicodeString& getGenericString(const Calendar &cal, UBool isShort, UBool commonlyUsedOnly,
+ UnicodeString &result, UErrorCode &status) const;
+
+ /*
+ * Private method to get a generic partial location string
+ */
+ UnicodeString& getGenericPartialLocationString(const UnicodeString &tzid, UBool isShort,
+ UDate date, UBool commonlyUsedOnly, UnicodeString &result) const;
+
+ /*
+ * Find a prefix matching time zone for the given zone string types.
+ * @param text The text contains a time zone string
+ * @param start The start index within the text
+ * @param types The bit mask representing a set of requested types
+ * @param matchLength Receives the match length
+ * @param status
+ * @return If any zone string matched for the requested types, returns a
+ * ZoneStringInfo for the longest match. If no matches are found for
+ * the requested types, returns a ZoneStringInfo for the longest match
+ * for any other types. If nothing matches at all, returns null.
+ */
+ const ZoneStringInfo* find(const UnicodeString &text, int32_t start, int32_t types,
+ int32_t &matchLength, UErrorCode &status) const;
+
+ UnicodeString& getRegion(UnicodeString ®ion) const;
+
+ static MessageFormat* getFallbackFormat(const Locale &locale, UErrorCode &status);
+ static MessageFormat* getRegionFormat(const Locale &locale, UErrorCode &status);
+ static const UChar* getZoneStringFromBundle(const UResourceBundle *zoneitem, const char *key);
+ static UBool isCommonlyUsed(const UResourceBundle *zoneitem);
+ static UnicodeString& getLocalizedCountry(const UnicodeString &countryCode, const Locale &locale,
+ UnicodeString &displayCountry);
+};
+
+inline UnicodeString&
+ZoneStringFormat::getLongStandard(const UnicodeString &tzid, UDate date,
+ UnicodeString &result) const {
+ return getString(tzid, ZSIDX_LONG_STANDARD, date, FALSE /* not used */, result);
+}
+
+inline UnicodeString&
+ZoneStringFormat::getLongDaylight(const UnicodeString &tzid, UDate date,
+ UnicodeString &result) const {
+ return getString(tzid, ZSIDX_LONG_DAYLIGHT, date, FALSE /* not used */, result);
+}
+
+inline UnicodeString&
+ZoneStringFormat::getLongGenericNonLocation(const UnicodeString &tzid, UDate date,
+ UnicodeString &result) const {
+ return getString(tzid, ZSIDX_LONG_GENERIC, date, FALSE /* not used */, result);
+}
+
+inline UnicodeString&
+ZoneStringFormat::getLongGenericPartialLocation(const UnicodeString &tzid, UDate date,
+ UnicodeString &result) const {
+ return getGenericPartialLocationString(tzid, FALSE, date, FALSE /* not used */, result);
+}
+
+inline UnicodeString&
+ZoneStringFormat::getShortStandard(const UnicodeString &tzid, UDate date, UBool commonlyUsedOnly,
+ UnicodeString &result) const {
+ return getString(tzid, ZSIDX_SHORT_STANDARD, date, commonlyUsedOnly, result);
+}
+
+inline UnicodeString&
+ZoneStringFormat::getShortDaylight(const UnicodeString &tzid, UDate date, UBool commonlyUsedOnly,
+ UnicodeString &result) const {
+ return getString(tzid, ZSIDX_SHORT_DAYLIGHT, date, commonlyUsedOnly, result);
+}
+
+inline UnicodeString&
+ZoneStringFormat::getShortGenericNonLocation(const UnicodeString &tzid, UDate date, UBool commonlyUsedOnly,
+ UnicodeString &result) const {
+ return getString(tzid, ZSIDX_SHORT_GENERIC, date, commonlyUsedOnly, result);
+}
+
+inline UnicodeString&
+ZoneStringFormat::getShortGenericPartialLocation(const UnicodeString &tzid, UDate date, UBool commonlyUsedOnly,
+ UnicodeString &result) const {
+ return getGenericPartialLocationString(tzid, TRUE, date, commonlyUsedOnly, result);
+}
+
+inline UnicodeString&
+ZoneStringFormat::getGenericLocation(const UnicodeString &tzid, UnicodeString &result) const {
+ return getString(tzid, ZSIDX_LOCATION, 0 /*not used*/, FALSE /*not used*/, result);
+}
+
+
+/*
+ * ZooneStrings is a container of localized zone strings used by ZoneStringFormat
+ */
+class ZoneStrings : public UMemory {
+public:
+ ZoneStrings(UnicodeString *strings, int32_t stringsCount, UBool commonlyUsed,
+ UnicodeString **genericPartialLocationStrings, int32_t genericRowCount, int32_t genericColCount);
+ virtual ~ZoneStrings();
+
+ UnicodeString& getString(int32_t typeIdx, UnicodeString &result) const;
+ inline UBool isShortFormatCommonlyUsed(void) const;
+ UnicodeString& getGenericPartialLocationString(const UnicodeString &mzid, UBool isShort,
+ UBool commonlyUsedOnly, UnicodeString &result) const;
+
+private:
+ UnicodeString *fStrings;
+ int32_t fStringsCount;
+ UBool fIsCommonlyUsed;
+ UnicodeString **fGenericPartialLocationStrings;
+ int32_t fGenericPartialLocationRowCount;
+ int32_t fGenericPartialLocationColCount;
+};
+
+inline UBool
+ZoneStrings::isShortFormatCommonlyUsed(void) const {
+ return fIsCommonlyUsed;
+}
+
+/*
+ * ZoneStringSearchResultHandler is an implementation of
+ * TextTrieMapSearchHandler. This class is used by ZoneStringFormat
+ * for collecting search results for localized zone strings.
+ */
+class ZoneStringSearchResultHandler : public UMemory, TextTrieMapSearchResultHandler {
+public:
+ ZoneStringSearchResultHandler();
+ virtual ~ZoneStringSearchResultHandler();
+
+ virtual UBool handleMatch(int32_t matchLength, const UVector *values, UErrorCode &status);
+ int32_t countMatches(void);
+ const ZoneStringInfo* getMatch(int32_t index, int32_t &matchLength);
+ void clear(void);
+
+private:
+ UVector *fResults;
+ int32_t fMatchLen[ZSIDX_COUNT];
+};
+
+
+/*
+ * ZoneStringFormat cache implementation
+ */
+class ZSFCacheEntry : public UMemory {
+public:
+ ~ZSFCacheEntry();
+
+ void delRef(void);
+ const ZoneStringFormat* getZoneStringFormat(void);
+
+private:
+ friend class ZSFCache;
+
+ ZSFCacheEntry(const Locale &locale, ZoneStringFormat *zsf, ZSFCacheEntry *next);
+
+ Locale fLocale;
+ ZoneStringFormat *fZoneStringFormat;
+ ZSFCacheEntry *fNext;
+ int32_t fRefCount;
+};
+
+class SafeZoneStringFormatPtr : public UMemory {
+public:
+ ~SafeZoneStringFormatPtr();
+ const ZoneStringFormat* get() const;
+
+private:
+ friend class ZSFCache;
+
+ SafeZoneStringFormatPtr(ZSFCacheEntry *cacheEntry);
+
+ ZSFCacheEntry *fCacheEntry;
+};
+
+class ZSFCache : public UMemory {
+public:
+ ZSFCache(int32_t capacity);
+ ~ZSFCache();
+
+ SafeZoneStringFormatPtr* get(const Locale &locale, UErrorCode &status);
+
+private:
+ int32_t fCapacity;
+ ZSFCacheEntry *fFirst;
+};
+
+U_NAMESPACE_END
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
+
+#endif // ZSTRFMT_H
diff --git a/icu4c/source/test/intltest/Makefile.in b/icu4c/source/test/intltest/Makefile.in
index 0e6964e..27a0424 100644
--- a/icu4c/source/test/intltest/Makefile.in
+++ b/icu4c/source/test/intltest/Makefile.in
@@ -56,7 +56,7 @@
itrbnf.o itrbnfrt.o itrbnfp.o ucaconf.o icusvtst.o \
uobjtest.o idnaref.o idnaconf.o nptrans.o punyref.o testidn.o testidna.o incaltst.o \
calcasts.o v32test.o uvectest.o textfile.o tokiter.o utxttest.o \
-windttst.o winnmtst.o winutil.o csdetest.o tzrulets.o
+windttst.o winnmtst.o winutil.o csdetest.o tzrulets.o tzoffloc.o tzfmttst.o
DEPS = $(OBJECTS:.o=.d)
diff --git a/icu4c/source/test/intltest/dtfmttst.cpp b/icu4c/source/test/intltest/dtfmttst.cpp
index 34f25a5..82e8018 100644
--- a/icu4c/source/test/intltest/dtfmttst.cpp
+++ b/icu4c/source/test/intltest/dtfmttst.cpp
@@ -69,21 +69,20 @@
TESTCASE(23,TestGreekMay);
TESTCASE(24,TestGenericTime);
TESTCASE(25,TestGenericTimeZoneOrder);
- TESTCASE(26,TestTimeZoneStringsAPI);
- TESTCASE(27,TestHost);
- TESTCASE(28,TestEras);
- TESTCASE(29,TestNarrowNames);
- TESTCASE(30,TestStandAloneDays);
- TESTCASE(31,TestStandAloneMonths);
- TESTCASE(32,TestQuarters);
- TESTCASE(33,TestZTimeZoneParsing);
- TESTCASE(34,TestRelative);
- TESTCASE(35,TestRelativeClone);
- TESTCASE(36,TestHostClone);
- TESTCASE(37,TestTimeZoneDisplayName);
+ TESTCASE(26,TestHost);
+ TESTCASE(27,TestEras);
+ TESTCASE(28,TestNarrowNames);
+ TESTCASE(29,TestStandAloneDays);
+ TESTCASE(30,TestStandAloneMonths);
+ TESTCASE(31,TestQuarters);
+ TESTCASE(32,TestZTimeZoneParsing);
+ TESTCASE(33,TestRelative);
+ TESTCASE(34,TestRelativeClone);
+ TESTCASE(35,TestHostClone);
+ TESTCASE(36,TestTimeZoneDisplayName);
/*
- TESTCASE(38,TestRelativeError);
- TESTCASE(39,TestRelativeOther);
+ TESTCASE(37,TestRelativeError);
+ TESTCASE(38,TestRelativeOther);
*/
default: name = ""; break;
}
@@ -100,7 +99,7 @@
/*
* Computational variables.
*/
- int32_t offset, hours, minutes;
+ int32_t offset, hours, minutes, seconds;
/*
* Instantiate a SimpleDateFormat set up to produce a full time
zone name.
@@ -141,8 +140,12 @@
}
hours = offset/3600000;
minutes = (offset%3600000)/60000;
+ seconds = (offset%60000)/1000;
UnicodeString dstOffset = (UnicodeString)"" + sign + (hours < 10 ? "0" : "") +
(int32_t)hours + ":" + (minutes < 10 ? "0" : "") + (int32_t)minutes;
+ if (seconds != 0) {
+ dstOffset = dstOffset + ":" + (seconds < 10 ? "0" : "") + seconds;
+ }
/*
* Instantiate a date so we can display the time zone name.
*/
@@ -382,7 +385,7 @@
"Wed", "225", "2", "33", "3", "PM", "2", "2", "PDT", "1997", "4", "1997", "2450674", "52452513", "-0700", "PT", "4", "8", "3", "3","PDT",
"Anno Domini", "1997", "August", "0013", "0014", "0014", "0034", "0012", "5130",
- "Wednesday", "0225", "0002", "0033", "0003", "PM", "0002", "0002", "Pacific Daylight Time", "1997", "0004", "1997", "2450674", "52452513", "-0700",
+ "Wednesday", "0225", "0002", "0033", "0003", "PM", "0002", "0002", "Pacific Daylight Time", "1997", "0004", "1997", "2450674", "52452513", "GMT-07:00",
"Pacific Time", "Wednesday", "August", "3rd quarter", "3rd quarter", "United States (Los Angeles)"
};
@@ -1752,7 +1755,7 @@
"y/M/d H:mm zzzz", "F", "2004 01 01 01:00 PST", "2004/1/1 1:00 Pacific Standard Time",
"y/M/d H:mm zzz", "F", "2004 01 01 01:00 PST", "2004/1/1 1:00 PST",
"y/M/d H:mm vvvv", "F", "2004 01 01 01:00 PST", "2004/1/1 1:00 Pacific Time",
- "y/M/d H:mm vvv", "F", "2004 01 01 01:00 PST", "2004/1/1 1:00 PT",
+ "y/M/d H:mm v", "F", "2004 01 01 01:00 PST", "2004/1/1 1:00 PT",
// non-generic timezone string influences dst offset even if wrong for date/time
"y/M/d H:mm zzz", "pf", "2004/1/1 1:00 PDT", "2004 01 01 01:00 PDT", "2004/1/1 0:00 PST",
"y/M/d H:mm vvvv", "pf", "2004/1/1 1:00 PDT", "2004 01 01 01:00 PDT", "2004/1/1 0:00 Pacific Time",
@@ -1765,21 +1768,25 @@
"y/M/d H:mm vvvv", "pf", "2004/7/1 1:00 PT", "2004 07 01 01:00 PDT", "2004/7/1 1:00 Pacific Time",
// daylight savings time transition edge cases.
// time to parse does not really exist, PT interpreted as earlier time
- "y/M/d H:mm zzz", "pf", "2005/4/3 2:30 PT", "2005 04 03 01:30 PST", "2005/4/3 1:30 PST", // adjust earlier
+ "y/M/d H:mm zzz", "pf", "2005/4/3 2:30 PT", "2005 04 03 03:30 PDT", "2005/4/3 3:30 PDT",
"y/M/d H:mm zzz", "pf", "2005/4/3 2:30 PST", "2005 04 03 03:30 PDT", "2005/4/3 3:30 PDT",
"y/M/d H:mm zzz", "pf", "2005/4/3 2:30 PDT", "2005 04 03 01:30 PST", "2005/4/3 1:30 PST",
- "y/M/d H:mm vvv", "pf", "2005/4/3 2:30 PT", "2005 04 03 01:30 PST", "2005/4/3 1:30 PT", // adjust earlier
- "y/M/d H:mm vvv", "pf", "2005/4/3 2:30 PST", "2005 04 03 03:30 PDT", "2005/4/3 3:30 PT",
- "y/M/d H:mm vvv", "pf", "2005/4/3 2:30 PDT", "2005 04 03 01:30 PST", "2005/4/3 1:30 PT",
- "y/M/d H:mm", "pf", "2005/4/3 2:30", "2005 04 03 01:30 PST", "2005/4/3 1:30",
- // time to parse is ambiguous, PT interpreted as LATER time (?)
- "y/M/d H:mm zzz", "pf", "2004/10/31 1:30 PT", "2004 10 31 01:30 PST", "2004/10/31 1:30 PST", // 1:30a PT -> 1:30a PST (later)
+ "y/M/d H:mm v", "pf", "2005/4/3 2:30 PT", "2005 04 03 03:30 PDT", "2005/4/3 3:30 PT",
+ "y/M/d H:mm v", "pf", "2005/4/3 2:30 PST", "2005 04 03 03:30 PDT", "2005/4/3 3:30 PT",
+ "y/M/d H:mm v", "pf", "2005/4/3 2:30 PDT", "2005 04 03 01:30 PST", "2005/4/3 1:30 PT",
+ "y/M/d H:mm", "pf", "2005/4/3 2:30", "2005 04 03 03:30 PDT", "2005/4/3 3:30",
+ // time to parse is ambiguous, PT interpreted as later time
+ "y/M/d H:mm zzz", "pf", "2005/10/30 1:30 PT", "2005 10 30 01:30 PST", "2005/10/30 1:30 PST",
+ "y/M/d H:mm v", "pf", "2005/10/30 1:30 PT", "2005 10 30 01:30 PST", "2005/10/30 1:30 PT",
+ "y/M/d H:mm", "pf", "2005/10/30 1:30 PT", "2005 10 30 01:30 PST", "2005/10/30 1:30",
+
+ "y/M/d H:mm zzz", "pf", "2004/10/31 1:30 PT", "2004 10 31 01:30 PST", "2004/10/31 1:30 PST",
"y/M/d H:mm zzz", "pf", "2004/10/31 1:30 PST", "2004 10 31 01:30 PST", "2004/10/31 1:30 PST",
"y/M/d H:mm zzz", "pf", "2004/10/31 1:30 PDT", "2004 10 31 01:30 PDT", "2004/10/31 1:30 PDT",
- "y/M/d H:mm vvv", "pf", "2004/10/31 1:30 PT", "2004 10 31 01:30 PST", "2004/10/31 1:30 PT", // 1:30a PT -> 1:30a PST (later)
- "y/M/d H:mm vvv", "pf", "2004/10/31 1:30 PST", "2004 10 31 01:30 PST", "2004/10/31 1:30 PT",
- "y/M/d H:mm vvv", "pf", "2004/10/31 1:30 PDT", "2004 10 31 01:30 PDT", "2004/10/31 1:30 PT",
- "y/M/d H:mm", "pf", "2004/10/31 1:30", "2004 10 31 01:30 PST", "2004/10/31 1:30", // 1:30a PT -> 1:30a PST (later)
+ "y/M/d H:mm v", "pf", "2004/10/31 1:30 PT", "2004 10 31 01:30 PST", "2004/10/31 1:30 PT",
+ "y/M/d H:mm v", "pf", "2004/10/31 1:30 PST", "2004 10 31 01:30 PST", "2004/10/31 1:30 PT",
+ "y/M/d H:mm v", "pf", "2004/10/31 1:30 PDT", "2004 10 31 01:30 PDT", "2004/10/31 1:30 PT",
+ "y/M/d H:mm", "pf", "2004/10/31 1:30", "2004 10 31 01:30 PST", "2004/10/31 1:30",
};
const int32_t ZDATA_length = sizeof(ZDATA)/ sizeof(ZDATA[0]);
expect(ZDATA, ZDATA_length, en);
@@ -1864,66 +1871,6 @@
expect(XDATA, XDATA_length, en);
}
-void DateFormatTest::TestTimeZoneStringsAPI() {
- // Verify data
- UErrorCode status = U_ZERO_ERROR;
- DateFormatSymbols symbols(Locale::getUS(), status);
- StringEnumeration* keys = symbols.createZoneStringIDs(status);
- if(U_FAILURE(status)){
- errln("Could not create the StringEnumeration for Locale::getUS(). Error: %s", u_errorName(status));
- return;
- }
-
- StringEnumeration* keys2 = symbols.createZoneStringIDs(status);
- ASSERT_OK(status);
- if(*keys2!=*keys){
- errln("operator!= failed for TimeZoneStringsEnum");
- }
- const UnicodeString* key = NULL;
-
- while( (key = keys->snext(status))!=NULL){
- logln(prettify(*key));
- }
- if(U_FAILURE(status)){
- errln("Could not iterate over the StringEnumeration. Error: %s", u_errorName(status));
- return;
- }
- UnicodeString expectedKey("meta/Alaska");
- UnicodeString expectedStrs[DateFormatSymbols::TIMEZONE_COUNT];
- expectedStrs[DateFormatSymbols::TIMEZONE_SHORT_GENERIC].setTo("AKT");
- expectedStrs[DateFormatSymbols::TIMEZONE_SHORT_STANDARD].setTo("AKST");
- expectedStrs[DateFormatSymbols::TIMEZONE_SHORT_DAYLIGHT].setTo("AKDT");
- expectedStrs[DateFormatSymbols::TIMEZONE_LONG_GENERIC].setTo("Alaska Time");
- expectedStrs[DateFormatSymbols::TIMEZONE_LONG_STANDARD].setTo("Alaska Standard Time");
- expectedStrs[DateFormatSymbols::TIMEZONE_LONG_DAYLIGHT].setTo("Alaska Daylight Time");
- expectedStrs[DateFormatSymbols::TIMEZONE_EXEMPLAR_CITY].setTo("");
- for(int32_t i=0; i<DateFormatSymbols::TIMEZONE_COUNT; i++){
- UnicodeString result;
- result = symbols.getZoneString(expectedKey, (DateFormatSymbols::TimeZoneTranslationType)i, result,status);
- if(U_FAILURE(status)){
- errln("Could not retrieve display name. Error: %s", u_errorName(status));
- return;
- }
- if(expectedStrs[i] != result){
- errln("Did not get the expected string. Expected: "+expectedStrs[i]+ UnicodeString(" Got: ") + result );
- }
- }
- expectedKey.setTo("America/Los_Angeles",0);
- UnicodeString exemplarCity("Phoenix");
- UnicodeString result;
- symbols.setZoneString(expectedKey,DateFormatSymbols::TIMEZONE_EXEMPLAR_CITY, exemplarCity, status);
- if(U_FAILURE(status)){
- errln("setZoneString() did not succeed. Error: %s", u_errorName(status));
- return;
- }
- result = symbols.getZoneString(expectedKey, DateFormatSymbols::TIMEZONE_EXEMPLAR_CITY, result,status);
- if(result != exemplarCity){
- errln("setZoneString() did not succeed. Expected: " + exemplarCity + " Got: " + result);
- }
- delete keys;
- delete keys2;
-}
-
void DateFormatTest::TestZTimeZoneParsing(void) {
UErrorCode status = U_ZERO_ERROR;
const Locale en("en");
diff --git a/icu4c/source/test/intltest/intltest.vcproj b/icu4c/source/test/intltest/intltest.vcproj
index a1bcbc1..7284d2e1 100644
--- a/icu4c/source/test/intltest/intltest.vcproj
+++ b/icu4c/source/test/intltest/intltest.vcproj
@@ -790,6 +790,22 @@
>
</File>
<File
+ RelativePath=".\tzfmttst.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\tzfmttst.h"
+ >
+ </File>
+ <File
+ RelativePath=".\tzoffloc.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\tzoffloc.h"
+ >
+ </File>
+ <File
RelativePath=".\tzregts.cpp"
>
</File>
diff --git a/icu4c/source/test/intltest/itformat.cpp b/icu4c/source/test/intltest/itformat.cpp
index 23bf1c1..eb80bad 100644
--- a/icu4c/source/test/intltest/itformat.cpp
+++ b/icu4c/source/test/intltest/itformat.cpp
@@ -46,6 +46,8 @@
#include "dadrcal.h" // DataDrivenCalendarTest
#include "dadrfmt.h" // DataDrivenFormatTest
#include "dtptngts.h" // IntlTestDateTimePatternGeneratorAPI
+#include "tzoffloc.h" // TimeZoneOffsetLocalTest
+#include "tzfmttst.h" // TimeZoneFormatTest
#define TESTCLASS(id, TestClass) \
@@ -110,6 +112,8 @@
TESTCLASS(30,DataDrivenCalendarTest);
TESTCLASS(31,DataDrivenFormatTest);
TESTCLASS(32,IntlTestDateTimePatternGeneratorAPI);
+ TESTCLASS(33,TimeZoneOffsetLocalTest);
+ TESTCLASS(34,TimeZoneFormatTest);
default: name = ""; break; //needed to end loop
diff --git a/icu4c/source/test/intltest/loctest.cpp b/icu4c/source/test/intltest/loctest.cpp
index 001ab8b..1ce0821 100644
--- a/icu4c/source/test/intltest/loctest.cpp
+++ b/icu4c/source/test/intltest/loctest.cpp
@@ -857,8 +857,8 @@
;
/* TODO: Change this test to be more like the cloctst version? */
- if (testCount != 488)
- errln("Expected getISOLanguages() to return 488 languages; it returned %d", testCount);
+ if (testCount != 489)
+ errln("Expected getISOLanguages() to return 489 languages; it returned %d", testCount);
else {
for (i = 0; i < 15; i++) {
int32_t j;
diff --git a/icu4c/source/test/intltest/miscdtfm.cpp b/icu4c/source/test/intltest/miscdtfm.cpp
index 8ee0fbf..36b9357 100644
--- a/icu4c/source/test/intltest/miscdtfm.cpp
+++ b/icu4c/source/test/intltest/miscdtfm.cpp
@@ -313,7 +313,7 @@
UnicodeString jstShort = "JST";
- UnicodeString tzID = "meta/Japan";
+ UnicodeString tzID = "Asia/Tokyo";
UnicodeString jdtLong(jdtLongC, 5, 5);
diff --git a/icu4c/source/test/intltest/tzfmttst.cpp b/icu4c/source/test/intltest/tzfmttst.cpp
new file mode 100644
index 0000000..d445da0
--- /dev/null
+++ b/icu4c/source/test/intltest/tzfmttst.cpp
@@ -0,0 +1,457 @@
+/*
+*******************************************************************************
+* Copyright (C) 2007, International Business Machines Corporation and *
+* others. All Rights Reserved. *
+*******************************************************************************
+*/
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "tzfmttst.h"
+
+#include "unicode/timezone.h"
+#include "unicode/simpletz.h"
+#include "unicode/calendar.h"
+#include "unicode/strenum.h"
+#include "unicode/smpdtfmt.h"
+#include "unicode/uchar.h"
+#include "unicode/basictz.h"
+#include "cstring.h"
+#include "zonemeta.h"
+
+#define DEBUG_ALL 0
+
+static const char* PATTERNS[] = {"z", "zzzz", "Z", "ZZZZ", "v", "vvvv", "V", "VVVV"};
+static const int NUM_PATTERNS = sizeof(PATTERNS)/sizeof(const char*);
+
+void
+TimeZoneFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
+{
+ if (exec) {
+ logln("TestSuite TimeZoneFormatTest");
+ }
+ switch (index) {
+ TESTCASE(0, TestTimeZoneRoundTrip);
+ TESTCASE(1, TestTimeRoundTrip);
+ default: name = ""; break;
+ }
+}
+
+void
+TimeZoneFormatTest::TestTimeZoneRoundTrip(void) {
+ UErrorCode status = U_ZERO_ERROR;
+
+ SimpleTimeZone unknownZone(-31415, (UnicodeString)"Etc/Unknown");
+ int32_t badDstOffset = -1234;
+ int32_t badZoneOffset = -2345;
+
+ int32_t testDateData[][3] = {
+ {2007, 1, 15},
+ {2007, 6, 15},
+ {1990, 1, 15},
+ {1990, 6, 15},
+ {1960, 1, 15},
+ {1960, 6, 15},
+ };
+
+ Calendar *cal = Calendar::createInstance(TimeZone::createTimeZone((UnicodeString)"UTC"), status);
+ if (U_FAILURE(status)) {
+ errln("Calendar::createInstance failed");
+ return;
+ }
+
+ // Set up rule equivalency test range
+ UDate low, high;
+ cal->set(1900, UCAL_JANUARY, 1);
+ low = cal->getTime(status);
+ cal->set(2040, UCAL_JANUARY, 1);
+ high = cal->getTime(status);
+ if (U_FAILURE(status)) {
+ errln("getTime failed");
+ return;
+ }
+
+ // Set up test dates
+ UDate DATES[(sizeof(testDateData)/sizeof(int32_t))/3];
+ const int32_t nDates = (sizeof(testDateData)/sizeof(int32_t))/3;
+ cal->clear();
+ for (int32_t i = 0; i < nDates; i++) {
+ cal->set(testDateData[i][0], testDateData[i][1], testDateData[i][2]);
+ DATES[i] = cal->getTime(status);
+ if (U_FAILURE(status)) {
+ errln("getTime failed");
+ return;
+ }
+ }
+
+ // Set up test locales
+ const Locale locales1[] = {
+ Locale("en_US")
+ };
+ const Locale locales2[] = {
+ Locale("en_US"),
+ Locale("en"),
+ Locale("en_CA"),
+ Locale("fr"),
+ Locale("zh_Hant")
+ };
+
+ const Locale *LOCALES;
+ int32_t nLocales;
+ if (DEBUG_ALL) {
+ LOCALES = Locale::getAvailableLocales(nLocales);
+ } else if (quick) {
+ LOCALES = locales1;
+ nLocales = sizeof(locales1)/sizeof(Locale);
+ } else {
+ LOCALES = locales2;
+ nLocales = sizeof(locales2)/sizeof(Locale);
+ }
+
+ StringEnumeration *tzids = TimeZone::createEnumeration();
+ if (U_FAILURE(status)) {
+ errln("tzids->count failed");
+ return;
+ }
+
+ int32_t inRaw, inDst;
+ int32_t outRaw, outDst;
+
+ // Run the roundtrip test
+ for (int32_t locidx = 0; locidx < nLocales; locidx++) {
+ for (int32_t patidx = 0; patidx < NUM_PATTERNS; patidx++) {
+
+ //DEBUG static const char* PATTERNS[] = {"z", "zzzz", "Z", "ZZZZ", "v", "vvvv", "V", "VVVV"};
+ //if (patidx != 1) continue;
+
+ SimpleDateFormat *sdf = new SimpleDateFormat((UnicodeString)PATTERNS[patidx], LOCALES[locidx], status);
+ if (U_FAILURE(status)) {
+ errln((UnicodeString)"new SimpleDateFormat failed for pattern " +
+ PATTERNS[patidx] + " for locale " + LOCALES[locidx].getName());
+ status = U_ZERO_ERROR;
+ continue;
+ }
+
+ tzids->reset(status);
+ const UnicodeString *tzid;
+ while ((tzid = tzids->snext(status))) {
+ TimeZone *tz = TimeZone::createTimeZone(*tzid);
+
+ for (int32_t datidx = 0; datidx < nDates; datidx++) {
+ UnicodeString tzstr;
+ FieldPosition fpos(0);
+ // Format
+ sdf->setTimeZone(*tz);
+ sdf->format(DATES[datidx], tzstr, fpos);
+
+ // Before parse, set unknown zone to SimpleDateFormat instance
+ // just for making sure that it does not depends on the time zone
+ // originally set.
+ sdf->setTimeZone(unknownZone);
+
+ // Parse
+ ParsePosition pos(0);
+ Calendar *outcal = Calendar::createInstance(unknownZone, status);
+ if (U_FAILURE(status)) {
+ errln("Failed to create an instance of calendar for receiving parse result.");
+ status = U_ZERO_ERROR;
+ continue;
+ }
+ outcal->set(UCAL_DST_OFFSET, badDstOffset);
+ outcal->set(UCAL_ZONE_OFFSET, badZoneOffset);
+
+ sdf->parse(tzstr, *outcal, pos);
+
+ // Check the result
+ const TimeZone &outtz = outcal->getTimeZone();
+ UnicodeString outtzid;
+ outtz.getID(outtzid);
+
+ tz->getOffset(DATES[datidx], false, inRaw, inDst, status);
+ if (U_FAILURE(status)) {
+ errln((UnicodeString)"Failed to get offsets from time zone" + *tzid);
+ status = U_ZERO_ERROR;
+ }
+ outtz.getOffset(DATES[datidx], false, outRaw, outDst, status);
+ if (U_FAILURE(status)) {
+ errln((UnicodeString)"Failed to get offsets from time zone" + outtzid);
+ status = U_ZERO_ERROR;
+ }
+
+ // Check if localized GMT format or RFC format is used.
+ int32_t numDigits = 0;
+ for (int n = 0; n < tzstr.length(); n++) {
+ if (u_isdigit(tzstr.charAt(n))) {
+ numDigits++;
+ }
+ }
+ if (numDigits >= 4) {
+ // Localized GMT or RFC: total offset (raw + dst) must be preserved.
+ int32_t inOffset = inRaw + inDst;
+ int32_t outOffset = outRaw + outDst;
+ if (inOffset != outOffset) {
+ errln((UnicodeString)"Offset round trip failed; tz=" + *tzid
+ + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
+ + ", time=" + DATES[datidx] + ", str=" + tzstr
+ + ", inOffset=" + inOffset + ", outOffset=" + outOffset);
+ }
+ } else if (uprv_strcmp(PATTERNS[patidx], "z") == 0 || uprv_strcmp(PATTERNS[patidx], "zzzz") == 0
+ || uprv_strcmp(PATTERNS[patidx], "v") == 0 || uprv_strcmp(PATTERNS[patidx], "vvvv") == 0
+ || uprv_strcmp(PATTERNS[patidx], "V") == 0) {
+ // Specific or generic: raw offset must be preserved.
+ if (inRaw != outRaw) {
+ errln((UnicodeString)"Raw offset round trip failed; tz=" + *tzid
+ + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
+ + ", time=" + DATES[datidx] + ", str=" + tzstr
+ + ", inRawOffset=" + inRaw + ", outRawOffset=" + outRaw);
+ }
+ } else { // "VVVV"
+ // Location: time zone rule must be preserved.
+ UnicodeString canonical;
+ ZoneMeta::getCanonicalID(*tzid, canonical);
+ if (outtzid != canonical) {
+ // Canonical ID did not match - check the rules
+ if (!((BasicTimeZone*)&outtz)->hasEquivalentTransitions((BasicTimeZone&)*tz, low, high, TRUE, status)) {
+ errln("Canonical round trip failed; tz=" + *tzid
+ + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
+ + ", time=" + DATES[datidx] + ", str=" + tzstr
+ + ", outtz=" + outtzid);
+ }
+ if (U_FAILURE(status)) {
+ errln("hasEquivalentTransitions failed");
+ status = U_ZERO_ERROR;
+ }
+ }
+ }
+ }
+ delete tz;
+ }
+ delete sdf;
+ }
+ }
+ delete cal;
+ delete tzids;
+}
+
+void
+TimeZoneFormatTest::TestTimeRoundTrip(void) {
+ UErrorCode status = U_ZERO_ERROR;
+
+ Calendar *cal = Calendar::createInstance(TimeZone::createTimeZone((UnicodeString)"UTC"), status);
+ if (U_FAILURE(status)) {
+ errln("Calendar::createInstance failed");
+ return;
+ }
+
+ UDate START_TIME, END_TIME;
+
+ if (DEBUG_ALL) {
+ cal->set(1900, UCAL_JANUARY, 1);
+ } else {
+ cal->set(1965, UCAL_JANUARY, 1);
+ }
+ START_TIME = cal->getTime(status);
+
+ cal->set(2015, UCAL_JANUARY, 1);
+ END_TIME = cal->getTime(status);
+ if (U_FAILURE(status)) {
+ errln("getTime failed");
+ return;
+ }
+
+ // Whether each pattern is ambiguous at DST->STD local time overlap
+ UBool AMBIGUOUS_DST_DECESSION[] = {FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE};
+ // Whether each pattern is ambiguous at STD->STD/DST->DST local time overlap
+ UBool AMBIGUOUS_NEGATIVE_SHIFT[] = {TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE};
+
+ UnicodeString BASEPATTERN("yyyy-MM-dd'T'HH:mm:ss.SSS");
+
+ // timer for performance analysis
+ UDate timer;
+ UDate times[NUM_PATTERNS];
+ for (int32_t i = 0; i < NUM_PATTERNS; i++) {
+ times[i] = 0;
+ }
+
+ UBool REALLY_VERBOSE = FALSE;
+
+ // Set up test locales
+ const Locale locales1[] = {
+ Locale("en_US")
+ };
+ const Locale locales2[] = {
+ Locale("en_US"),
+ Locale("en"),
+ Locale("de_DE"),
+ Locale("es_ES"),
+ Locale("fr_FR"),
+ Locale("it_IT"),
+ Locale("ja_JP"),
+ Locale("ko_KR"),
+ Locale("pt_BR"),
+ Locale("zh_Hans_CN"),
+ Locale("zh_Hant_TW")
+ };
+
+ const Locale *LOCALES;
+ int32_t nLocales;
+ if (DEBUG_ALL) {
+ LOCALES = Locale::getAvailableLocales(nLocales);
+ } else if (quick) {
+ LOCALES = locales1;
+ nLocales = sizeof(locales1)/sizeof(Locale);
+ } else {
+ LOCALES = locales2;
+ nLocales = sizeof(locales2)/sizeof(Locale);
+ }
+
+ StringEnumeration *tzids = TimeZone::createEnumeration();
+ if (U_FAILURE(status)) {
+ errln("tzids->count failed");
+ return;
+ }
+
+ int32_t testCounts = 0;
+ UDate testTimes[4];
+ UBool expectedRoundTrip[4];
+ int32_t testLen = 0;
+
+ for (int32_t locidx = 0; locidx < nLocales; locidx++) {
+ logln((UnicodeString)"Locale: " + LOCALES[locidx].getName());
+
+ for (int32_t patidx = 0; patidx < NUM_PATTERNS; patidx++) {
+ logln((UnicodeString)" pattern: " + PATTERNS[patidx]);
+
+ //DEBUG static const char* PATTERNS[] = {"z", "zzzz", "Z", "ZZZZ", "v", "vvvv", "V", "VVVV"};
+ //if (patidx != 1) continue;
+
+ UnicodeString pattern(BASEPATTERN);
+ pattern.append(" ").append(PATTERNS[patidx]);
+
+ SimpleDateFormat *sdf = new SimpleDateFormat(pattern, LOCALES[locidx], status);
+ if (U_FAILURE(status)) {
+ errln((UnicodeString)"new SimpleDateFormat failed for pattern " +
+ pattern + " for locale " + LOCALES[locidx].getName());
+ status = U_ZERO_ERROR;
+ continue;
+ }
+
+ tzids->reset(status);
+ const UnicodeString *tzid;
+
+ timer = Calendar::getNow();
+
+ while ((tzid = tzids->snext(status))) {
+ UnicodeString canonical;
+ ZoneMeta::getCanonicalID(*tzid, canonical);
+ if (*tzid != canonical) {
+ // Skip aliases
+ continue;
+ }
+ BasicTimeZone *tz = (BasicTimeZone*)TimeZone::createTimeZone(*tzid);
+ sdf->setTimeZone(*tz);
+
+ UDate t = START_TIME;
+ TimeZoneTransition tzt;
+ UBool tztAvail = FALSE;
+ UBool middle = TRUE;
+
+ while (t < END_TIME) {
+ if (!tztAvail) {
+ testTimes[0] = t;
+ expectedRoundTrip[0] = TRUE;
+ testLen = 1;
+ } else {
+ int32_t fromOffset = tzt.getFrom()->getRawOffset() + tzt.getFrom()->getDSTSavings();
+ int32_t toOffset = tzt.getTo()->getRawOffset() + tzt.getTo()->getDSTSavings();
+ int32_t delta = toOffset - fromOffset;
+ if (delta < 0) {
+ UBool isDstDecession = tzt.getFrom()->getDSTSavings() > 0 && tzt.getTo()->getDSTSavings() == 0;
+ testTimes[0] = t + delta - 1;
+ expectedRoundTrip[0] = TRUE;
+ testTimes[1] = t + delta;
+ expectedRoundTrip[1] = isDstDecession ?
+ !AMBIGUOUS_DST_DECESSION[patidx] : !AMBIGUOUS_NEGATIVE_SHIFT[patidx];
+ testTimes[2] = t - 1;
+ expectedRoundTrip[2] = isDstDecession ?
+ !AMBIGUOUS_DST_DECESSION[patidx] : !AMBIGUOUS_NEGATIVE_SHIFT[patidx];
+ testTimes[3] = t;
+ expectedRoundTrip[3] = TRUE;
+ testLen = 4;
+ } else {
+ testTimes[0] = t - 1;
+ expectedRoundTrip[0] = TRUE;
+ testTimes[1] = t;
+ expectedRoundTrip[1] = TRUE;
+ testLen = 2;
+ }
+ }
+ for (int32_t testidx = 0; testidx < testLen; testidx++) {
+ if (quick) {
+ // reduce regular test time
+ if (!expectedRoundTrip[testidx]) {
+ continue;
+ }
+ }
+ testCounts++;
+
+ UnicodeString text;
+ FieldPosition fpos(0);
+ sdf->format(testTimes[testidx], text, fpos);
+
+ UDate parsedDate = sdf->parse(text, status);
+ if (U_FAILURE(status)) {
+ errln((UnicodeString)"Failed to parse " + text);
+ status = U_ZERO_ERROR;
+ continue;
+ }
+ if (parsedDate != testTimes[testidx]) {
+ UnicodeString msg = (UnicodeString)"Time round trip failed for "
+ + "tzid=" + *tzid
+ + ", locale=" + LOCALES[locidx].getName()
+ + ", pattern=" + PATTERNS[patidx]
+ + ", text=" + text
+ + ", time=" + testTimes[testidx]
+ + ", restime=" + parsedDate
+ + ", diff=" + (parsedDate - testTimes[testidx]);
+ if (expectedRoundTrip[testidx]) {
+ errln((UnicodeString)"FAIL: " + msg);
+ } else if (REALLY_VERBOSE) {
+ logln(msg);
+ }
+ }
+ }
+ tztAvail = tz->getNextTransition(t, FALSE, tzt);
+ if (!tztAvail) {
+ break;
+ }
+ if (middle) {
+ // Test the date in the middle of two transitions.
+ t += (int64_t)((tzt.getTime() - t)/2);
+ middle = FALSE;
+ tztAvail = FALSE;
+ } else {
+ t = tzt.getTime();
+ }
+ }
+ delete tz;
+ }
+ times[patidx] += (Calendar::getNow() - timer);
+ delete sdf;
+ }
+ }
+ UDate total = 0;
+ logln("### Elapsed time by patterns ###");
+ for (int32_t i = 0; i < NUM_PATTERNS; i++) {
+ logln(UnicodeString("") + times[i] + "ms (" + PATTERNS[i] + ")");
+ total += times[i];
+ }
+ logln((UnicodeString)"Total: " + total + "ms");
+ logln((UnicodeString)"Iteration: " + testCounts);
+
+ delete cal;
+ delete tzids;
+}
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/test/intltest/tzfmttst.h b/icu4c/source/test/intltest/tzfmttst.h
new file mode 100644
index 0000000..2258330
--- /dev/null
+++ b/icu4c/source/test/intltest/tzfmttst.h
@@ -0,0 +1,27 @@
+/*
+*******************************************************************************
+* Copyright (C) 2007, International Business Machines Corporation and *
+* others. All Rights Reserved. *
+*******************************************************************************
+*/
+
+#ifndef _TIMEZONEFORMATTEST_
+#define _TIMEZONEFORMATTEST_
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "intltest.h"
+
+class TimeZoneFormatTest : public IntlTest {
+ // IntlTest override
+ void runIndexedTest(int32_t index, UBool exec, const char*& name, char* par);
+
+ void TestTimeZoneRoundTrip(void);
+ void TestTimeRoundTrip(void);
+};
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
+
+#endif // _TIMEZONEFORMATTEST_
diff --git a/icu4c/source/test/intltest/tzoffloc.cpp b/icu4c/source/test/intltest/tzoffloc.cpp
new file mode 100644
index 0000000..1c4c584
--- /dev/null
+++ b/icu4c/source/test/intltest/tzoffloc.cpp
@@ -0,0 +1,329 @@
+/*
+*******************************************************************************
+* Copyright (C) 2007, International Business Machines Corporation and *
+* others. All Rights Reserved. *
+*******************************************************************************
+*/
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "tzoffloc.h"
+
+#include "unicode/ucal.h"
+#include "unicode/timezone.h"
+#include "unicode/calendar.h"
+#include "unicode/dtrule.h"
+#include "unicode/tzrule.h"
+#include "unicode/rbtz.h"
+#include "unicode/simpletz.h"
+#include "unicode/tzrule.h"
+#include "unicode/smpdtfmt.h"
+#include "unicode/gregocal.h"
+
+void
+TimeZoneOffsetLocalTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
+{
+ if (exec) {
+ logln("TestSuite TimeZoneOffsetLocalTest");
+ }
+ switch (index) {
+ TESTCASE(0, TestGetOffsetAroundTransition);
+ default: name = ""; break;
+ }
+}
+
+/*
+ * Testing getOffset APIs around rule transition by local standard/wall time.
+ */
+void
+TimeZoneOffsetLocalTest::TestGetOffsetAroundTransition() {
+ const int32_t NUM_DATES = 10;
+ const int32_t NUM_TIMEZONES = 3;
+
+ const int32_t HOUR = 60*60*1000;
+ const int32_t MINUTE = 60*1000;
+
+ const int32_t DATES[NUM_DATES][6] = {
+ {2006, UCAL_APRIL, 2, 1, 30, 1*HOUR+30*MINUTE},
+ {2006, UCAL_APRIL, 2, 2, 00, 2*HOUR},
+ {2006, UCAL_APRIL, 2, 2, 30, 2*HOUR+30*MINUTE},
+ {2006, UCAL_APRIL, 2, 3, 00, 3*HOUR},
+ {2006, UCAL_APRIL, 2, 3, 30, 3*HOUR+30*MINUTE},
+ {2006, UCAL_OCTOBER, 29, 0, 30, 0*HOUR+30*MINUTE},
+ {2006, UCAL_OCTOBER, 29, 1, 00, 1*HOUR},
+ {2006, UCAL_OCTOBER, 29, 1, 30, 1*HOUR+30*MINUTE},
+ {2006, UCAL_OCTOBER, 29, 2, 00, 2*HOUR},
+ {2006, UCAL_OCTOBER, 29, 2, 30, 2*HOUR+30*MINUTE},
+ };
+
+ // Expected offsets by int32_t getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
+ // uint8_t dayOfWeek, int32_t millis, UErrorCode& status)
+ const int32_t OFFSETS1[NUM_DATES] = {
+ // April 2, 2006
+ -8*HOUR,
+ -7*HOUR,
+ -7*HOUR,
+ -7*HOUR,
+ -7*HOUR,
+
+ // October 29, 2006
+ -7*HOUR,
+ -8*HOUR,
+ -8*HOUR,
+ -8*HOUR,
+ -8*HOUR,
+ };
+
+ // Expected offsets by void getOffset(UDate date, UBool local, int32_t& rawOffset,
+ // int32_t& dstOffset, UErrorCode& ec) with local=TRUE
+ // or void getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ // int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) with
+ // nonExistingTimeOpt=kStandard/duplicatedTimeOpt=kStandard
+ const int32_t OFFSETS2[NUM_DATES][2] = {
+ // April 2, 2006
+ {-8*HOUR, 0},
+ {-8*HOUR, 0},
+ {-8*HOUR, 0},
+ {-8*HOUR, 1*HOUR},
+ {-8*HOUR, 1*HOUR},
+
+ // Oct 29, 2006
+ {-8*HOUR, 1*HOUR},
+ {-8*HOUR, 0},
+ {-8*HOUR, 0},
+ {-8*HOUR, 0},
+ {-8*HOUR, 0},
+ };
+
+ // Expected offsets by void getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt,
+ // int32_t duplicatedTimeOpt, int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) with
+ // nonExistingTimeOpt=kDaylight/duplicatedTimeOpt=kDaylight
+ const int32_t OFFSETS3[][2] = {
+ // April 2, 2006
+ {-8*HOUR, 0},
+ {-8*HOUR, 1*HOUR},
+ {-8*HOUR, 1*HOUR},
+ {-8*HOUR, 1*HOUR},
+ {-8*HOUR, 1*HOUR},
+
+ // October 29, 2006
+ {-8*HOUR, 1*HOUR},
+ {-8*HOUR, 1*HOUR},
+ {-8*HOUR, 1*HOUR},
+ {-8*HOUR, 0},
+ {-8*HOUR, 0},
+ };
+
+ UErrorCode status = U_ZERO_ERROR;
+
+ int32_t rawOffset, dstOffset;
+ TimeZone* utc = TimeZone::createTimeZone("UTC");
+ Calendar* cal = Calendar::createInstance(*utc, status);
+ if (U_FAILURE(status)) {
+ errln("Calendar::createInstance failed");
+ return;
+ }
+ cal->clear();
+
+ // Set up TimeZone objects - OlsonTimeZone, SimpleTimeZone and RuleBasedTimeZone
+ BasicTimeZone *TESTZONES[NUM_TIMEZONES];
+
+ TESTZONES[0] = (BasicTimeZone*)TimeZone::createTimeZone("America/Los_Angeles");
+ TESTZONES[1] = new SimpleTimeZone(-8*HOUR, "Simple Pacific Time",
+ UCAL_APRIL, 1, UCAL_SUNDAY, 2*HOUR,
+ UCAL_OCTOBER, -1, UCAL_SUNDAY, 2*HOUR, status);
+ if (U_FAILURE(status)) {
+ errln("SimpleTimeZone constructor failed");
+ return;
+ }
+
+ InitialTimeZoneRule *ir = new InitialTimeZoneRule(
+ "Pacific Standard Time", // Initial time Name
+ -8*HOUR, // Raw offset
+ 0*HOUR); // DST saving amount
+
+ RuleBasedTimeZone *rbPT = new RuleBasedTimeZone("Rule based Pacific Time", ir);
+
+ DateTimeRule *dtr;
+ AnnualTimeZoneRule *atzr;
+ const int32_t STARTYEAR = 2000;
+
+ dtr = new DateTimeRule(UCAL_APRIL, 1, UCAL_SUNDAY,
+ 2*HOUR, DateTimeRule::WALL_TIME); // 1st Sunday in April, at 2AM wall time
+ atzr = new AnnualTimeZoneRule("Pacific Daylight Time",
+ -8*HOUR /* rawOffset */, 1*HOUR /* dstSavings */, dtr,
+ STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
+ rbPT->addTransitionRule(atzr, status);
+ if (U_FAILURE(status)) {
+ errln("Could not add DST start rule to the RuleBasedTimeZone rbPT");
+ return;
+ }
+
+ dtr = new DateTimeRule(UCAL_OCTOBER, -1, UCAL_SUNDAY,
+ 2*HOUR, DateTimeRule::WALL_TIME); // last Sunday in October, at 2AM wall time
+ atzr = new AnnualTimeZoneRule("Pacific Standard Time",
+ -8*HOUR /* rawOffset */, 0 /* dstSavings */, dtr,
+ STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
+ rbPT->addTransitionRule(atzr, status);
+ if (U_FAILURE(status)) {
+ errln("Could not add STD start rule to the RuleBasedTimeZone rbPT");
+ return;
+ }
+
+ rbPT->complete(status);
+ if (U_FAILURE(status)) {
+ errln("complete() failed for RuleBasedTimeZone rbPT");
+ return;
+ }
+
+ TESTZONES[2] = rbPT;
+
+ // Calculate millis
+ UDate MILLIS[NUM_DATES];
+ for (int32_t i = 0; i < NUM_DATES; i++) {
+ cal->clear();
+ cal->set(DATES[i][0], DATES[i][1], DATES[i][2], DATES[i][3], DATES[i][4]);
+ MILLIS[i] = cal->getTime(status);
+ if (U_FAILURE(status)) {
+ errln("cal->getTime failed");
+ return;
+ }
+ }
+
+ SimpleDateFormat df(UnicodeString("yyyy-MM-dd HH:mm:ss"), status);
+ if (U_FAILURE(status)) {
+ errln("Failed to initialize a SimpleDateFormat");
+ }
+ df.setTimeZone(*utc);
+ UnicodeString dateStr;
+
+ // Test getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
+ // uint8_t dayOfWeek, int32_t millis, UErrorCode& status)
+ for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
+ for (int32_t d = 0; d < NUM_DATES; d++) {
+ status = U_ZERO_ERROR;
+ int32_t offset = TESTZONES[i]->getOffset(GregorianCalendar::AD, DATES[d][0], DATES[d][1], DATES[d][2],
+ UCAL_SUNDAY, DATES[d][5], status);
+ if (U_FAILURE(status)) {
+ errln((UnicodeString)"getOffset(era,year,month,day,dayOfWeek,millis,status) failed for TESTZONES[" + i + "]");
+ } else if (offset != OFFSETS1[d]) {
+ dateStr.remove();
+ df.format(MILLIS[d], dateStr);
+ errln((UnicodeString)"Bad offset returned by TESTZONES[" + i + "] at "
+ + dateStr + "(standard) - Got: " + offset + " Expected: " + OFFSETS1[d]);
+ }
+ }
+ }
+
+ // Test getOffset(UDate date, UBool local, int32_t& rawOffset,
+ // int32_t& dstOffset, UErrorCode& ec) with local = TRUE
+ for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
+ for (int32_t m = 0; m < NUM_DATES; m++) {
+ status = U_ZERO_ERROR;
+ TESTZONES[i]->getOffset(MILLIS[m], TRUE, rawOffset, dstOffset, status);
+ if (U_FAILURE(status)) {
+ errln((UnicodeString)"getOffset(date,local,rawOfset,dstOffset,ec) failed for TESTZONES[" + i + "]");
+ } else if (rawOffset != OFFSETS2[m][0] || dstOffset != OFFSETS2[m][1]) {
+ dateStr.remove();
+ df.format(MILLIS[m], dateStr);
+ errln((UnicodeString)"Bad offset returned by TESTZONES[" + i + "] at "
+ + dateStr + "(wall) - Got: "
+ + rawOffset + "/" + dstOffset
+ + " Expected: " + OFFSETS2[m][0] + "/" + OFFSETS2[m][1]);
+ }
+ }
+ }
+
+ // Test getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ // int32_t& rawOffset, int32_t& dstOffset, UErroCode& status)
+ // with nonExistingTimeOpt=kStandard/duplicatedTimeOpt=kStandard
+ for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
+ for (int m = 0; m < NUM_DATES; m++) {
+ status = U_ZERO_ERROR;
+ TESTZONES[i]->getOffsetFromLocal(MILLIS[m], BasicTimeZone::kStandard, BasicTimeZone::kStandard,
+ rawOffset, dstOffset, status);
+ if (U_FAILURE(status)) {
+ errln((UnicodeString)"getOffsetFromLocal with kStandard/kStandard failed for TESTZONES[" + i + "]");
+ } else if (rawOffset != OFFSETS2[m][0] || dstOffset != OFFSETS2[m][1]) {
+ dateStr.remove();
+ df.format(MILLIS[m], dateStr);
+ errln((UnicodeString)"Bad offset returned by TESTZONES[" + i + "] at "
+ + dateStr + "(wall/kStandard/kStandard) - Got: "
+ + rawOffset + "/" + dstOffset
+ + " Expected: " + OFFSETS2[m][0] + "/" + OFFSETS2[m][1]);
+ }
+ }
+ }
+
+ // Test getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ // int32_t& rawOffset, int32_t& dstOffset, UErroCode& status)
+ // with nonExistingTimeOpt=kDaylight/duplicatedTimeOpt=kDaylight
+ for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
+ for (int m = 0; m < NUM_DATES; m++) {
+ status = U_ZERO_ERROR;
+ TESTZONES[i]->getOffsetFromLocal(MILLIS[m], BasicTimeZone::kDaylight, BasicTimeZone::kDaylight,
+ rawOffset, dstOffset, status);
+ if (U_FAILURE(status)) {
+ errln((UnicodeString)"getOffsetFromLocal with kDaylight/kDaylight failed for TESTZONES[" + i + "]");
+ } else if (rawOffset != OFFSETS3[m][0] || dstOffset != OFFSETS3[m][1]) {
+ dateStr.remove();
+ df.format(MILLIS[m], dateStr);
+ errln((UnicodeString)"Bad offset returned by TESTZONES[" + i + "] at "
+ + dateStr + "(wall/kDaylight/kDaylight) - Got: "
+ + rawOffset + "/" + dstOffset
+ + " Expected: " + OFFSETS3[m][0] + "/" + OFFSETS3[m][1]);
+ }
+ }
+ }
+
+ // Test getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ // int32_t& rawOffset, int32_t& dstOffset, UErroCode& status)
+ // with nonExistingTimeOpt=kFormer/duplicatedTimeOpt=kLatter
+ for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
+ for (int m = 0; m < NUM_DATES; m++) {
+ status = U_ZERO_ERROR;
+ TESTZONES[i]->getOffsetFromLocal(MILLIS[m], BasicTimeZone::kFormer, BasicTimeZone::kLatter,
+ rawOffset, dstOffset, status);
+ if (U_FAILURE(status)) {
+ errln((UnicodeString)"getOffsetFromLocal with kFormer/kLatter failed for TESTZONES[" + i + "]");
+ } else if (rawOffset != OFFSETS2[m][0] || dstOffset != OFFSETS2[m][1]) {
+ dateStr.remove();
+ df.format(MILLIS[m], dateStr);
+ errln((UnicodeString)"Bad offset returned by TESTZONES[" + i + "] at "
+ + dateStr + "(wall/kFormer/kLatter) - Got: "
+ + rawOffset + "/" + dstOffset
+ + " Expected: " + OFFSETS2[m][0] + "/" + OFFSETS2[m][1]);
+ }
+ }
+ }
+
+ // Test getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+ // int32_t& rawOffset, int32_t& dstOffset, UErroCode& status)
+ // with nonExistingTimeOpt=kLatter/duplicatedTimeOpt=kFormer
+ for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
+ for (int m = 0; m < NUM_DATES; m++) {
+ status = U_ZERO_ERROR;
+ TESTZONES[i]->getOffsetFromLocal(MILLIS[m], BasicTimeZone::kLatter, BasicTimeZone::kFormer,
+ rawOffset, dstOffset, status);
+ if (U_FAILURE(status)) {
+ errln((UnicodeString)"getOffsetFromLocal with kLatter/kFormer failed for TESTZONES[" + i + "]");
+ } else if (rawOffset != OFFSETS3[m][0] || dstOffset != OFFSETS3[m][1]) {
+ dateStr.remove();
+ df.format(MILLIS[m], dateStr);
+ errln((UnicodeString)"Bad offset returned by TESTZONES[" + i + "] at "
+ + dateStr + "(wall/kLatter/kFormer) - Got: "
+ + rawOffset + "/" + dstOffset
+ + " Expected: " + OFFSETS3[m][0] + "/" + OFFSETS3[m][1]);
+ }
+ }
+ }
+
+ for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
+ delete TESTZONES[i];
+ }
+ delete utc;
+ delete cal;
+}
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/test/intltest/tzoffloc.h b/icu4c/source/test/intltest/tzoffloc.h
new file mode 100644
index 0000000..7d8fa23
--- /dev/null
+++ b/icu4c/source/test/intltest/tzoffloc.h
@@ -0,0 +1,26 @@
+/*
+*******************************************************************************
+* Copyright (C) 2007, International Business Machines Corporation and *
+* others. All Rights Reserved. *
+*******************************************************************************
+*/
+
+#ifndef _TIMEZONEOFFSETLOCALTEST_
+#define _TIMEZONEOFFSETLOCALTEST_
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "intltest.h"
+
+class TimeZoneOffsetLocalTest : public IntlTest {
+ // IntlTest override
+ void runIndexedTest(int32_t index, UBool exec, const char*& name, char* par);
+
+ void TestGetOffsetAroundTransition(void);
+};
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
+
+#endif // _TIMEZONEOFFSETLOCALTEST_
diff --git a/icu4c/source/test/intltest/tztest.cpp b/icu4c/source/test/intltest/tztest.cpp
index 28e3301..0c5b07c 100644
--- a/icu4c/source/test/intltest/tztest.cpp
+++ b/icu4c/source/test/intltest/tztest.cpp
@@ -18,6 +18,7 @@
#include "cmemory.h"
#include "putilimp.h"
#include "cstring.h"
+#include "olsontz.h"
#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
@@ -755,53 +756,92 @@
}
}
+
/**
* Utility function for TestCustomParse
*/
-UnicodeString& TimeZoneTest::formatMinutes(int32_t min, UnicodeString& rv, UBool insertSep/*=TRUE*/)
-{
- rv.remove();
+UnicodeString& TimeZoneTest::formatOffset(int32_t offset, UnicodeString &rv) {
+ rv.remove();
+ UChar sign = 0x002B;
+ if (offset < 0) {
+ sign = 0x002D;
+ offset = -offset;
+ }
- UChar sign = 0x002B;
- if (min < 0) {
- sign = 0x002D;
- min = -min;
+ int32_t s = offset % 60;
+ offset /= 60;
+ int32_t m = offset % 60;
+ int32_t h = offset / 60;
+
+ rv += (UChar)(sign);
+ if (h >= 10) {
+ rv += (UChar)(0x0030 + (h/10));
+ } else {
+ rv += (UChar)0x0030;
+ }
+ rv += (UChar)(0x0030 + (h%10));
+
+ rv += (UChar)0x003A; /* ':' */
+ if (m >= 10) {
+ rv += (UChar)(0x0030 + (m/10));
+ } else {
+ rv += (UChar)0x0030;
+ }
+ rv += (UChar)(0x0030 + (m%10));
+
+ if (s) {
+ rv += (UChar)0x003A; /* ':' */
+ if (s >= 10) {
+ rv += (UChar)(0x0030 + (s/10));
+ } else {
+ rv += (UChar)0x0030;
}
- int h = min/60;
- min = min%60;
-
- rv += (UChar)(sign);
- if(h >= 10)
- rv += (UChar)(0x0030 + (h/10));
- else
- rv += "0";
-
- rv += (UChar)(0x0030 + (h%10));
-
- if (insertSep)
- rv += ":";
-
- if(min >= 10)
- rv += (UChar)(0x0030 + (min/10));
- else
- rv += "0";
-
- rv += (UChar)(0x0030 + (min%10));
-
- return rv;
+ rv += (UChar)(0x0030 + (s%10));
+ }
+ return rv;
}
/**
- * Utility function for TestCustomParse, generating RFC822 style
- * time zone string for the give offset in minutes
+ * Utility function for TestCustomParse, generating time zone ID
+ * string for the give offset.
*/
-UnicodeString& TimeZoneTest::formatRFC822TZ(int32_t min, UnicodeString& rv)
-{
- UnicodeString offsetStr;
- formatMinutes(min, offsetStr, FALSE);
+UnicodeString& TimeZoneTest::formatTZID(int32_t offset, UnicodeString &rv) {
rv.remove();
+ UChar sign = 0x002B;
+ if (offset < 0) {
+ sign = 0x002D;
+ offset = -offset;
+ }
+
+ int32_t s = offset % 60;
+ offset /= 60;
+ int32_t m = offset % 60;
+ int32_t h = offset / 60;
+
rv += "GMT";
- rv += offsetStr;
+ rv += (UChar)(sign);
+ if (h >= 10) {
+ rv += (UChar)(0x0030 + (h/10));
+ } else {
+ rv += (UChar)0x0030;
+ }
+ rv += (UChar)(0x0030 + (h%10));
+
+ if (m >= 10) {
+ rv += (UChar)(0x0030 + (m/10));
+ } else {
+ rv += (UChar)0x0030;
+ }
+ rv += (UChar)(0x0030 + (m%10));
+
+ if (s) {
+ if (s >= 10) {
+ rv += (UChar)(0x0030 + (s/10));
+ } else {
+ rv += (UChar)0x0030;
+ }
+ rv += (UChar)(0x0030 + (s%10));
+ }
return rv;
}
@@ -825,92 +865,63 @@
}
kData[] =
{
- // ID Expected offset in minutes
- //{"GMT", kUnparseable}, //Isn't custom. Can't test it here. [returns normal GMT]
+ // ID Expected offset in seconds
+ {"GMT", kUnparseable}, //Isn't custom. [returns normal GMT]
{"GMT-YOUR.AD.HERE", kUnparseable},
- // {"GMT0", kUnparseable}, // ICU 2.8: An Olson zone ID
- // {"GMT+0", (0)}, // ICU 2.8: An Olson zone ID
- {"GMT+1", (60)},
- {"GMT-0030", (-30)},
+ {"GMT0", kUnparseable},
+ {"GMT+0", (0)},
+ {"GMT+1", (1*60*60)},
+ {"GMT-0030", (-30*60)},
{"GMT+15:99", kUnparseable},
{"GMT+", kUnparseable},
{"GMT-", kUnparseable},
{"GMT+0:", kUnparseable},
{"GMT-:", kUnparseable},
- {"GMT-YOUR.AD.HERE", kUnparseable},
- {"GMT+0010", (10)}, // Interpret this as 00:10
- {"GMT-10", (-10*60)},
- {"GMT+30", (30)},
- {"GMT-3:30", (-(3*60+30))},
- {"GMT-230", (-(2*60+30))},
+ {"GMT-YOUR.AD.HERE", kUnparseable},
+ {"GMT+0010", (10*60)}, // Interpret this as 00:10
+ {"GMT-10", (-10*60*60)},
+ {"GMT+30", kUnparseable},
+ {"GMT-3:30", (-(3*60+30)*60)},
+ {"GMT-230", (-(2*60+30)*60)},
+ {"GMT+05:13:05", ((5*60+13)*60+5)},
+ {"GMT-71023", (-((7*60+10)*60+23))},
+ {"GMT+01:23:45:67", kUnparseable},
+ {"GMT+01:234", kUnparseable},
+ {"GMT-2:31:123", kUnparseable},
+ {"GMT+3:75", kUnparseable},
+ {"GMT-01010101", kUnparseable},
{0, 0}
};
- for (i=0; kData[i].customId != 0; i++)
- {
+ for (i=0; kData[i].customId != 0; i++) {
UnicodeString id(kData[i].customId);
int32_t exp = kData[i].expectedOffset;
-/*
- { // for no data test Jitterbug 4354
- UErrorCode success = U_ZERO_ERROR;
- NumberFormat* numberFormat = NumberFormat::createInstance(success);
- if (U_FAILURE(success)) {
- dataerrln(" NumberFormat::createInstance() error");
- return;
- }
- delete numberFormat;
- }
- */
-
TimeZone *zone = TimeZone::createTimeZone(id);
UnicodeString itsID, temp;
- logln();
- logln("testing # " + formatMinutes(i, temp) + id);
-
- /*
- if(zone == NULL)
- {
- errln("FAIL: Could not createTimeZone(" + id + "). Returned NULL.");
- continue;
- }
- */
-
-
- if (! zone->getID(itsID).compare("GMT"))
- //if(zone == NULL)
- {
- logln(id + " -> generic GMT");
- // When TimeZone.getTimeZone() can't parse the id, it
- // returns GMT -- a dubious practice, but required for
- // backward compatibility.
- if (exp != kUnparseable) {
- errln("FAIL: Expected offset of " + formatMinutes(exp,temp) +
- " for " + id + ", got parse failure");
- }
- }
- else
- {
+ if (zone->getDynamicClassID() == OlsonTimeZone::getStaticClassID()) {
+ logln(id + " -> Olson time zone");
+ } else {
zone->getID(itsID);
- int32_t ioffset = zone->getRawOffset()/60000;
+ int32_t ioffset = zone->getRawOffset()/1000;
UnicodeString offset, expectedID;
- formatMinutes(ioffset, offset);
- formatRFC822TZ(ioffset, expectedID);
- logln(id + " -> " + itsID + " GMT" + offset);
- if (exp == kUnparseable)
- {
- errln("FAIL: Expected parse failure for " + id +
- ", got offset of " + offset +
- ", id " + itsID);
+ formatOffset(ioffset, offset);
+ formatTZID(ioffset, expectedID);
+ logln(id + " -> " + itsID + " " + offset);
+ if (exp == kUnparseable && itsID != "GMT") {
+ errln("Expected parse failure for " + id +
+ ", got offset of " + offset +
+ ", id " + itsID);
}
- else if (ioffset != exp ||
- (itsID.compare(expectedID) != 0))
- {
- errln("Expected offset of " + formatMinutes(exp,temp) +
- ", id " + expectedID +
- ", for " + id +
- ", got offset of " + offset +
- ", id " + itsID);
+ // JDK 1.3 creates custom zones with the ID "Custom"
+ // JDK 1.4 creates custom zones with IDs of the form "GMT+02:00"
+ // ICU creates custom zones with IDs of the form "GMT+0200"
+ else if (exp != kUnparseable && (ioffset != exp || itsID != expectedID)) {
+ errln("Expected offset of " + formatOffset(exp, temp) +
+ ", id " + expectedID +
+ ", for " + id +
+ ", got offset of " + offset +
+ ", id " + itsID);
}
}
delete zone;
diff --git a/icu4c/source/test/intltest/tztest.h b/icu4c/source/test/intltest/tztest.h
index 7e9c333..931509c 100644
--- a/icu4c/source/test/intltest/tztest.h
+++ b/icu4c/source/test/intltest/tztest.h
@@ -92,8 +92,8 @@
private:
// internal functions
- static UnicodeString& formatMinutes(int32_t min, UnicodeString& rv, UBool insertSep = TRUE);
- static UnicodeString& formatRFC822TZ(int32_t min, UnicodeString& rv);
+ static UnicodeString& formatOffset(int32_t offset, UnicodeString& rv);
+ static UnicodeString& formatTZID(int32_t offset, UnicodeString& rv);
};
#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/test/testdata/structLocale.txt b/icu4c/source/test/testdata/structLocale.txt
index b744a73..9a54011 100644
--- a/icu4c/source/test/testdata/structLocale.txt
+++ b/icu4c/source/test/testdata/structLocale.txt
@@ -1897,6 +1897,7 @@
ypk{""}
za{""}
zap{""}
+ zbl{""}
zen{""}
zh{""}
zh_Hans{""}
@@ -1938,6 +1939,7 @@
Scripts{
Arab{""}
Armn{""}
+ Avst{""}
Bali{""}
Batk{""}
Beng{""}
@@ -2001,6 +2003,7 @@
Lyci{""}
Lydi{""}
Mand{""}
+ Mani{""}
Maya{""}
Mero{""}
Mlym{""}
@@ -2016,12 +2019,14 @@
Osma{""}
Perm{""}
Phag{""}
+ Phlv{""}
Phnx{""}
Plrd{""}
Qaai{""}
Rjng{""}
Roro{""}
Runr{""}
+ Samr{""}
Sara{""}
Saur{""}
Sgnw{""}
@@ -2080,6 +2085,7 @@
1606NICT{""}
1694ACAD{""}
1901{""}
+ 1994{""}
1996{""}
AREVELA{""}
AREVMDA{""}
@@ -2091,6 +2097,7 @@
GAULISH{""}
GUOYU{""}
HAKKA{""}
+ LIPAW{""}
LOJBAN{""}
MONOTON{""}
NEDIS{""}
@@ -2102,6 +2109,7 @@
REVISED{""}
ROZAJ{""}
SAAHO{""}
+ SCOTLAND{""}
SCOUSE{""}
SOLBA{""}
TARASK{""}