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(&param, 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 &region, 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 &region, 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 &region) 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 &region) 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{""}