Skip ab/6749736 in stage.

Merged-In: I122d1acaca1d3895d02d18f2221a3af636476d65
Change-Id: I9594b3caa20a0062774a965bd9c0a78649ec3d12
diff --git a/apex/manifest.json b/apex/manifest.json
index c1bdf6a..98811b1 100644
--- a/apex/manifest.json
+++ b/apex/manifest.json
@@ -1,4 +1,4 @@
 {
   "name": "com.android.tzdata",
-  "version": 300000000
+  "version": 300900700
 }
diff --git a/debug_tools/host/main/java/TzFileDumper.java b/debug_tools/host/main/java/TzFileDumper.java
index d06a66c..5979328 100644
--- a/debug_tools/host/main/java/TzFileDumper.java
+++ b/debug_tools/host/main/java/TzFileDumper.java
@@ -285,8 +285,8 @@
             Object[] row = new Object[] {
                     type.gmtOffsetSeconds,
                     type.isDst,
-                    type.ttisgmt,
-                    type.ttisstd,
+                    nullToEmptyString(type.ttisgmt),
+                    nullToEmptyString(type.ttisstd),
                     formatDurationSeconds(type.gmtOffsetSeconds),
                     formatIsDst(type.isDst),
             };
@@ -299,6 +299,10 @@
                 "[gmtOffset ISO]", "[DST?]");
     }
 
+    private static Object nullToEmptyString(Object object) {
+        return object == null ? "" : object;
+    }
+
     private static void fillIntArray(MappedByteBuffer mappedByteBuffer, int[] toFill) {
         for (int i = 0; i < toFill.length; i++) {
             toFill[i] = mappedByteBuffer.getInt();
diff --git a/input_tools/android/tzids/Android.bp b/input_tools/android/tzids/Android.bp
new file mode 100644
index 0000000..c4750c8
--- /dev/null
+++ b/input_tools/android/tzids/Android.bp
@@ -0,0 +1,42 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Proto library
+java_library_host {
+    name: "tzids_protos",
+
+    proto: {
+        type: "full",
+        include_dirs: ["external/protobuf/src"],
+    },
+
+    srcs: ["src/main/proto/**/*.proto"],
+}
+
+java_library_host {
+    name: "tzids",
+    srcs: ["src/main/java/**/*.java"],
+    static_libs: ["tzids_protos"],
+}
+
+java_test_host {
+    name: "tzids_tests",
+
+    srcs: ["src/test/java/**/*.java"],
+    static_libs: [
+        "junit",
+        "tzids",
+    ],
+    test_suites: ["general-tests"],
+}
diff --git a/input_tools/android/tzids/TEST_MAPPING b/input_tools/android/tzids/TEST_MAPPING
new file mode 100644
index 0000000..89921f9
--- /dev/null
+++ b/input_tools/android/tzids/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+  "presubmit": [
+    {
+      "name" : "tzids_tests",
+      "host" : true
+    }
+  ]
+}
diff --git a/input_tools/android/tzids/src/main/java/com/android/timezone/tzids/TimeZoneIds.java b/input_tools/android/tzids/src/main/java/com/android/timezone/tzids/TimeZoneIds.java
new file mode 100644
index 0000000..cc35e17
--- /dev/null
+++ b/input_tools/android/tzids/src/main/java/com/android/timezone/tzids/TimeZoneIds.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.timezone.tzids;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.stream.Collectors.toMap;
+
+import com.android.timezone.tzids.proto.TzIdsProto;
+import com.google.protobuf.TextFormat;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Function;
+
+/**
+ * A representation of Android's country ISO code mapping for TZDB time zone IDs, and information
+ * about time zone ID equivalence, for use in host tooling.
+ *
+ * <p>The Android team maintain the mapping based on information from IANA's TZDB and other
+ * sources. Strictly speaking, TZDB no longer supports the idea of a time zone ID being associated
+ * with a single country (see zone1970.tab Vs zone.tab), but Android requires most zones be mapped
+ * this way: Android has a region-based time zone picker in settings (for selecting the device's
+ * time zone manually), and it uses a country-based algorithm for telephony-based time zone
+ * detection. Most time zone IDs in TZDB have an exemplar location, and a time zone ID will be
+ * mapped to the country of that exemplar location.
+ *
+ * <p>Besides the country mapping, only some IDs for a country are actually "useful". The others are
+ * associated with equivalent alternatives that are preferred. Mappings between different time zone
+ * IDs exist in two main situations:
+ * <ol>
+ *     <li>"Links" : When TZDB has a direct link / synonym. e.g. "GB-Eire", an old ID, maps to
+ *     "Europe/London", the modern / canonical equivalent. "GB-Eire" and "Europe/London" are
+ *     identical in TZDB. Note: Other libraries with additional metadata like ICU could still treat
+ *     "GB-Eire" differently from "Europe/London".
+ *     These will typically be as you'd expect from looking at the IANA tzdb backward file. However,
+ *     the IDs preferred by Android are determined by countryzones.txt, which could choose to
+ *     continue using an old ID, in which case the link may be reversed. This reversal is expected
+ *     when an older version of ICU doesn't have strings for a new ID.</li>
+ *     <li>"Replacements" : When zones have coalesced over time within a country. TZDB has many
+ *     "extra" IDs for time zones because of historical differences that haven't been relevant for
+ *     years. Replacements have a "from" time which indicates the point in time after which the two
+ *     zones can be considered equivalent. e.g. "America/Boise" has the same rules as
+ *     "America/Phoenix" after Sun, 03 Feb 1974, so the two can be considered equivalent today, but
+ *     not for dates before then.</li>
+ * </ol>
+ *
+ * <p>Other notes:
+ * <ul>
+ *     <li>There are entries in TZDB that are not mapped to a country and so are not included.
+ *     e.g. Etc/GMT+5.</li>
+ *     <li>Time zone IDs must be mapped to at most one country.</li>
+ *     <li>There are many time zone IDs in TZDB that have identical rules, but are associated with
+ *     different countries. These are deliberately not recorded as links or replacements.
+ *     i.e. Links/replacements do not link across ISO country codes.</li>
+ * </ul>
+ */
+public class TimeZoneIds {
+
+    private final TzIdsProto.TimeZoneIds mTimeZoneIdsProto;
+
+    /** Creates a {@link TimeZoneIds} from the proto definition. */
+    public TimeZoneIds(TzIdsProto.TimeZoneIds timeZoneIdsProto) {
+        mTimeZoneIdsProto = Objects.requireNonNull(timeZoneIdsProto);
+    }
+
+    /** Loads the {@code inputFile} as a {@link TimeZoneIds}. */
+    public static TimeZoneIds load(File inputFile) throws IOException {
+        TzIdsProto.TimeZoneIds.Builder builder = TzIdsProto.TimeZoneIds.newBuilder();
+        try (InputStreamReader reader =
+                new InputStreamReader(new FileInputStream(inputFile), UTF_8)) {
+            TextFormat.getParser().merge(reader, builder);
+        }
+        return new TimeZoneIds(builder.build());
+    }
+
+    /** Store this in {@code file}. */
+    public void store(File file) throws IOException {
+        try (OutputStreamWriter writer =
+                new OutputStreamWriter(new FileOutputStream(file), UTF_8)) {
+            writer.append("# Autogenerated file - DO NOT EDIT.\n");
+            TextFormat.print(mTimeZoneIdsProto, writer);
+        }
+    }
+
+    /**
+     * Returns the country code associated with the supplied {@code zoneId} or {@code null} if it
+     * is not found.
+     */
+    public String getCountryCodeForZoneId(String zoneId) {
+        for (TzIdsProto.CountryMapping countryMapping
+                : mTimeZoneIdsProto.getCountryMappingsList()) {
+            if (countryMapping.getTimeZoneIdsList().contains(zoneId)) {
+                return countryMapping.getIsoCode();
+            }
+
+            for (TzIdsProto.TimeZoneLink timeZoneLink : countryMapping.getTimeZoneLinksList()) {
+                if (timeZoneLink.getAlternativeId().equals(zoneId)) {
+                    return countryMapping.getIsoCode();
+                }
+            }
+
+            for (TzIdsProto.TimeZoneReplacement timeZoneReplacement
+                    : countryMapping.getTimeZoneReplacementsList()) {
+                if (timeZoneReplacement.getReplacedId().equals(zoneId)) {
+                    return countryMapping.getIsoCode();
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns a country mapping for TZDB time zone IDs on or after the specified time.
+     * The {@link Map} returned contains entries that link Olson IDs to themselves, or to preferred
+     * Olson IDs. The {@code replacementThreshold} is used to identify which "replacements" should
+     * be considered (see {@link TimeZoneIds} for details).
+     *
+     * <p>This mapping is useful when interpreting data from third party sources that could use any
+     * of the many equivalent zone IDs from TZDB and need to interact with Android devices.
+     *
+     * <p>If no map entry is present for an ID, then the IDs is either not valid or is not mapped to
+     * the ISO country code by Android's mapping data.
+     *
+     * @param isoCode the ISO 3166 alpha-2 country code for the country
+     * @param replacementThreshold the point in time to use when interpreting replacements
+     * @throws IllegalArgumentException if the country code is not recognized
+     */
+    public Map<String, String> getCountryIdMap(String isoCode, Instant replacementThreshold) {
+        for (TzIdsProto.CountryMapping countryMapping
+                : mTimeZoneIdsProto.getCountryMappingsList()) {
+            if (countryMapping.getIsoCode().equalsIgnoreCase(isoCode)) {
+                return createCountryIdMap(countryMapping, replacementThreshold);
+            }
+        }
+        throw new IllegalArgumentException("Unknown country code: " + isoCode);
+    }
+
+    private Map<String, String> createCountryIdMap(
+            TzIdsProto.CountryMapping countryMapping, Instant replacementThreshold) {
+        Map<String, String> tzIdMap = new HashMap<>();
+
+        // Add identity entries for the preferred IDs.
+        putAllSafely(tzIdMap, countryMapping.getTimeZoneIdsList()
+                .stream()
+                .collect(toMap(Function.identity(), Function.identity())));
+
+        // Handle links: just add straightforward map entries.
+        putAllSafely(tzIdMap, countryMapping.getTimeZoneLinksList()
+                .stream()
+                .collect(toMap(x -> x.getAlternativeId(), x -> x.getPreferredId())));
+
+        // Handle replacements: either add the replacementId or replacedId depending on the
+        // replacementThreshold.
+        putAllSafely(tzIdMap, countryMapping.getTimeZoneReplacementsList()
+                .stream()
+                .collect(toMap(x -> x.getReplacedId(), x -> {
+                    if (x.getFromMillis() <= replacementThreshold.toEpochMilli()) {
+                        return x.getReplacementId();
+                    } else {
+                        return x.getReplacedId();
+                    }
+                })));
+        return tzIdMap;
+    }
+
+    /**
+     * Like {@link Map#putAll(Map)} but throws an exception if {@code sourceMap} contains a key
+     * already present in {@code targetMap}.
+     */
+    private static void putAllSafely(Map<String, String> targetMap, Map<String, String> sourceMap) {
+        for (Map.Entry<String, String> entry : sourceMap.entrySet()) {
+            String oldMapping = targetMap.put(entry.getKey(), entry.getValue());
+            if (oldMapping != null) {
+                throw new IllegalArgumentException("Key is not unique: "
+                        + entry.getKey() + " maps to both "
+                        + entry.getValue() + " and " + oldMapping);
+            }
+        }
+    }
+}
diff --git a/input_tools/android/tzids/src/main/proto/tz_ids_proto.proto b/input_tools/android/tzids/src/main/proto/tz_ids_proto.proto
new file mode 100644
index 0000000..e92aae9
--- /dev/null
+++ b/input_tools/android/tzids/src/main/proto/tz_ids_proto.proto
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+option java_package = "com.android.timezone.tzids.proto";
+option java_multiple_files = false;
+
+package com.android.timezone.tzids.proto;
+
+// Information about Olson IDs used / preferred by Android.
+message TimeZoneIds {
+    // The IANA TZDB version the data was generated from.
+    optional string ianaVersion = 1;
+
+    // Information about IDs that are mapped to ISO 3166 Alpha-2 country codes.
+    repeated CountryMapping countryMappings = 2;
+}
+
+// Information about Olson IDs recognized by Android as being related to a country.
+message CountryMapping {
+    // The ISO 3166 Alpha-2 country code.
+    required string isoCode = 1;
+
+    // The IANA TZDB Olson IDs preferred by Android for the country.
+    repeated string timeZoneIds = 2;
+
+    // Links for time zones that are recognized as being for the country, but are not preferred.
+    // These links are for time zones that have always been equivalent.
+    // e.g. "GB-Eire" is linked to "Europe/London" because "GB-Eire"" is just an obsoleted synonym.
+    repeated TimeZoneLink timeZoneLinks = 3;
+
+    // Replacements for time zones where the replaced time zone is not identical to the replacement
+    // before some point in time. After that point in time, the two zones have been judged as
+    // equivalent. e.g. "America/Boise" has the same rules as "America/Phoenix" after Sun, 03 Feb
+    // 1974, so the two can be considered equivalent today, but not for dates before that.
+    repeated TimeZoneReplacement timeZoneReplacements = 4;
+}
+
+// An ID replacement when one time zone Olson ID is just direct synonym for another.
+message TimeZoneLink {
+    // The alternative Olson ID. This will typically be an obsoleted Olson ID.
+    required string alternativeId = 1;
+    // The Android preferred Olson ID. This will typically be a newer / more correct Olson ID.
+    required string preferredId = 2;
+}
+
+// The functional replacement of one time zone ID by another after a point in time.
+// Computed by looking at offset behavior / zone metadata.
+message TimeZoneReplacement {
+    // The Olson ID that was replaced / ceased to be distinct.
+    required string replacedId = 1;
+    // The Olson ID that is better / to use in place of replacedId on Android after fromMillis.
+    required string replacementId = 2;
+    // When replacementId replaced replacedId. Milliseconds from the start of the Unix epoch.
+    required int64 fromMillis = 3;
+}
diff --git a/input_tools/android/tzids/src/test/java/com/android/timezone/tzids/TimeZoneIdsTest.java b/input_tools/android/tzids/src/test/java/com/android/timezone/tzids/TimeZoneIdsTest.java
new file mode 100644
index 0000000..7d8fb63
--- /dev/null
+++ b/input_tools/android/tzids/src/test/java/com/android/timezone/tzids/TimeZoneIdsTest.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.timezone.tzids;
+
+import static java.time.ZoneOffset.UTC;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import com.android.timezone.tzids.proto.TzIdsProto;
+import org.junit.Test;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.Month;
+import java.util.HashMap;
+import java.util.Map;
+
+/** Tests for {@link TimeZoneIds}. */
+public final class TimeZoneIdsTest {
+
+    @Test
+    public void getCountryIdMap_links() throws Exception {
+        TzIdsProto.TimeZoneIds.Builder tzIdsBuilder = TzIdsProto.TimeZoneIds.newBuilder();
+
+        TzIdsProto.CountryMapping gb = TzIdsProto.CountryMapping.newBuilder()
+                .setIsoCode("gb")
+                .addTimeZoneIds("Europe/London")
+                .addTimeZoneLinks(createLink("GB", "Europe/London"))
+                .build();
+        tzIdsBuilder.addCountryMappings(gb);
+
+        TimeZoneIds tzIds = new TimeZoneIds(tzIdsBuilder.build());
+
+        Map<String, String> expectedMap = new HashMap<>();
+        expectedMap.put("Europe/London", "Europe/London");
+        expectedMap.put("GB", "Europe/London");
+
+        assertEquals(expectedMap, tzIds.getCountryIdMap("Gb", Instant.EPOCH));
+        assertEquals(expectedMap, tzIds.getCountryIdMap("GB", Instant.EPOCH));
+        assertEquals(expectedMap, tzIds.getCountryIdMap("gB", Instant.EPOCH));
+
+        assertEquals(expectedMap, tzIds.getCountryIdMap("gb", Instant.MIN));
+        assertEquals(expectedMap, tzIds.getCountryIdMap("gb", Instant.EPOCH));
+        assertEquals(expectedMap, tzIds.getCountryIdMap("gb", Instant.MAX));
+    }
+
+    @Test
+    public void getCountryIdMap_replacements() throws Exception {
+        TzIdsProto.TimeZoneIds.Builder tzIdsBuilder = TzIdsProto.TimeZoneIds.newBuilder();
+
+        // A much-simplified version of the US time zone IDs.
+        Instant boiseFrom = LocalDateTime.of(1974, Month.FEBRUARY, 3, 9, 0).toInstant(UTC);
+        Instant dakotaFrom = LocalDateTime.of(1992, Month.OCTOBER, 25, 8, 0).toInstant(UTC);
+        TzIdsProto.CountryMapping us = TzIdsProto.CountryMapping.newBuilder()
+                .setIsoCode("us")
+                .addTimeZoneIds("America/Phoenix")
+                .addTimeZoneIds("America/Chicago")
+                .addTimeZoneReplacements(
+                        createReplacement("America/Boise", "America/Phoenix", boiseFrom))
+                .addTimeZoneReplacements(
+                        createReplacement(
+                                "America/North_Dakota/Center", "America/Chicago", dakotaFrom))
+                .build();
+        tzIdsBuilder.addCountryMappings(us);
+
+        TimeZoneIds tzIds = new TimeZoneIds(tzIdsBuilder.build());
+
+        Map<String, String> baseExpectedMap = new HashMap<>();
+        baseExpectedMap.put("America/Phoenix", "America/Phoenix");
+        baseExpectedMap.put("America/Chicago", "America/Chicago");
+
+        // Before all replacements in effect.
+        {
+            Map<String, String> expectedMap = new HashMap<>(baseExpectedMap);
+            expectedMap.put("America/Boise", "America/Boise");
+            expectedMap.put("America/North_Dakota/Center", "America/North_Dakota/Center");
+
+            assertEquals(expectedMap, tzIds.getCountryIdMap("us", Instant.EPOCH));
+            assertEquals(expectedMap, tzIds.getCountryIdMap("us", boiseFrom.minusMillis(1)));
+        }
+
+        // One replacement in effect.
+        {
+            Map<String, String> expectedMap = new HashMap<>(baseExpectedMap);
+            expectedMap.put("America/Boise", "America/Phoenix");
+            expectedMap.put("America/North_Dakota/Center", "America/North_Dakota/Center");
+
+            assertEquals(expectedMap, tzIds.getCountryIdMap("us", boiseFrom));
+            assertEquals(expectedMap, tzIds.getCountryIdMap("us", dakotaFrom.minusMillis(1)));
+        }
+
+        // All replacements in effect.
+        {
+            Map<String, String> expectedMap = new HashMap<>(baseExpectedMap);
+            expectedMap.put("America/Boise", "America/Phoenix");
+            expectedMap.put("America/North_Dakota/Center", "America/Chicago");
+
+            assertEquals(expectedMap, tzIds.getCountryIdMap("us", dakotaFrom));
+            assertEquals(expectedMap,
+                    tzIds.getCountryIdMap("us", Instant.ofEpochMilli(Long.MAX_VALUE)));
+        }
+    }
+
+    @Test
+    public void getCountryCodeForZoneId() {
+        TzIdsProto.TimeZoneIds.Builder tzIdsBuilder = TzIdsProto.TimeZoneIds.newBuilder();
+
+        TzIdsProto.CountryMapping gb = TzIdsProto.CountryMapping.newBuilder()
+                .setIsoCode("gb")
+                .addTimeZoneIds("Europe/London")
+                .addTimeZoneLinks(createLink("GB", "Europe/London"))
+                .build();
+        tzIdsBuilder.addCountryMappings(gb);
+
+        Instant boiseFrom = LocalDateTime.of(1974, Month.FEBRUARY, 3, 9, 0).toInstant(UTC);
+        TzIdsProto.CountryMapping us = TzIdsProto.CountryMapping.newBuilder()
+                .setIsoCode("us")
+                .addTimeZoneIds("America/Phoenix")
+                .addTimeZoneLinks(createLink("US/Arizona", "America/Phoenix"))
+                .addTimeZoneReplacements(
+                        createReplacement("America/Boise", "America/Phoenix", boiseFrom))
+                .build();
+        tzIdsBuilder.addCountryMappings(us);
+
+        TimeZoneIds tzIds = new TimeZoneIds(tzIdsBuilder.build());
+        assertNull(tzIds.getCountryCodeForZoneId("FooBar"));
+        assertEquals("gb", tzIds.getCountryCodeForZoneId("GB"));
+        assertEquals("gb", tzIds.getCountryCodeForZoneId("Europe/London"));
+        assertEquals("us", tzIds.getCountryCodeForZoneId("America/Phoenix"));
+        assertEquals("us", tzIds.getCountryCodeForZoneId("US/Arizona"));
+        assertEquals("us", tzIds.getCountryCodeForZoneId("America/Boise"));
+    }
+
+    private static TzIdsProto.TimeZoneLink createLink(
+            String alternativeId, String preferredId) {
+        return TzIdsProto.TimeZoneLink.newBuilder()
+                .setAlternativeId(alternativeId)
+                .setPreferredId(preferredId)
+                .build();
+    }
+
+    private static TzIdsProto.TimeZoneReplacement createReplacement(String replacedId,
+            String replacementId, Instant from) {
+        return TzIdsProto.TimeZoneReplacement.newBuilder()
+                .setReplacedId(replacedId)
+                .setReplacementId(replacementId)
+                .setFromMillis(from.toEpochMilli())
+                .build();
+    }
+}
diff --git a/input_tools/android/tzlookup_generator/Android.bp b/input_tools/android/tzlookup_generator/Android.bp
index 7278dc5..cc15ada 100644
--- a/input_tools/android/tzlookup_generator/Android.bp
+++ b/input_tools/android/tzlookup_generator/Android.bp
@@ -14,26 +14,14 @@
 
 // Proto library
 java_library_host {
-    name: "countryzonesprotos",
+    name: "countryzones_protos",
 
     proto: {
         type: "full",
         include_dirs: ["external/protobuf/src"],
     },
 
-    srcs: ["src/main/proto/**/country_zones_file.proto"],
-}
-
-// Proto library
-java_library_host {
-    name: "tzaliasesprotos",
-
-    proto: {
-        type: "full",
-        include_dirs: ["external/protobuf/src"],
-    },
-
-    srcs: ["src/main/proto/**/tz_aliases_file.proto"],
+    srcs: ["src/main/proto/**/*.proto"],
 }
 
 java_library_host {
@@ -41,10 +29,9 @@
 
     srcs: ["src/main/java/**/*.java"],
     static_libs: [
-        "countryzonesprotos",
+        "countryzones_protos",
         "icu4j",
-        "libprotobuf-java-full",
-        "tzaliasesprotos",
+        "tzids",
         "tztools_common",
     ],
 }
diff --git a/input_tools/android/tzlookup_generator/README.android b/input_tools/android/tzlookup_generator/README.android
index 4c993d4..fbb4ac8 100644
--- a/input_tools/android/tzlookup_generator/README.android
+++ b/input_tools/android/tzlookup_generator/README.android
@@ -1,2 +1,12 @@
-This tool generates the tzlookup.xml file from the countryzones.txt (proto text) file.
-The tool also uses ICU4J and IANA data to synthesize some time zone metadata.
+This tool generates the tzlookup.xml file from the countryzones.txt (proto
+text) file. tzlookup.xml is used to associate zones with regions/countries. The
+tool uses ICU4J and IANA data to synthesize some time zone metadata.
+
+Besides tzlookup.xml, the tool also generates a tzids.prototxt file
+containing information about zone IDs, id links and "functionally identical"
+zones[1] within each known country. These can be used to map time zone IDs to
+Android's preferred set. This duplicates information from the tzlookup.xml, but
+it is easier to use from host tools than Android's tzlookup.xml's format.
+
+[1] i.e. zones that differed in the past but were "folded in" to other zones
+and are not distinct for future times according to IANA or ICU/CLDR.
diff --git a/input_tools/android/tzlookup_generator/src/main/java/com/android/libcore/timezone/tzlookup/TzLookupFile.java b/input_tools/android/tzlookup_generator/src/main/java/com/android/libcore/timezone/tzlookup/TzLookupFile.java
index 6a24d0f..446b857 100644
--- a/input_tools/android/tzlookup_generator/src/main/java/com/android/libcore/timezone/tzlookup/TzLookupFile.java
+++ b/input_tools/android/tzlookup_generator/src/main/java/com/android/libcore/timezone/tzlookup/TzLookupFile.java
@@ -15,7 +15,7 @@
  */
 package com.android.libcore.timezone.tzlookup;
 
-import com.android.libcore.timezone.tzaliases.proto.TzAliasesFile;
+import com.android.timezone.tzids.proto.TzIdsProto;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
@@ -185,37 +185,43 @@
             timeZoneMappings.add(timeZoneMapping);
         }
 
-        static TzAliasesFile.TimeZoneAliases createTimeZoneAliases(Country country) {
-            TzAliasesFile.TimeZoneAliases.Builder countryAliasesBuilder =
-                    TzAliasesFile.TimeZoneAliases.newBuilder();
+        static TzIdsProto.CountryMapping createCountryMappingProto(Country country) {
+            TzIdsProto.CountryMapping.Builder countryMappingBuilder =
+                    TzIdsProto.CountryMapping.newBuilder()
+                            .setIsoCode(country.isoCode);
             for (TzLookupFile.TimeZoneMapping timeZoneMapping : country.timeZoneMappings) {
                 String mappingTimeZoneId = timeZoneMapping.olsonId;
                 String notUsedReplacementId = timeZoneMapping.notAfterReplacementId;
                 Instant notUsedAfterInstant = timeZoneMapping.notUsedAfterInclusive;
-                if (notUsedReplacementId != null && notUsedAfterInstant != null) {
+                if (notUsedReplacementId == null && notUsedAfterInstant == null) {
+                    countryMappingBuilder.addTimeZoneIds(mappingTimeZoneId);
+                } else if (notUsedReplacementId != null && notUsedAfterInstant != null) {
                     String replacedTimeZoneId = mappingTimeZoneId;
-                    TzAliasesFile.TimeZoneReplacement timeZoneReplacement =
-                            TzAliasesFile.TimeZoneReplacement.newBuilder()
+                    TzIdsProto.TimeZoneReplacement timeZoneReplacement =
+                            TzIdsProto.TimeZoneReplacement.newBuilder()
                                     .setReplacedId(replacedTimeZoneId)
                                     .setReplacementId(notUsedReplacementId)
                                     .setFromMillis(notUsedAfterInstant.toEpochMilli())
                                     .build();
-                    countryAliasesBuilder.addTimeZoneReplacement(timeZoneReplacement);
+                    countryMappingBuilder.addTimeZoneReplacements(timeZoneReplacement);
+                } else {
+                    throw new IllegalArgumentException(
+                            "Malformed TimeZoneMapping:" + timeZoneMapping);
                 }
 
                 for (String alternativeZoneId : timeZoneMapping.alternativeZoneIds) {
                     // We could collapse links when notUsedReplacementId != null by using it instead
                     // of mappingTimeZoneId below, but that would potentially lose information.
-                    // Leave it to the downstream components to collapse aliases if they want to.
-                    TzAliasesFile.TimeZoneLink timeZoneLink =
-                            TzAliasesFile.TimeZoneLink.newBuilder()
+                    // Leave it to the downstream components to collapse links if they want to.
+                    TzIdsProto.TimeZoneLink timeZoneLink =
+                            TzIdsProto.TimeZoneLink.newBuilder()
                                     .setPreferredId(mappingTimeZoneId)
                                     .setAlternativeId(alternativeZoneId)
                                     .build();
-                    countryAliasesBuilder.addTimeZoneLink(timeZoneLink);
+                    countryMappingBuilder.addTimeZoneLinks(timeZoneLink);
                 }
             }
-            return countryAliasesBuilder.build();
+            return countryMappingBuilder.build();
         }
 
         static void writeXml(Country country, XMLStreamWriter writer)
diff --git a/input_tools/android/tzlookup_generator/src/main/java/com/android/libcore/timezone/tzlookup/TzLookupGenerator.java b/input_tools/android/tzlookup_generator/src/main/java/com/android/libcore/timezone/tzlookup/TzLookupGenerator.java
index 20ffe96..9bf86e0 100644
--- a/input_tools/android/tzlookup_generator/src/main/java/com/android/libcore/timezone/tzlookup/TzLookupGenerator.java
+++ b/input_tools/android/tzlookup_generator/src/main/java/com/android/libcore/timezone/tzlookup/TzLookupGenerator.java
@@ -15,23 +15,21 @@
  */
 package com.android.libcore.timezone.tzlookup;
 
-import com.android.libcore.timezone.tzaliases.proto.TzAliasesFile;
 import com.android.libcore.timezone.countryzones.proto.CountryZonesFile;
 import com.android.libcore.timezone.tzlookup.zonetree.CountryZoneTree;
 import com.android.libcore.timezone.tzlookup.zonetree.CountryZoneUsage;
 import com.android.libcore.timezone.util.Errors;
 import com.android.libcore.timezone.util.Errors.HaltExecutionException;
-import com.google.protobuf.TextFormat;
+import com.android.timezone.tzids.TimeZoneIds;
+import com.android.timezone.tzids.proto.TzIdsProto;
 import com.ibm.icu.util.BasicTimeZone;
 import com.ibm.icu.util.Calendar;
 import com.ibm.icu.util.GregorianCalendar;
 import com.ibm.icu.util.TimeZone;
 import com.ibm.icu.util.TimeZoneRule;
 
-import java.io.FileOutputStream;
+import java.io.File;
 import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.nio.charset.StandardCharsets;
 import java.text.ParseException;
 import java.time.Instant;
 import java.time.temporal.ChronoUnit;
@@ -43,10 +41,11 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
+
 import javax.xml.stream.XMLStreamException;
 
 /**
- * Generates Android's tzlookup.xml and zone alias file using ICU4J, Android's countryzones.txt
+ * Generates Android's tzlookup.xml and tzids.prototxt file using ICU4J, Android's countryzones.txt
  * file, and TZDB's backwards and zones.tab files.
  */
 public final class TzLookupGenerator {
@@ -80,7 +79,7 @@
     private final String zoneTabFileIn;
     private final String backwardFileIn;
     private final String tzLookupXmlFileOut;
-    private final String timeZoneAliasesFileOut;
+    private final String timeZoneIdsFileOut;
 
     /**
      * Executes the generator.
@@ -91,7 +90,7 @@
                     "usage: java com.android.libcore.timezone.tzlookup.TzLookupGenerator"
                             + " <[in] countryzones.txt file> <[in] zone.tab file>"
                             + " <[in] backward file>"
-                            + " <[out] tzlookup.xml file> <[out] zone alias file>");
+                            + " <[out] tzlookup.xml file> <[out] zone IDs file>");
             System.exit(0);
         }
         TzLookupGenerator tzLookupGenerator =
@@ -101,12 +100,12 @@
     }
 
     TzLookupGenerator(String countryZonesFileIn, String zoneTabFileIn, String backwardFileIn,
-            String tzLookupXmlFileOut, String timeZoneAliasesFileOut) {
+            String tzLookupXmlFileOut, String timeZoneIdsFileOut) {
         this.countryZonesFileIn = countryZonesFileIn;
         this.zoneTabFileIn = zoneTabFileIn;
         this.backwardFileIn = backwardFileIn;
         this.tzLookupXmlFileOut = tzLookupXmlFileOut;
-        this.timeZoneAliasesFileOut = timeZoneAliasesFileOut;
+        this.timeZoneIdsFileOut = timeZoneIdsFileOut;
     }
 
     boolean execute() {
@@ -166,7 +165,7 @@
 
             // Write the output structure if there wasn't an error.
             errors.throwIfError("Errors accumulated");
-            writeOutputData(outputData, tzLookupXmlFileOut, timeZoneAliasesFileOut, errors);
+            writeOutputData(outputData, tzLookupXmlFileOut, timeZoneIdsFileOut, errors);
             return true;
         } catch (HaltExecutionException e) {
             logError("Stopping due to fatal condition", e);
@@ -237,7 +236,7 @@
     }
 
     private static void writeOutputData(OutputData outputData,
-            String tzLookupXmlFileName, String timeZoneAliasesFileName, Errors errors)
+            String tzLookupXmlFileName, String timeZoneIdsFileName, Errors errors)
             throws HaltExecutionException {
         errors.pushScope("write " + tzLookupXmlFileName);
         try {
@@ -252,17 +251,15 @@
             errors.popScope();
         }
 
-        errors.pushScope("write " + timeZoneAliasesFileName);
-        try (OutputStreamWriter timeZoneAliasesWriter =
-                     new OutputStreamWriter(new FileOutputStream(timeZoneAliasesFileName),
-                             StandardCharsets.UTF_8)) {
-            // Write out the tz aliases file used during later stages of the pipeline.
-            logInfo("Writing " + timeZoneAliasesFileName);
+        errors.pushScope("write " + timeZoneIdsFileName);
+        try {
+            // Write out the tz IDs file used during later stages of the pipeline.
+            logInfo("Writing " + timeZoneIdsFileName);
 
-            TzAliasesFile.TimeZoneAliases timeZoneAliases = outputData.getTimeZoneAliases();
-            TextFormat.print(timeZoneAliases, timeZoneAliasesWriter);
+            TimeZoneIds timeZoneIds = outputData.getTimeZoneIds();
+            timeZoneIds.store(new File(timeZoneIdsFileName));
         } catch (IOException e) {
-            errors.addFatalAndHalt("Unable to write " + timeZoneAliasesFileName, e);
+            errors.addFatalAndHalt("Unable to write " + timeZoneIdsFileName, e);
         } finally {
             errors.popScope();
         }
@@ -276,9 +273,6 @@
         TzLookupFile.TimeZones timeZonesOut = new TzLookupFile.TimeZones(inputIanaVersion);
         TzLookupFile.CountryZones tzLookupCountryZones = new TzLookupFile.CountryZones();
         timeZonesOut.setCountryZones(tzLookupCountryZones);
-        TzAliasesFile.TimeZoneAliases.Builder timeZoneAliasesBuilder =
-                TzAliasesFile.TimeZoneAliases.newBuilder();
-        timeZoneAliasesBuilder.setIanaVersion(inputIanaVersion);
 
         // The time use when sampling the offsets for a zone.
         final long offsetSampleTimeMillis = getSampleOffsetTimeMillisForData(inputIanaVersion);
@@ -288,6 +282,9 @@
         // to WW2) so we start looking at the beginning of "this year".
         long everUseUtcStartTimeMillis = getYearStartTimeMillisForData(inputIanaVersion);
 
+        TzIdsProto.TimeZoneIds.Builder tzIdsBuilder = TzIdsProto.TimeZoneIds.newBuilder()
+                .setIanaVersion(inputIanaVersion);
+
         // Process each Country.
         for (CountryZonesFile.Country countryIn : countriesIn) {
             String isoCode = countryIn.getIsoCode();
@@ -307,10 +304,11 @@
             }
 
             tzLookupCountryZones.addCountry(countryOutputData.getTzLookupCountry());
-            timeZoneAliasesBuilder.mergeFrom(countryOutputData.getTimeZoneAliases());
+            tzIdsBuilder.addCountryMappings(countryOutputData.getTimeZoneIdsCountryMapping());
         }
         errors.throwIfError("One or more countries failed");
-        return new OutputData(timeZonesOut, timeZoneAliasesBuilder.build());
+        TimeZoneIds timeZoneIds = new TimeZoneIds(tzIdsBuilder.build());
+        return new OutputData(timeZonesOut, timeZoneIds);
     }
 
     private static CountryOutputData processCountry(long offsetSampleTimeMillis,
@@ -449,31 +447,12 @@
                 }
             }
 
-            // TimeZoneAliases contains only information that is available from Country so we can
+            // CountryMapping contains only information that is available from Country so we can
             // currently build one from the other.
-            // Note: Because TimeZoneAliases is driven from Android's country mapping data, the
-            // aliases will only include zone IDs that are linked to a country AND where the link
-            // stays within the scope of a ISO 3166 country code.
-            //
-            // For example, Europe/Guernsey is a link in IANA's data to Europe/London in the TZDB
-            // "europe" file. However, this is a cross-ISO code link: Europe/London is associated
-            // with ISO code "GB", Europe/Guernsey is associated with ISO code "GG". So
-            // Europe/Guernsey is not recognized as an alias for Europe/London and it will not
-            // appear in the TimeZoneAliases.
-            //
-            // This is the behavior we want: although the TZDB makes use of links as a shorthand
-            // when countries (often neighbours with complicated shared histories) have followed the
-            // same rules, Android's data almost exclusively uses region-specific time zone IDs on
-            // the assumption it is what users would prefer, e.g. if we didn't do this, it would be
-            // difficult for some users to understand why the exemplar location for their time zone
-            // is in a different region / country from the one they are in. We have had bugs to
-            // prove this for macro-areas like the EU that have harmonized time zone rules but where
-            // users still prefer (say) Europe/Paris over Europe/Berlin, where there has been no
-            // functional difference between their time zones for several decades.
-            TzAliasesFile.TimeZoneAliases timeZoneAliases =
-                    TzLookupFile.Country.createTimeZoneAliases(countryOut);
+            TzIdsProto.CountryMapping countryMappingProto =
+                    TzLookupFile.Country.createCountryMappingProto(countryOut);
 
-            return new CountryOutputData(countryOut, timeZoneAliases);
+            return new CountryOutputData(countryOut, countryMappingProto);
         } finally{
             // End of country processing.
             errors.popScope();
@@ -680,40 +659,39 @@
 
     private static class CountryOutputData {
         private final TzLookupFile.Country tzLookupCountry;
-        private final TzAliasesFile.TimeZoneAliases timeZoneAliases;
+        private final TzIdsProto.CountryMapping timeZoneIdsCountryMapping;
 
         private CountryOutputData(TzLookupFile.Country tzLookupCountry,
-                TzAliasesFile.TimeZoneAliases timeZoneAliases) {
+                TzIdsProto.CountryMapping timeZoneIdsCountryMapping) {
             this.tzLookupCountry = Objects.requireNonNull(tzLookupCountry);
-            this.timeZoneAliases = Objects.requireNonNull(timeZoneAliases);
+            this.timeZoneIdsCountryMapping = Objects.requireNonNull(timeZoneIdsCountryMapping);
         }
 
         private TzLookupFile.Country getTzLookupCountry() {
             return tzLookupCountry;
         }
 
-        private TzAliasesFile.TimeZoneAliases getTimeZoneAliases() {
-            return timeZoneAliases;
+        private TzIdsProto.CountryMapping getTimeZoneIdsCountryMapping() {
+            return timeZoneIdsCountryMapping;
         }
     }
 
     private static class OutputData {
 
         private final TzLookupFile.TimeZones tzLookupTimeZones;
-        private final TzAliasesFile.TimeZoneAliases timeZoneAliases;
+        private final TimeZoneIds timeZoneIds;
 
-        private OutputData(TzLookupFile.TimeZones tzLookupTimeZones,
-                TzAliasesFile.TimeZoneAliases timeZoneAliases) {
+        private OutputData(TzLookupFile.TimeZones tzLookupTimeZones, TimeZoneIds timeZoneIds) {
             this.tzLookupTimeZones = Objects.requireNonNull(tzLookupTimeZones);
-            this.timeZoneAliases = Objects.requireNonNull(timeZoneAliases);
+            this.timeZoneIds = Objects.requireNonNull(timeZoneIds);
         }
 
         private TzLookupFile.TimeZones getTzLookupTimeZones() {
             return tzLookupTimeZones;
         }
 
-        private TzAliasesFile.TimeZoneAliases getTimeZoneAliases() {
-            return timeZoneAliases;
+        private TimeZoneIds getTimeZoneIds() {
+            return timeZoneIds;
         }
     }
 }
diff --git a/input_tools/android/tzlookup_generator/src/main/proto/tz_aliases_file.proto b/input_tools/android/tzlookup_generator/src/main/proto/tz_aliases_file.proto
deleted file mode 100644
index c5ae52c..0000000
--- a/input_tools/android/tzlookup_generator/src/main/proto/tz_aliases_file.proto
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-
-option java_package = "com.android.libcore.timezone.tzaliases.proto";
-option java_multiple_files = false;
-
-package com.android.libcore.timezone.tzaliases.proto;
-
-// Information about Olson IDs used by Android.
-message TimeZoneAliases {
-    // The IANA TZDB version the data was generated from.
-    optional string ianaVersion = 1;
-    // Aliases that are always correct, i.e. obsoleted IDs.
-    repeated TimeZoneLink timeZoneLink = 2;
-    // Aliases that are time dependent. i.e. where time zones have become equivalent over time.
-    repeated TimeZoneReplacement timeZoneReplacement = 3;
-}
-
-// An alias / synonym when one time zone ID is just direct synonym for another.
-//
-// These will typically be as you'd expect from looking at the IANA tzdb backward file. However, the
-// IDs preferred by Android are determined by countryzones.txt, which could choose to continue using
-// an old ID, in which case the link may be reversed. This reversal is expected when an older
-// version of ICU doesn't have strings for a new ID because it was added after an Android release
-// launched.
-message TimeZoneLink {
-    // The alternative ID. This will typically be an obsoleted IANA ID.
-    required string alternativeId = 1;
-    // The Android preferred ID. This will typically be a newer / more correct Olson ID.
-    required string preferredId = 2;
-}
-
-// The functional replacement of one time zone by another after a point in time.
-// Computed by looking at offset behavior / zone metadata.
-message TimeZoneReplacement {
-    // The Olson ID that was replaced / ceased to be distinct.
-    required string replacedId = 1;
-    // The Olson ID that is the better / to use in place of replacedId.
-    required string replacementId = 2;
-    // When replacementId replaced replacedId. Milliseconds from the start of the Unix epoch.
-    required int64 fromMillis = 3;
-}
diff --git a/input_tools/android/tzlookup_generator/src/test/java/com/android/libcore/timezone/tzlookup/TzLookupGeneratorTest.java b/input_tools/android/tzlookup_generator/src/test/java/com/android/libcore/timezone/tzlookup/TzLookupGeneratorTest.java
index 373626a..a3d4976 100644
--- a/input_tools/android/tzlookup_generator/src/test/java/com/android/libcore/timezone/tzlookup/TzLookupGeneratorTest.java
+++ b/input_tools/android/tzlookup_generator/src/test/java/com/android/libcore/timezone/tzlookup/TzLookupGeneratorTest.java
@@ -16,9 +16,17 @@
 
 package com.android.libcore.timezone.tzlookup;
 
+import static com.android.libcore.timezone.countryzones.proto.CountryZonesFile.Country;
+import static com.android.libcore.timezone.testing.TestUtils.assertAbsent;
+import static com.android.libcore.timezone.testing.TestUtils.assertContains;
+import static com.android.libcore.timezone.testing.TestUtils.createFile;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import com.android.libcore.timezone.countryzones.proto.CountryZonesFile;
-import com.android.libcore.timezone.tzaliases.proto.TzAliasesFile;
 import com.android.libcore.timezone.testing.TestUtils;
+import com.android.timezone.tzids.proto.TzIdsProto;
 import com.google.protobuf.TextFormat;
 import com.ibm.icu.util.TimeZone;
 
@@ -38,14 +46,6 @@
 import java.util.Map;
 import java.util.stream.Collectors;
 
-import static com.android.libcore.timezone.countryzones.proto.CountryZonesFile.Country;
-import static com.android.libcore.timezone.testing.TestUtils.assertAbsent;
-import static com.android.libcore.timezone.testing.TestUtils.assertContains;
-import static com.android.libcore.timezone.testing.TestUtils.createFile;
-import static junit.framework.TestCase.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 public class TzLookupGeneratorTest {
 
     private static final String INVALID_TIME_ZONE_ID = "NOT_A_VALID_ID";
@@ -70,10 +70,10 @@
         String zoneTabFile = createZoneTabFile(gbZoneTabEntries);
         String backwardFile = createBackwardFile(createEmptyBackwardLinks());
         String tzLookupFile = createTempFileName("tzlookup");
-        String tzAliasFile = createTempFileName("tzalias");
+        String tzIdsFile = createTempFileName("tzids");
 
         TzLookupGenerator tzLookupGenerator = new TzLookupGenerator(
-                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzAliasFile);
+                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzIdsFile);
         assertFalse(tzLookupGenerator.execute());
     }
 
@@ -91,14 +91,14 @@
         String backwardFile = createBackwardFile(createEmptyBackwardLinks());
 
         String tzLookupFile = createTempFileName("tzlookup");
-        String tzAliasFile = createTempFileName("tzalias");
+        String tzIdsFile = createTempFileName("tzids");
 
         TzLookupGenerator tzLookupGenerator = new TzLookupGenerator(
-                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzAliasFile);
+                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzIdsFile);
         assertFalse(tzLookupGenerator.execute());
 
         assertFileMissing(tzLookupFile);
-        assertFileMissing(tzAliasFile);
+        assertFileMissing(tzIdsFile);
     }
 
     @Test
@@ -113,14 +113,14 @@
         String zoneTabFile = createZoneTabFile(createValidZoneTabEntriesGb());
 
         String tzLookupFile = createTempFileName("tzlookup");
-        String tzAliasFile = createTempFileName("tzalias");
+        String tzIdsFile = createTempFileName("tzids");
 
         TzLookupGenerator tzLookupGenerator = new TzLookupGenerator(
-                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzAliasFile);
+                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzIdsFile);
         assertFalse(tzLookupGenerator.execute());
 
         assertFileMissing(tzLookupFile);
-        assertFileMissing(tzAliasFile);
+        assertFileMissing(tzIdsFile);
     }
 
     @Test
@@ -140,14 +140,14 @@
         String zoneTabFile = createZoneTabFile(createValidZoneTabEntriesGb());
 
         String tzLookupFile = createTempFileName("tzlookup");
-        String tzAliasFile = createTempFileName("tzalias");
+        String tzIdsFile = createTempFileName("tzids");
 
         TzLookupGenerator tzLookupGenerator = new TzLookupGenerator(
-                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzAliasFile);
+                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzIdsFile);
         assertFalse(tzLookupGenerator.execute());
 
         assertFileMissing(tzLookupFile);
-        assertFileMissing(tzAliasFile);
+        assertFileMissing(tzIdsFile);
     }
 
     @Test
@@ -165,14 +165,14 @@
         String backwardFile = createBackwardFile(createEmptyBackwardLinks());
 
         String tzLookupFile = createTempFileName("tzlookup");
-        String tzAliasFile = createTempFileName("tzalias");
+        String tzIdsFile = createTempFileName("tzids");
 
         TzLookupGenerator tzLookupGenerator = new TzLookupGenerator(
-                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzAliasFile);
+                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzIdsFile);
         assertFalse(tzLookupGenerator.execute());
 
         assertFileMissing(tzLookupFile);
-        assertFileMissing(tzAliasFile);
+        assertFileMissing(tzIdsFile);
     }
 
     @Test
@@ -189,14 +189,14 @@
         String backwardFile = createBackwardFile(createEmptyBackwardLinks());
 
         String tzLookupFile = createTempFileName("tzlookup");
-        String tzAliasFile = createTempFileName("tzalias");
+        String tzIdsFile = createTempFileName("tzids");
 
         TzLookupGenerator tzLookupGenerator = new TzLookupGenerator(
-                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzAliasFile);
+                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzIdsFile);
         assertFalse(tzLookupGenerator.execute());
 
         assertFileMissing(tzLookupFile);
-        assertFileMissing(tzAliasFile);
+        assertFileMissing(tzIdsFile);
     }
 
     @Test
@@ -250,14 +250,14 @@
         String backwardFile = createBackwardFile(createEmptyBackwardLinks());
 
         String tzLookupFile = createTempFileName("tzlookup");
-        String tzAliasFile = createTempFileName("tzalias");
+        String tzIdsFile = createTempFileName("tzids");
 
         TzLookupGenerator tzLookupGenerator = new TzLookupGenerator(
-                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzAliasFile);
+                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzIdsFile);
         assertFalse(tzLookupGenerator.execute());
 
         assertFileMissing(tzLookupFile);
-        assertFileMissing(tzAliasFile);
+        assertFileMissing(tzIdsFile);
     }
 
     @Test
@@ -274,14 +274,14 @@
         String backwardFile = createBackwardFile(createEmptyBackwardLinks());
 
         String tzLookupFile = createTempFileName("tzlookup");
-        String tzAliasFile = createTempFileName("tzalias");
+        String tzIdsFile = createTempFileName("tzids");
 
         TzLookupGenerator tzLookupGenerator = new TzLookupGenerator(
-                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzAliasFile);
+                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzIdsFile);
         assertFalse(tzLookupGenerator.execute());
 
         assertFileMissing(tzLookupFile);
-        assertFileMissing(tzAliasFile);
+        assertFileMissing(tzIdsFile);
     }
 
     @Test
@@ -295,14 +295,14 @@
                 createZoneTabFile(createValidZoneTabEntriesFr(), createValidZoneTabEntriesUs());
         String backwardFile = createBackwardFile(createEmptyBackwardLinks());
         String tzLookupFile = createTempFileName("tzlookup");
-        String tzAliasFile = createTempFileName("tzalias");
+        String tzIdsFile = createTempFileName("tzids");
 
         TzLookupGenerator tzLookupGenerator = new TzLookupGenerator(
-                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzAliasFile);
+                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzIdsFile);
         assertFalse(tzLookupGenerator.execute());
 
         assertFileMissing(tzLookupFile);
-        assertFileMissing(tzAliasFile);
+        assertFileMissing(tzIdsFile);
     }
 
     @Test
@@ -318,14 +318,14 @@
         String zoneTabFile = createZoneTabFile(createValidZoneTabEntriesGb());
         String backwardFile = createBackwardFile(createEmptyBackwardLinks());
         String tzLookupFile = createTempFileName("tzlookup");
-        String tzAliasFile = createTempFileName("tzalias");
+        String tzIdsFile = createTempFileName("tzids");
 
         TzLookupGenerator tzLookupGenerator = new TzLookupGenerator(
-                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzAliasFile);
+                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzIdsFile);
         assertFalse(tzLookupGenerator.execute());
 
         assertFileMissing(tzLookupFile);
-        assertFileMissing(tzAliasFile);
+        assertFileMissing(tzIdsFile);
     }
 
     @Test
@@ -339,14 +339,14 @@
 
         String backwardFile = createBackwardFile(createEmptyBackwardLinks());
         String tzLookupFile = createTempFileName("tzlookup");
-        String tzAliasFile = createTempFileName("tzalias");
+        String tzIdsFile = createTempFileName("tzids");
 
         TzLookupGenerator tzLookupGenerator = new TzLookupGenerator(
-                countryZonesFile, zoneTabFileWithDupes, backwardFile, tzLookupFile, tzAliasFile);
+                countryZonesFile, zoneTabFileWithDupes, backwardFile, tzLookupFile, tzIdsFile);
         assertFalse(tzLookupGenerator.execute());
 
         assertFileMissing(tzLookupFile);
-        assertFileMissing(tzAliasFile);
+        assertFileMissing(tzIdsFile);
     }
 
     @Test
@@ -363,14 +363,14 @@
         String backwardFile = createBackwardFile(createEmptyBackwardLinks());
 
         String tzLookupFile = createTempFileName("tzlookup");
-        String tzAliasFile = createTempFileName("tzalias");
+        String tzIdsFile = createTempFileName("tzids");
 
         TzLookupGenerator tzLookupGenerator = new TzLookupGenerator(
-                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzAliasFile);
+                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzIdsFile);
         assertFalse(tzLookupGenerator.execute());
 
         assertFileMissing(tzLookupFile);
-        assertFileMissing(tzAliasFile);
+        assertFileMissing(tzIdsFile);
     }
 
     @Test
@@ -391,14 +391,14 @@
         String backwardFile = createBackwardFile(createEmptyBackwardLinks());
 
         String tzLookupFile = createTempFileName("tzlookup");
-        String tzAliasFile = createTempFileName("tzalias");
+        String tzIdsFile = createTempFileName("tzids");
 
         TzLookupGenerator tzLookupGenerator = new TzLookupGenerator(
-                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzAliasFile);
+                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzIdsFile);
         assertFalse(tzLookupGenerator.execute());
 
         assertFileMissing(tzLookupFile);
-        assertFileMissing(tzAliasFile);
+        assertFileMissing(tzIdsFile);
     }
 
     @Test
@@ -410,14 +410,14 @@
         String badBackwardFile = TestUtils.createFile(tempDir, "THIS IS NOT VALID");
 
         String tzLookupFile = createTempFileName("tzlookup");
-        String tzAliasFile = createTempFileName("tzalias");
+        String tzIdsFile = createTempFileName("tzids");
 
         TzLookupGenerator tzLookupGenerator = new TzLookupGenerator(
-                countryZonesFile, zoneTabFile, badBackwardFile, tzLookupFile, tzAliasFile);
+                countryZonesFile, zoneTabFile, badBackwardFile, tzLookupFile, tzIdsFile);
         assertFalse(tzLookupGenerator.execute());
 
         assertFileMissing(tzLookupFile);
-        assertFileMissing(tzAliasFile);
+        assertFileMissing(tzIdsFile);
     }
 
     @Test
@@ -441,11 +441,15 @@
         String expectedTzLookupXmlLine = "<id alts=\"GB\">Europe/London</id>\n";
         assertContains(outputData.tzLookupXml, expectedTzLookupXmlLine);
 
-        TzAliasesFile.TimeZoneAliases.Builder b = TzAliasesFile.TimeZoneAliases
+        TzIdsProto.TimeZoneIds.Builder tzIdsBuilder = TzIdsProto.TimeZoneIds
                 .newBuilder()
                 .setIanaVersion(TZDB_VERSION);
+        TzIdsProto.CountryMapping.Builder b = TzIdsProto.CountryMapping.newBuilder()
+                .setIsoCode("gb")
+                .addTimeZoneIds("Europe/London");
         addLink(b, "GB" /* alternativeId */, "Europe/London" /* preferredId */);
-        assertEquals(b.build(), outputData.timeZoneAliases);
+        tzIdsBuilder.addCountryMappings(b);
+        assertEquals(tzIdsBuilder.build(), outputData.timeZoneIds);
     }
 
     @Test
@@ -498,23 +502,33 @@
             assertContains(outputData.tzLookupXml, expectedTzLookupXmlLine);
         }
 
-        TzAliasesFile.TimeZoneAliases.Builder b = TzAliasesFile.TimeZoneAliases
+        TzIdsProto.TimeZoneIds.Builder tzIdsBuilder = TzIdsProto.TimeZoneIds
                 .newBuilder()
                 .setIanaVersion(TZDB_VERSION);
+
+        TzIdsProto.CountryMapping.Builder b = TzIdsProto.CountryMapping.newBuilder()
+                .setIsoCode("gl")
+                .addTimeZoneIds("America/Danmarkshavn")
+                .addTimeZoneIds("America/Scoresbysund")
+                .addTimeZoneIds("America/Godthab")
+                .addTimeZoneIds("America/Thule");
+
         // Because Android lists America/Nuuk as the aliasId in countryzones.txt, the link will
         // be reversed from the usual.
         addLink(b, "America/Nuuk" /* alternativeId */, "America/Godthab" /* preferredId */);
-        assertEquals(b.build(), outputData.timeZoneAliases);
+
+        tzIdsBuilder.addCountryMappings(b);
+        assertEquals(tzIdsBuilder.build(), outputData.timeZoneIds);
     }
 
-    private static void addLink(TzAliasesFile.TimeZoneAliases.Builder builder, String alternativeId,
+    private static void addLink(TzIdsProto.CountryMapping.Builder builder, String alternativeId,
             String preferredId) {
-        TzAliasesFile.TimeZoneLink link =
-                TzAliasesFile.TimeZoneLink.newBuilder()
+        TzIdsProto.TimeZoneLink link =
+                TzIdsProto.TimeZoneLink.newBuilder()
                         .setAlternativeId(alternativeId)
                         .setPreferredId(preferredId)
                         .build();
-        builder.addTimeZoneLink(link);
+        builder.addTimeZoneLinks(link);
     }
 
     @Test
@@ -658,9 +672,21 @@
             assertContains(outputData.tzLookupXml, expectedTzLookupXmlLine);
         }
         
-        TzAliasesFile.TimeZoneAliases.Builder b = TzAliasesFile.TimeZoneAliases
+        TzIdsProto.TimeZoneIds.Builder tzIdsBuilder = TzIdsProto.TimeZoneIds
                 .newBuilder()
                 .setIanaVersion(TZDB_VERSION);
+
+        TzIdsProto.CountryMapping.Builder b = TzIdsProto.CountryMapping.newBuilder()
+                .setIsoCode("us")
+                .addTimeZoneIds("America/New_York")
+                .addTimeZoneIds("America/Chicago")
+                .addTimeZoneIds("America/Denver")
+                .addTimeZoneIds("America/Phoenix")
+                .addTimeZoneIds("America/Los_Angeles")
+                .addTimeZoneIds("America/Anchorage")
+                .addTimeZoneIds("Pacific/Honolulu")
+                .addTimeZoneIds("America/Adak");
+
         addReplacement(b, 167814000000L, "America/New_York", "America/Detroit");
         addReplacement(b, 152089200000L, "America/New_York", "America/Kentucky/Louisville");
         addReplacement(b, 972802800000L, "America/New_York", "America/Kentucky/Monticello");
@@ -682,27 +708,29 @@
         addReplacement(b, 436363200000L, "America/Anchorage", "America/Nome");
         addReplacement(b, 1547978400000L, "America/Anchorage", "America/Metlakatla");
         addReplacement(b, 341402400000L, "America/Anchorage", "America/Sitka");;
-        assertEquals(b.build(), outputData.timeZoneAliases);
+
+        tzIdsBuilder.addCountryMappings(b);
+        assertEquals(tzIdsBuilder.build(), outputData.timeZoneIds);
     }
 
-    private static void addReplacement(TzAliasesFile.TimeZoneAliases.Builder builder,
+    private static void addReplacement(TzIdsProto.CountryMapping.Builder builder,
             long fromMillis, String replacementId, String replacedId) {
-        TzAliasesFile.TimeZoneReplacement replacement =
-                TzAliasesFile.TimeZoneReplacement.newBuilder()
+        TzIdsProto.TimeZoneReplacement replacement =
+                TzIdsProto.TimeZoneReplacement.newBuilder()
                         .setReplacedId(replacedId)
                         .setReplacementId(replacementId)
                         .setFromMillis(fromMillis)
                         .build();
-        builder.addTimeZoneReplacement(replacement);
+        builder.addTimeZoneReplacements(replacement);
     }
 
     static class OutputData {
         final String tzLookupXml;
-        final TzAliasesFile.TimeZoneAliases timeZoneAliases;
+        final TzIdsProto.TimeZoneIds timeZoneIds;
 
-        OutputData(String tzLookupXml, TzAliasesFile.TimeZoneAliases timeZoneAliases) {
+        OutputData(String tzLookupXml, TzIdsProto.TimeZoneIds timeZoneIds) {
             this.tzLookupXml = tzLookupXml;
-            this.timeZoneAliases = timeZoneAliases;
+            this.timeZoneIds = timeZoneIds;
         }
     }
 
@@ -717,22 +745,22 @@
         String backwardFile = createBackwardFile(backwardLinks);
 
         String tzLookupFile = createTempFileName("tzlookup");
-        String tzAliasFile = createTempFileName("tzalias");
+        String tzIdsFile = createTempFileName("tzids");
 
         TzLookupGenerator tzLookupGenerator = new TzLookupGenerator(
-                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzAliasFile);
+                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzIdsFile);
         assertTrue(tzLookupGenerator.execute());
 
         Path tzLookupFilePath = checkFileExists(tzLookupFile);
         String tzLookupXml = readFileToString(tzLookupFilePath);
 
-        Path tzAliasFilePath = checkFileExists(tzAliasFile);
-        String timeZoneAliasesText = readFileToString(tzAliasFilePath);
-        TzAliasesFile.TimeZoneAliases.Builder timeZoneAliasesBuilder =
-                TzAliasesFile.TimeZoneAliases.newBuilder();
-        TextFormat.merge(timeZoneAliasesText, timeZoneAliasesBuilder);
+        Path tzIdsFilePath = checkFileExists(tzIdsFile);
+        String timeZoneIdsText = readFileToString(tzIdsFilePath);
+        TzIdsProto.TimeZoneIds.Builder timeZoneIdsBuilder =
+                TzIdsProto.TimeZoneIds.newBuilder();
+        TextFormat.merge(timeZoneIdsText, timeZoneIdsBuilder);
 
-        return new OutputData(tzLookupXml, timeZoneAliasesBuilder.build());
+        return new OutputData(tzLookupXml, timeZoneIdsBuilder.build());
     }
 
     private void generateTzLookupXmlExpectFailure(CountryZonesFile.Country country,
@@ -746,10 +774,10 @@
         String backwardFile = createBackwardFile(backwardLinks);
 
         String tzLookupFile = createTempFileName("tzlookup");
-        String tzAliasFile = createTempFileName("tzalias");
+        String tzIdsFile = createTempFileName("tzids");
 
         TzLookupGenerator tzLookupGenerator = new TzLookupGenerator(
-                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzAliasFile);
+                countryZonesFile, zoneTabFile, backwardFile, tzLookupFile, tzIdsFile);
         assertFalse(tzLookupGenerator.execute());
     }
 
diff --git a/output_data/android/tzids.prototxt b/output_data/android/tzids.prototxt
new file mode 100644
index 0000000..c97b964
--- /dev/null
+++ b/output_data/android/tzids.prototxt
@@ -0,0 +1,1980 @@
+# Autogenerated file - DO NOT EDIT.
+ianaVersion: "2020a"
+countryMappings {
+  isoCode: "ad"
+  timeZoneIds: "Europe/Andorra"
+}
+countryMappings {
+  isoCode: "ae"
+  timeZoneIds: "Asia/Dubai"
+}
+countryMappings {
+  isoCode: "af"
+  timeZoneIds: "Asia/Kabul"
+}
+countryMappings {
+  isoCode: "ag"
+  timeZoneIds: "America/Antigua"
+}
+countryMappings {
+  isoCode: "ai"
+  timeZoneIds: "America/Anguilla"
+}
+countryMappings {
+  isoCode: "al"
+  timeZoneIds: "Europe/Tirane"
+}
+countryMappings {
+  isoCode: "am"
+  timeZoneIds: "Asia/Yerevan"
+}
+countryMappings {
+  isoCode: "ao"
+  timeZoneIds: "Africa/Luanda"
+}
+countryMappings {
+  isoCode: "aq"
+  timeZoneIds: "Antarctica/McMurdo"
+  timeZoneIds: "Antarctica/DumontDUrville"
+  timeZoneIds: "Antarctica/Casey"
+  timeZoneIds: "Antarctica/Davis"
+  timeZoneIds: "Antarctica/Mawson"
+  timeZoneIds: "Antarctica/Vostok"
+  timeZoneIds: "Antarctica/Syowa"
+  timeZoneIds: "Antarctica/Troll"
+  timeZoneIds: "Antarctica/Rothera"
+  timeZoneIds: "Antarctica/Palmer"
+}
+countryMappings {
+  isoCode: "ar"
+  timeZoneIds: "America/Argentina/Buenos_Aires"
+  timeZoneIds: "America/Argentina/San_Luis"
+  timeZoneLinks {
+    alternativeId: "America/Buenos_Aires"
+    preferredId: "America/Argentina/Buenos_Aires"
+  }
+  timeZoneLinks {
+    alternativeId: "America/Cordoba"
+    preferredId: "America/Argentina/Cordoba"
+  }
+  timeZoneLinks {
+    alternativeId: "America/Rosario"
+    preferredId: "America/Argentina/Cordoba"
+  }
+  timeZoneLinks {
+    alternativeId: "America/Mendoza"
+    preferredId: "America/Argentina/Mendoza"
+  }
+  timeZoneLinks {
+    alternativeId: "America/Jujuy"
+    preferredId: "America/Argentina/Jujuy"
+  }
+  timeZoneLinks {
+    alternativeId: "America/Argentina/ComodRivadavia"
+    preferredId: "America/Argentina/Catamarca"
+  }
+  timeZoneLinks {
+    alternativeId: "America/Catamarca"
+    preferredId: "America/Argentina/Catamarca"
+  }
+  timeZoneReplacements {
+    replacedId: "America/Argentina/Cordoba"
+    replacementId: "America/Argentina/Buenos_Aires"
+    fromMillis: 687931200000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Argentina/Mendoza"
+    replacementId: "America/Argentina/Buenos_Aires"
+    fromMillis: 1237082400000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Argentina/Tucuman"
+    replacementId: "America/Argentina/Buenos_Aires"
+    fromMillis: 1087099200000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Argentina/Salta"
+    replacementId: "America/Argentina/Buenos_Aires"
+    fromMillis: 1096171200000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Argentina/San_Juan"
+    replacementId: "America/Argentina/Buenos_Aires"
+    fromMillis: 1090728000000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Argentina/Jujuy"
+    replacementId: "America/Argentina/Buenos_Aires"
+    fromMillis: 687931200000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Argentina/Catamarca"
+    replacementId: "America/Argentina/Buenos_Aires"
+    fromMillis: 1087704000000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Argentina/La_Rioja"
+    replacementId: "America/Argentina/Buenos_Aires"
+    fromMillis: 687931200000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Argentina/Rio_Gallegos"
+    replacementId: "America/Argentina/Buenos_Aires"
+    fromMillis: 673588800000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Argentina/Ushuaia"
+    replacementId: "America/Argentina/Buenos_Aires"
+    fromMillis: 1087704000000
+  }
+}
+countryMappings {
+  isoCode: "as"
+  timeZoneIds: "Pacific/Pago_Pago"
+  timeZoneLinks {
+    alternativeId: "Pacific/Samoa"
+    preferredId: "Pacific/Pago_Pago"
+  }
+  timeZoneLinks {
+    alternativeId: "US/Samoa"
+    preferredId: "Pacific/Pago_Pago"
+  }
+}
+countryMappings {
+  isoCode: "at"
+  timeZoneIds: "Europe/Vienna"
+}
+countryMappings {
+  isoCode: "au"
+  timeZoneIds: "Australia/Sydney"
+  timeZoneIds: "Australia/Brisbane"
+  timeZoneIds: "Antarctica/Macquarie"
+  timeZoneIds: "Australia/Lord_Howe"
+  timeZoneIds: "Australia/Adelaide"
+  timeZoneIds: "Australia/Darwin"
+  timeZoneIds: "Australia/Perth"
+  timeZoneIds: "Australia/Eucla"
+  timeZoneLinks {
+    alternativeId: "Australia/ACT"
+    preferredId: "Australia/Sydney"
+  }
+  timeZoneLinks {
+    alternativeId: "Australia/Canberra"
+    preferredId: "Australia/Sydney"
+  }
+  timeZoneLinks {
+    alternativeId: "Australia/NSW"
+    preferredId: "Australia/Sydney"
+  }
+  timeZoneLinks {
+    alternativeId: "Australia/Victoria"
+    preferredId: "Australia/Melbourne"
+  }
+  timeZoneLinks {
+    alternativeId: "Australia/Tasmania"
+    preferredId: "Australia/Hobart"
+  }
+  timeZoneLinks {
+    alternativeId: "Australia/Queensland"
+    preferredId: "Australia/Brisbane"
+  }
+  timeZoneLinks {
+    alternativeId: "Australia/LHI"
+    preferredId: "Australia/Lord_Howe"
+  }
+  timeZoneLinks {
+    alternativeId: "Australia/South"
+    preferredId: "Australia/Adelaide"
+  }
+  timeZoneLinks {
+    alternativeId: "Australia/Yancowinna"
+    preferredId: "Australia/Broken_Hill"
+  }
+  timeZoneLinks {
+    alternativeId: "Australia/North"
+    preferredId: "Australia/Darwin"
+  }
+  timeZoneLinks {
+    alternativeId: "Australia/West"
+    preferredId: "Australia/Perth"
+  }
+  timeZoneReplacements {
+    replacedId: "Australia/Melbourne"
+    replacementId: "Australia/Sydney"
+    fromMillis: 796147200000
+  }
+  timeZoneReplacements {
+    replacedId: "Australia/Hobart"
+    replacementId: "Australia/Sydney"
+    fromMillis: 1193500800000
+  }
+  timeZoneReplacements {
+    replacedId: "Australia/Currie"
+    replacementId: "Australia/Sydney"
+    fromMillis: 37728000000
+  }
+  timeZoneReplacements {
+    replacedId: "Australia/Lindeman"
+    replacementId: "Australia/Brisbane"
+    fromMillis: 762883200000
+  }
+  timeZoneReplacements {
+    replacedId: "Australia/Broken_Hill"
+    replacementId: "Australia/Adelaide"
+    fromMillis: 796149000000
+  }
+}
+countryMappings {
+  isoCode: "aw"
+  timeZoneIds: "America/Aruba"
+}
+countryMappings {
+  isoCode: "ax"
+  timeZoneIds: "Europe/Mariehamn"
+}
+countryMappings {
+  isoCode: "az"
+  timeZoneIds: "Asia/Baku"
+}
+countryMappings {
+  isoCode: "ba"
+  timeZoneIds: "Europe/Sarajevo"
+}
+countryMappings {
+  isoCode: "bb"
+  timeZoneIds: "America/Barbados"
+}
+countryMappings {
+  isoCode: "bd"
+  timeZoneIds: "Asia/Dhaka"
+  timeZoneLinks {
+    alternativeId: "Asia/Dacca"
+    preferredId: "Asia/Dhaka"
+  }
+}
+countryMappings {
+  isoCode: "be"
+  timeZoneIds: "Europe/Brussels"
+}
+countryMappings {
+  isoCode: "bf"
+  timeZoneIds: "Africa/Ouagadougou"
+}
+countryMappings {
+  isoCode: "bg"
+  timeZoneIds: "Europe/Sofia"
+}
+countryMappings {
+  isoCode: "bh"
+  timeZoneIds: "Asia/Bahrain"
+}
+countryMappings {
+  isoCode: "bi"
+  timeZoneIds: "Africa/Bujumbura"
+}
+countryMappings {
+  isoCode: "bj"
+  timeZoneIds: "Africa/Porto-Novo"
+}
+countryMappings {
+  isoCode: "bl"
+  timeZoneIds: "America/St_Barthelemy"
+}
+countryMappings {
+  isoCode: "bm"
+  timeZoneIds: "Atlantic/Bermuda"
+}
+countryMappings {
+  isoCode: "bn"
+  timeZoneIds: "Asia/Brunei"
+}
+countryMappings {
+  isoCode: "bo"
+  timeZoneIds: "America/La_Paz"
+}
+countryMappings {
+  isoCode: "bq"
+  timeZoneIds: "America/Kralendijk"
+}
+countryMappings {
+  isoCode: "br"
+  timeZoneIds: "America/Noronha"
+  timeZoneIds: "America/Sao_Paulo"
+  timeZoneIds: "America/Manaus"
+  timeZoneIds: "America/Rio_Branco"
+  timeZoneLinks {
+    alternativeId: "Brazil/DeNoronha"
+    preferredId: "America/Noronha"
+  }
+  timeZoneLinks {
+    alternativeId: "Brazil/East"
+    preferredId: "America/Sao_Paulo"
+  }
+  timeZoneLinks {
+    alternativeId: "Brazil/West"
+    preferredId: "America/Manaus"
+  }
+  timeZoneLinks {
+    alternativeId: "America/Porto_Acre"
+    preferredId: "America/Rio_Branco"
+  }
+  timeZoneLinks {
+    alternativeId: "Brazil/Acre"
+    preferredId: "America/Rio_Branco"
+  }
+  timeZoneReplacements {
+    replacedId: "America/Bahia"
+    replacementId: "America/Sao_Paulo"
+    fromMillis: 1550368800000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Santarem"
+    replacementId: "America/Sao_Paulo"
+    fromMillis: 1214280000000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Recife"
+    replacementId: "America/Sao_Paulo"
+    fromMillis: 1330221600000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Fortaleza"
+    replacementId: "America/Sao_Paulo"
+    fromMillis: 972180000000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Belem"
+    replacementId: "America/Sao_Paulo"
+    fromMillis: 1013911200000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Maceio"
+    replacementId: "America/Sao_Paulo"
+    fromMillis: 824004000000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Araguaina"
+    replacementId: "America/Sao_Paulo"
+    fromMillis: 1361066400000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Cuiaba"
+    replacementId: "America/Manaus"
+    fromMillis: 1550372400000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Campo_Grande"
+    replacementId: "America/Manaus"
+    fromMillis: 1076814000000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Porto_Velho"
+    replacementId: "America/Manaus"
+    fromMillis: 761713200000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Boa_Vista"
+    replacementId: "America/Manaus"
+    fromMillis: 971578800000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Eirunepe"
+    replacementId: "America/Rio_Branco"
+    fromMillis: 761716800000
+  }
+}
+countryMappings {
+  isoCode: "bs"
+  timeZoneIds: "America/Nassau"
+}
+countryMappings {
+  isoCode: "bt"
+  timeZoneIds: "Asia/Thimphu"
+  timeZoneLinks {
+    alternativeId: "Asia/Thimbu"
+    preferredId: "Asia/Thimphu"
+  }
+}
+countryMappings {
+  isoCode: "bw"
+  timeZoneIds: "Africa/Gaborone"
+}
+countryMappings {
+  isoCode: "by"
+  timeZoneIds: "Europe/Minsk"
+}
+countryMappings {
+  isoCode: "bz"
+  timeZoneIds: "America/Belize"
+}
+countryMappings {
+  isoCode: "ca"
+  timeZoneIds: "America/Toronto"
+  timeZoneIds: "America/Vancouver"
+  timeZoneIds: "America/Edmonton"
+  timeZoneIds: "America/Winnipeg"
+  timeZoneIds: "America/Halifax"
+  timeZoneIds: "America/St_Johns"
+  timeZoneIds: "America/Blanc-Sablon"
+  timeZoneIds: "America/Atikokan"
+  timeZoneIds: "America/Regina"
+  timeZoneIds: "America/Whitehorse"
+  timeZoneLinks {
+    alternativeId: "America/Montreal"
+    preferredId: "America/Toronto"
+  }
+  timeZoneLinks {
+    alternativeId: "Canada/Eastern"
+    preferredId: "America/Toronto"
+  }
+  timeZoneLinks {
+    alternativeId: "Canada/Pacific"
+    preferredId: "America/Vancouver"
+  }
+  timeZoneLinks {
+    alternativeId: "Canada/Mountain"
+    preferredId: "America/Edmonton"
+  }
+  timeZoneLinks {
+    alternativeId: "Canada/Central"
+    preferredId: "America/Winnipeg"
+  }
+  timeZoneLinks {
+    alternativeId: "Canada/Atlantic"
+    preferredId: "America/Halifax"
+  }
+  timeZoneLinks {
+    alternativeId: "Canada/Newfoundland"
+    preferredId: "America/St_Johns"
+  }
+  timeZoneLinks {
+    alternativeId: "America/Coral_Harbour"
+    preferredId: "America/Atikokan"
+  }
+  timeZoneLinks {
+    alternativeId: "Canada/Saskatchewan"
+    preferredId: "America/Regina"
+  }
+  timeZoneLinks {
+    alternativeId: "Canada/Yukon"
+    preferredId: "America/Whitehorse"
+  }
+  timeZoneReplacements {
+    replacedId: "America/Moncton"
+    replacementId: "America/Halifax"
+    fromMillis: 1162098000000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Glace_Bay"
+    replacementId: "America/Halifax"
+    fromMillis: 57733200000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Goose_Bay"
+    replacementId: "America/Halifax"
+    fromMillis: 1299996000000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Thunder_Bay"
+    replacementId: "America/Toronto"
+    fromMillis: 120636000000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Iqaluit"
+    replacementId: "America/Toronto"
+    fromMillis: 972802800000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Nipigon"
+    replacementId: "America/Toronto"
+    fromMillis: 89186400000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Pangnirtung"
+    replacementId: "America/Toronto"
+    fromMillis: 796806000000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Swift_Current"
+    replacementId: "America/Winnipeg"
+    fromMillis: 73472400000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Rankin_Inlet"
+    replacementId: "America/Winnipeg"
+    fromMillis: 1130659200000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Rainy_River"
+    replacementId: "America/Winnipeg"
+    fromMillis: 986112000000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Resolute"
+    replacementId: "America/Winnipeg"
+    fromMillis: 1173600000000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Yellowknife"
+    replacementId: "America/Edmonton"
+    fromMillis: 309945600000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Dawson_Creek"
+    replacementId: "America/Edmonton"
+    fromMillis: 1583661600000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Creston"
+    replacementId: "America/Edmonton"
+    fromMillis: 84013200000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Fort_Nelson"
+    replacementId: "America/Edmonton"
+    fromMillis: 1425808800000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Inuvik"
+    replacementId: "America/Edmonton"
+    fromMillis: 294228000000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Cambridge_Bay"
+    replacementId: "America/Edmonton"
+    fromMillis: 986115600000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Dawson"
+    replacementId: "America/Edmonton"
+    fromMillis: 120646800000
+  }
+}
+countryMappings {
+  isoCode: "cc"
+  timeZoneIds: "Indian/Cocos"
+}
+countryMappings {
+  isoCode: "cd"
+  timeZoneIds: "Africa/Lubumbashi"
+  timeZoneIds: "Africa/Kinshasa"
+}
+countryMappings {
+  isoCode: "cf"
+  timeZoneIds: "Africa/Bangui"
+}
+countryMappings {
+  isoCode: "cg"
+  timeZoneIds: "Africa/Brazzaville"
+}
+countryMappings {
+  isoCode: "ch"
+  timeZoneIds: "Europe/Zurich"
+}
+countryMappings {
+  isoCode: "ci"
+  timeZoneIds: "Africa/Abidjan"
+  timeZoneLinks {
+    alternativeId: "Africa/Timbuktu"
+    preferredId: "Africa/Abidjan"
+  }
+}
+countryMappings {
+  isoCode: "ck"
+  timeZoneIds: "Pacific/Rarotonga"
+}
+countryMappings {
+  isoCode: "cl"
+  timeZoneIds: "America/Punta_Arenas"
+  timeZoneIds: "America/Santiago"
+  timeZoneIds: "Pacific/Easter"
+  timeZoneLinks {
+    alternativeId: "Chile/Continental"
+    preferredId: "America/Santiago"
+  }
+  timeZoneLinks {
+    alternativeId: "Chile/EasterIsland"
+    preferredId: "Pacific/Easter"
+  }
+}
+countryMappings {
+  isoCode: "cm"
+  timeZoneIds: "Africa/Douala"
+}
+countryMappings {
+  isoCode: "cn"
+  timeZoneIds: "Asia/Shanghai"
+  timeZoneIds: "Asia/Urumqi"
+  timeZoneLinks {
+    alternativeId: "Asia/Chongqing"
+    preferredId: "Asia/Shanghai"
+  }
+  timeZoneLinks {
+    alternativeId: "Asia/Chungking"
+    preferredId: "Asia/Shanghai"
+  }
+  timeZoneLinks {
+    alternativeId: "Asia/Harbin"
+    preferredId: "Asia/Shanghai"
+  }
+  timeZoneLinks {
+    alternativeId: "PRC"
+    preferredId: "Asia/Shanghai"
+  }
+  timeZoneLinks {
+    alternativeId: "Asia/Kashgar"
+    preferredId: "Asia/Urumqi"
+  }
+}
+countryMappings {
+  isoCode: "co"
+  timeZoneIds: "America/Bogota"
+}
+countryMappings {
+  isoCode: "cr"
+  timeZoneIds: "America/Costa_Rica"
+}
+countryMappings {
+  isoCode: "cu"
+  timeZoneIds: "America/Havana"
+  timeZoneLinks {
+    alternativeId: "Cuba"
+    preferredId: "America/Havana"
+  }
+}
+countryMappings {
+  isoCode: "cv"
+  timeZoneIds: "Atlantic/Cape_Verde"
+}
+countryMappings {
+  isoCode: "cw"
+  timeZoneIds: "America/Curacao"
+}
+countryMappings {
+  isoCode: "cx"
+  timeZoneIds: "Indian/Christmas"
+}
+countryMappings {
+  isoCode: "cy"
+  timeZoneIds: "Asia/Nicosia"
+  timeZoneIds: "Asia/Famagusta"
+}
+countryMappings {
+  isoCode: "cz"
+  timeZoneIds: "Europe/Prague"
+}
+countryMappings {
+  isoCode: "de"
+  timeZoneIds: "Europe/Berlin"
+  timeZoneReplacements {
+    replacedId: "Europe/Busingen"
+    replacementId: "Europe/Berlin"
+    fromMillis: 338950800000
+  }
+}
+countryMappings {
+  isoCode: "dj"
+  timeZoneIds: "Africa/Djibouti"
+}
+countryMappings {
+  isoCode: "dk"
+  timeZoneIds: "Europe/Copenhagen"
+}
+countryMappings {
+  isoCode: "dm"
+  timeZoneIds: "America/Dominica"
+}
+countryMappings {
+  isoCode: "do"
+  timeZoneIds: "America/Santo_Domingo"
+}
+countryMappings {
+  isoCode: "dz"
+  timeZoneIds: "Africa/Algiers"
+}
+countryMappings {
+  isoCode: "ec"
+  timeZoneIds: "America/Guayaquil"
+  timeZoneIds: "Pacific/Galapagos"
+}
+countryMappings {
+  isoCode: "ee"
+  timeZoneIds: "Europe/Tallinn"
+}
+countryMappings {
+  isoCode: "eg"
+  timeZoneIds: "Africa/Cairo"
+  timeZoneLinks {
+    alternativeId: "Egypt"
+    preferredId: "Africa/Cairo"
+  }
+}
+countryMappings {
+  isoCode: "eh"
+  timeZoneIds: "Africa/El_Aaiun"
+}
+countryMappings {
+  isoCode: "er"
+  timeZoneIds: "Africa/Asmara"
+}
+countryMappings {
+  isoCode: "es"
+  timeZoneIds: "Europe/Madrid"
+  timeZoneIds: "Atlantic/Canary"
+  timeZoneReplacements {
+    replacedId: "Africa/Ceuta"
+    replacementId: "Europe/Madrid"
+    fromMillis: 496803600000
+  }
+}
+countryMappings {
+  isoCode: "et"
+  timeZoneIds: "Africa/Addis_Ababa"
+}
+countryMappings {
+  isoCode: "fi"
+  timeZoneIds: "Europe/Helsinki"
+}
+countryMappings {
+  isoCode: "fj"
+  timeZoneIds: "Pacific/Fiji"
+}
+countryMappings {
+  isoCode: "fk"
+  timeZoneIds: "Atlantic/Stanley"
+}
+countryMappings {
+  isoCode: "fm"
+  timeZoneIds: "Pacific/Pohnpei"
+  timeZoneIds: "Pacific/Kosrae"
+  timeZoneIds: "Pacific/Chuuk"
+  timeZoneLinks {
+    alternativeId: "Pacific/Ponape"
+    preferredId: "Pacific/Pohnpei"
+  }
+  timeZoneLinks {
+    alternativeId: "Pacific/Truk"
+    preferredId: "Pacific/Chuuk"
+  }
+  timeZoneLinks {
+    alternativeId: "Pacific/Yap"
+    preferredId: "Pacific/Chuuk"
+  }
+}
+countryMappings {
+  isoCode: "fo"
+  timeZoneIds: "Atlantic/Faroe"
+  timeZoneLinks {
+    alternativeId: "Atlantic/Faeroe"
+    preferredId: "Atlantic/Faroe"
+  }
+}
+countryMappings {
+  isoCode: "fr"
+  timeZoneIds: "Europe/Paris"
+}
+countryMappings {
+  isoCode: "ga"
+  timeZoneIds: "Africa/Libreville"
+}
+countryMappings {
+  isoCode: "gb"
+  timeZoneIds: "Europe/London"
+  timeZoneLinks {
+    alternativeId: "Europe/Belfast"
+    preferredId: "Europe/London"
+  }
+  timeZoneLinks {
+    alternativeId: "GB"
+    preferredId: "Europe/London"
+  }
+  timeZoneLinks {
+    alternativeId: "GB-Eire"
+    preferredId: "Europe/London"
+  }
+}
+countryMappings {
+  isoCode: "gd"
+  timeZoneIds: "America/Grenada"
+}
+countryMappings {
+  isoCode: "ge"
+  timeZoneIds: "Asia/Tbilisi"
+}
+countryMappings {
+  isoCode: "gf"
+  timeZoneIds: "America/Cayenne"
+}
+countryMappings {
+  isoCode: "gg"
+  timeZoneIds: "Europe/Guernsey"
+}
+countryMappings {
+  isoCode: "gh"
+  timeZoneIds: "Africa/Accra"
+}
+countryMappings {
+  isoCode: "gi"
+  timeZoneIds: "Europe/Gibraltar"
+}
+countryMappings {
+  isoCode: "gl"
+  timeZoneIds: "America/Danmarkshavn"
+  timeZoneIds: "America/Scoresbysund"
+  timeZoneIds: "America/Nuuk"
+  timeZoneIds: "America/Thule"
+  timeZoneLinks {
+    alternativeId: "America/Godthab"
+    preferredId: "America/Nuuk"
+  }
+}
+countryMappings {
+  isoCode: "gm"
+  timeZoneIds: "Africa/Banjul"
+}
+countryMappings {
+  isoCode: "gn"
+  timeZoneIds: "Africa/Conakry"
+}
+countryMappings {
+  isoCode: "gp"
+  timeZoneIds: "America/Guadeloupe"
+}
+countryMappings {
+  isoCode: "gq"
+  timeZoneIds: "Africa/Malabo"
+}
+countryMappings {
+  isoCode: "gr"
+  timeZoneIds: "Europe/Athens"
+}
+countryMappings {
+  isoCode: "gs"
+  timeZoneIds: "Atlantic/South_Georgia"
+}
+countryMappings {
+  isoCode: "gt"
+  timeZoneIds: "America/Guatemala"
+}
+countryMappings {
+  isoCode: "gu"
+  timeZoneIds: "Pacific/Guam"
+}
+countryMappings {
+  isoCode: "gw"
+  timeZoneIds: "Africa/Bissau"
+}
+countryMappings {
+  isoCode: "gy"
+  timeZoneIds: "America/Guyana"
+}
+countryMappings {
+  isoCode: "hk"
+  timeZoneIds: "Asia/Hong_Kong"
+  timeZoneLinks {
+    alternativeId: "Hongkong"
+    preferredId: "Asia/Hong_Kong"
+  }
+}
+countryMappings {
+  isoCode: "hn"
+  timeZoneIds: "America/Tegucigalpa"
+}
+countryMappings {
+  isoCode: "hr"
+  timeZoneIds: "Europe/Zagreb"
+}
+countryMappings {
+  isoCode: "ht"
+  timeZoneIds: "America/Port-au-Prince"
+}
+countryMappings {
+  isoCode: "hu"
+  timeZoneIds: "Europe/Budapest"
+}
+countryMappings {
+  isoCode: "id"
+  timeZoneIds: "Asia/Jayapura"
+  timeZoneIds: "Asia/Makassar"
+  timeZoneIds: "Asia/Jakarta"
+  timeZoneLinks {
+    alternativeId: "Asia/Ujung_Pandang"
+    preferredId: "Asia/Makassar"
+  }
+  timeZoneReplacements {
+    replacedId: "Asia/Pontianak"
+    replacementId: "Asia/Jakarta"
+    fromMillis: 567964800000
+  }
+}
+countryMappings {
+  isoCode: "ie"
+  timeZoneIds: "Europe/Dublin"
+  timeZoneLinks {
+    alternativeId: "Eire"
+    preferredId: "Europe/Dublin"
+  }
+}
+countryMappings {
+  isoCode: "il"
+  timeZoneIds: "Asia/Jerusalem"
+  timeZoneLinks {
+    alternativeId: "Asia/Tel_Aviv"
+    preferredId: "Asia/Jerusalem"
+  }
+  timeZoneLinks {
+    alternativeId: "Israel"
+    preferredId: "Asia/Jerusalem"
+  }
+}
+countryMappings {
+  isoCode: "im"
+  timeZoneIds: "Europe/Isle_of_Man"
+}
+countryMappings {
+  isoCode: "in"
+  timeZoneIds: "Asia/Kolkata"
+  timeZoneLinks {
+    alternativeId: "Asia/Calcutta"
+    preferredId: "Asia/Kolkata"
+  }
+}
+countryMappings {
+  isoCode: "io"
+  timeZoneIds: "Indian/Chagos"
+}
+countryMappings {
+  isoCode: "iq"
+  timeZoneIds: "Asia/Baghdad"
+}
+countryMappings {
+  isoCode: "ir"
+  timeZoneIds: "Asia/Tehran"
+  timeZoneLinks {
+    alternativeId: "Iran"
+    preferredId: "Asia/Tehran"
+  }
+}
+countryMappings {
+  isoCode: "is"
+  timeZoneIds: "Atlantic/Reykjavik"
+  timeZoneLinks {
+    alternativeId: "Iceland"
+    preferredId: "Atlantic/Reykjavik"
+  }
+}
+countryMappings {
+  isoCode: "it"
+  timeZoneIds: "Europe/Rome"
+}
+countryMappings {
+  isoCode: "je"
+  timeZoneIds: "Europe/Jersey"
+}
+countryMappings {
+  isoCode: "jm"
+  timeZoneIds: "America/Jamaica"
+  timeZoneLinks {
+    alternativeId: "Jamaica"
+    preferredId: "America/Jamaica"
+  }
+}
+countryMappings {
+  isoCode: "jo"
+  timeZoneIds: "Asia/Amman"
+}
+countryMappings {
+  isoCode: "jp"
+  timeZoneIds: "Asia/Tokyo"
+  timeZoneLinks {
+    alternativeId: "Japan"
+    preferredId: "Asia/Tokyo"
+  }
+}
+countryMappings {
+  isoCode: "ke"
+  timeZoneIds: "Africa/Nairobi"
+  timeZoneLinks {
+    alternativeId: "Africa/Asmera"
+    preferredId: "Africa/Nairobi"
+  }
+}
+countryMappings {
+  isoCode: "kg"
+  timeZoneIds: "Asia/Bishkek"
+}
+countryMappings {
+  isoCode: "kh"
+  timeZoneIds: "Asia/Phnom_Penh"
+}
+countryMappings {
+  isoCode: "ki"
+  timeZoneIds: "Pacific/Kiritimati"
+  timeZoneIds: "Pacific/Enderbury"
+  timeZoneIds: "Pacific/Tarawa"
+}
+countryMappings {
+  isoCode: "km"
+  timeZoneIds: "Indian/Comoro"
+}
+countryMappings {
+  isoCode: "kn"
+  timeZoneIds: "America/St_Kitts"
+}
+countryMappings {
+  isoCode: "kp"
+  timeZoneIds: "Asia/Pyongyang"
+}
+countryMappings {
+  isoCode: "kr"
+  timeZoneIds: "Asia/Seoul"
+  timeZoneLinks {
+    alternativeId: "ROK"
+    preferredId: "Asia/Seoul"
+  }
+}
+countryMappings {
+  isoCode: "kw"
+  timeZoneIds: "Asia/Kuwait"
+}
+countryMappings {
+  isoCode: "ky"
+  timeZoneIds: "America/Cayman"
+}
+countryMappings {
+  isoCode: "kz"
+  timeZoneIds: "Asia/Almaty"
+  timeZoneIds: "Asia/Oral"
+  timeZoneReplacements {
+    replacedId: "Asia/Qostanay"
+    replacementId: "Asia/Almaty"
+    fromMillis: 1099170000000
+  }
+  timeZoneReplacements {
+    replacedId: "Asia/Aqtau"
+    replacementId: "Asia/Oral"
+    fromMillis: 1099173600000
+  }
+  timeZoneReplacements {
+    replacedId: "Asia/Qyzylorda"
+    replacementId: "Asia/Oral"
+    fromMillis: 1545328800000
+  }
+  timeZoneReplacements {
+    replacedId: "Asia/Aqtobe"
+    replacementId: "Asia/Oral"
+    fromMillis: 1545328800000
+  }
+  timeZoneReplacements {
+    replacedId: "Asia/Atyrau"
+    replacementId: "Asia/Oral"
+    fromMillis: 922572000000
+  }
+}
+countryMappings {
+  isoCode: "la"
+  timeZoneIds: "Asia/Vientiane"
+}
+countryMappings {
+  isoCode: "lb"
+  timeZoneIds: "Asia/Beirut"
+}
+countryMappings {
+  isoCode: "lc"
+  timeZoneIds: "America/St_Lucia"
+}
+countryMappings {
+  isoCode: "li"
+  timeZoneIds: "Europe/Vaduz"
+}
+countryMappings {
+  isoCode: "lk"
+  timeZoneIds: "Asia/Colombo"
+}
+countryMappings {
+  isoCode: "lr"
+  timeZoneIds: "Africa/Monrovia"
+}
+countryMappings {
+  isoCode: "ls"
+  timeZoneIds: "Africa/Maseru"
+}
+countryMappings {
+  isoCode: "lt"
+  timeZoneIds: "Europe/Vilnius"
+}
+countryMappings {
+  isoCode: "lu"
+  timeZoneIds: "Europe/Luxembourg"
+}
+countryMappings {
+  isoCode: "lv"
+  timeZoneIds: "Europe/Riga"
+}
+countryMappings {
+  isoCode: "ly"
+  timeZoneIds: "Africa/Tripoli"
+  timeZoneLinks {
+    alternativeId: "Libya"
+    preferredId: "Africa/Tripoli"
+  }
+}
+countryMappings {
+  isoCode: "ma"
+  timeZoneIds: "Africa/Casablanca"
+}
+countryMappings {
+  isoCode: "mc"
+  timeZoneIds: "Europe/Monaco"
+}
+countryMappings {
+  isoCode: "md"
+  timeZoneIds: "Europe/Chisinau"
+  timeZoneLinks {
+    alternativeId: "Europe/Tiraspol"
+    preferredId: "Europe/Chisinau"
+  }
+}
+countryMappings {
+  isoCode: "me"
+  timeZoneIds: "Europe/Podgorica"
+}
+countryMappings {
+  isoCode: "mf"
+  timeZoneIds: "America/Marigot"
+}
+countryMappings {
+  isoCode: "mg"
+  timeZoneIds: "Indian/Antananarivo"
+}
+countryMappings {
+  isoCode: "mh"
+  timeZoneIds: "Pacific/Majuro"
+  timeZoneLinks {
+    alternativeId: "Kwajalein"
+    preferredId: "Pacific/Kwajalein"
+  }
+  timeZoneReplacements {
+    replacedId: "Pacific/Kwajalein"
+    replacementId: "Pacific/Majuro"
+    fromMillis: 745934400000
+  }
+}
+countryMappings {
+  isoCode: "mk"
+  timeZoneIds: "Europe/Skopje"
+}
+countryMappings {
+  isoCode: "ml"
+  timeZoneIds: "Africa/Bamako"
+}
+countryMappings {
+  isoCode: "mm"
+  timeZoneIds: "Asia/Yangon"
+  timeZoneLinks {
+    alternativeId: "Asia/Rangoon"
+    preferredId: "Asia/Yangon"
+  }
+}
+countryMappings {
+  isoCode: "mn"
+  timeZoneIds: "Asia/Choibalsan"
+  timeZoneIds: "Asia/Ulaanbaatar"
+  timeZoneIds: "Asia/Hovd"
+  timeZoneLinks {
+    alternativeId: "Asia/Ulan_Bator"
+    preferredId: "Asia/Ulaanbaatar"
+  }
+}
+countryMappings {
+  isoCode: "mo"
+  timeZoneIds: "Asia/Macau"
+  timeZoneLinks {
+    alternativeId: "Asia/Macao"
+    preferredId: "Asia/Macau"
+  }
+}
+countryMappings {
+  isoCode: "mp"
+  timeZoneIds: "Pacific/Saipan"
+}
+countryMappings {
+  isoCode: "mq"
+  timeZoneIds: "America/Martinique"
+}
+countryMappings {
+  isoCode: "mr"
+  timeZoneIds: "Africa/Nouakchott"
+}
+countryMappings {
+  isoCode: "ms"
+  timeZoneIds: "America/Montserrat"
+}
+countryMappings {
+  isoCode: "mt"
+  timeZoneIds: "Europe/Malta"
+}
+countryMappings {
+  isoCode: "mu"
+  timeZoneIds: "Indian/Mauritius"
+}
+countryMappings {
+  isoCode: "mv"
+  timeZoneIds: "Indian/Maldives"
+}
+countryMappings {
+  isoCode: "mw"
+  timeZoneIds: "Africa/Blantyre"
+}
+countryMappings {
+  isoCode: "mx"
+  timeZoneIds: "America/Mexico_City"
+  timeZoneIds: "America/Matamoros"
+  timeZoneIds: "America/Cancun"
+  timeZoneIds: "America/Chihuahua"
+  timeZoneIds: "America/Hermosillo"
+  timeZoneIds: "America/Ojinaga"
+  timeZoneIds: "America/Tijuana"
+  timeZoneLinks {
+    alternativeId: "Mexico/General"
+    preferredId: "America/Mexico_City"
+  }
+  timeZoneLinks {
+    alternativeId: "Mexico/BajaSur"
+    preferredId: "America/Mazatlan"
+  }
+  timeZoneLinks {
+    alternativeId: "America/Ensenada"
+    preferredId: "America/Tijuana"
+  }
+  timeZoneLinks {
+    alternativeId: "America/Santa_Isabel"
+    preferredId: "America/Tijuana"
+  }
+  timeZoneLinks {
+    alternativeId: "Mexico/BajaNorte"
+    preferredId: "America/Tijuana"
+  }
+  timeZoneReplacements {
+    replacedId: "America/Merida"
+    replacementId: "America/Mexico_City"
+    fromMillis: 407653200000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Monterrey"
+    replacementId: "America/Mexico_City"
+    fromMillis: 594198000000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Bahia_Banderas"
+    replacementId: "America/Mexico_City"
+    fromMillis: 1270371600000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Mazatlan"
+    replacementId: "America/Chihuahua"
+    fromMillis: 891766800000
+  }
+}
+countryMappings {
+  isoCode: "my"
+  timeZoneIds: "Asia/Kuala_Lumpur"
+  timeZoneReplacements {
+    replacedId: "Asia/Kuching"
+    replacementId: "Asia/Kuala_Lumpur"
+    fromMillis: 378664200000
+  }
+}
+countryMappings {
+  isoCode: "mz"
+  timeZoneIds: "Africa/Maputo"
+}
+countryMappings {
+  isoCode: "na"
+  timeZoneIds: "Africa/Windhoek"
+}
+countryMappings {
+  isoCode: "nc"
+  timeZoneIds: "Pacific/Noumea"
+}
+countryMappings {
+  isoCode: "ne"
+  timeZoneIds: "Africa/Niamey"
+}
+countryMappings {
+  isoCode: "nf"
+  timeZoneIds: "Pacific/Norfolk"
+}
+countryMappings {
+  isoCode: "ng"
+  timeZoneIds: "Africa/Lagos"
+}
+countryMappings {
+  isoCode: "ni"
+  timeZoneIds: "America/Managua"
+}
+countryMappings {
+  isoCode: "nl"
+  timeZoneIds: "Europe/Amsterdam"
+}
+countryMappings {
+  isoCode: "no"
+  timeZoneIds: "Europe/Oslo"
+  timeZoneLinks {
+    alternativeId: "Atlantic/Jan_Mayen"
+    preferredId: "Europe/Oslo"
+  }
+}
+countryMappings {
+  isoCode: "np"
+  timeZoneIds: "Asia/Kathmandu"
+  timeZoneLinks {
+    alternativeId: "Asia/Katmandu"
+    preferredId: "Asia/Kathmandu"
+  }
+}
+countryMappings {
+  isoCode: "nr"
+  timeZoneIds: "Pacific/Nauru"
+}
+countryMappings {
+  isoCode: "nu"
+  timeZoneIds: "Pacific/Niue"
+}
+countryMappings {
+  isoCode: "nz"
+  timeZoneIds: "Pacific/Auckland"
+  timeZoneIds: "Pacific/Chatham"
+  timeZoneLinks {
+    alternativeId: "Antarctica/South_Pole"
+    preferredId: "Pacific/Auckland"
+  }
+  timeZoneLinks {
+    alternativeId: "NZ"
+    preferredId: "Pacific/Auckland"
+  }
+  timeZoneLinks {
+    alternativeId: "NZ-CHAT"
+    preferredId: "Pacific/Chatham"
+  }
+}
+countryMappings {
+  isoCode: "om"
+  timeZoneIds: "Asia/Muscat"
+}
+countryMappings {
+  isoCode: "pa"
+  timeZoneIds: "America/Panama"
+}
+countryMappings {
+  isoCode: "pe"
+  timeZoneIds: "America/Lima"
+}
+countryMappings {
+  isoCode: "pf"
+  timeZoneIds: "Pacific/Gambier"
+  timeZoneIds: "Pacific/Marquesas"
+  timeZoneIds: "Pacific/Tahiti"
+}
+countryMappings {
+  isoCode: "pg"
+  timeZoneIds: "Pacific/Port_Moresby"
+  timeZoneIds: "Pacific/Bougainville"
+}
+countryMappings {
+  isoCode: "ph"
+  timeZoneIds: "Asia/Manila"
+}
+countryMappings {
+  isoCode: "pk"
+  timeZoneIds: "Asia/Karachi"
+}
+countryMappings {
+  isoCode: "pl"
+  timeZoneIds: "Europe/Warsaw"
+  timeZoneLinks {
+    alternativeId: "Poland"
+    preferredId: "Europe/Warsaw"
+  }
+}
+countryMappings {
+  isoCode: "pm"
+  timeZoneIds: "America/Miquelon"
+}
+countryMappings {
+  isoCode: "pn"
+  timeZoneIds: "Pacific/Pitcairn"
+}
+countryMappings {
+  isoCode: "pr"
+  timeZoneIds: "America/Puerto_Rico"
+}
+countryMappings {
+  isoCode: "ps"
+  timeZoneIds: "Asia/Hebron"
+  timeZoneReplacements {
+    replacedId: "Asia/Gaza"
+    replacementId: "Asia/Hebron"
+    fromMillis: 1317330000000
+  }
+}
+countryMappings {
+  isoCode: "pt"
+  timeZoneIds: "Europe/Lisbon"
+  timeZoneIds: "Atlantic/Azores"
+  timeZoneLinks {
+    alternativeId: "Portugal"
+    preferredId: "Europe/Lisbon"
+  }
+  timeZoneReplacements {
+    replacedId: "Atlantic/Madeira"
+    replacementId: "Europe/Lisbon"
+    fromMillis: 828234000000
+  }
+}
+countryMappings {
+  isoCode: "pw"
+  timeZoneIds: "Pacific/Palau"
+}
+countryMappings {
+  isoCode: "py"
+  timeZoneIds: "America/Asuncion"
+}
+countryMappings {
+  isoCode: "qa"
+  timeZoneIds: "Asia/Qatar"
+}
+countryMappings {
+  isoCode: "re"
+  timeZoneIds: "Indian/Reunion"
+}
+countryMappings {
+  isoCode: "ro"
+  timeZoneIds: "Europe/Bucharest"
+}
+countryMappings {
+  isoCode: "rs"
+  timeZoneIds: "Europe/Belgrade"
+}
+countryMappings {
+  isoCode: "ru"
+  timeZoneIds: "Asia/Kamchatka"
+  timeZoneIds: "Asia/Anadyr"
+  timeZoneIds: "Asia/Magadan"
+  timeZoneIds: "Asia/Sakhalin"
+  timeZoneIds: "Asia/Srednekolymsk"
+  timeZoneIds: "Asia/Vladivostok"
+  timeZoneIds: "Asia/Chita"
+  timeZoneIds: "Asia/Irkutsk"
+  timeZoneIds: "Asia/Krasnoyarsk"
+  timeZoneIds: "Asia/Novosibirsk"
+  timeZoneIds: "Asia/Barnaul"
+  timeZoneIds: "Asia/Omsk"
+  timeZoneIds: "Asia/Yekaterinburg"
+  timeZoneIds: "Europe/Samara"
+  timeZoneIds: "Europe/Saratov"
+  timeZoneIds: "Europe/Volgograd"
+  timeZoneIds: "Europe/Moscow"
+  timeZoneIds: "Europe/Kirov"
+  timeZoneIds: "Europe/Kaliningrad"
+  timeZoneLinks {
+    alternativeId: "W-SU"
+    preferredId: "Europe/Moscow"
+  }
+  timeZoneReplacements {
+    replacedId: "Asia/Ust-Nera"
+    replacementId: "Asia/Vladivostok"
+    fromMillis: 1315828800000
+  }
+  timeZoneReplacements {
+    replacedId: "Asia/Yakutsk"
+    replacementId: "Asia/Chita"
+    fromMillis: 1459015200000
+  }
+  timeZoneReplacements {
+    replacedId: "Asia/Khandyga"
+    replacementId: "Asia/Chita"
+    fromMillis: 1315832400000
+  }
+  timeZoneReplacements {
+    replacedId: "Asia/Novokuznetsk"
+    replacementId: "Asia/Krasnoyarsk"
+    fromMillis: 1459022400000
+  }
+  timeZoneReplacements {
+    replacedId: "Asia/Tomsk"
+    replacementId: "Asia/Barnaul"
+    fromMillis: 1464465600000
+  }
+  timeZoneReplacements {
+    replacedId: "Europe/Ulyanovsk"
+    replacementId: "Europe/Saratov"
+    fromMillis: 1480806000000
+  }
+  timeZoneReplacements {
+    replacedId: "Europe/Astrakhan"
+    replacementId: "Europe/Saratov"
+    fromMillis: 701823600000
+  }
+}
+countryMappings {
+  isoCode: "rw"
+  timeZoneIds: "Africa/Kigali"
+}
+countryMappings {
+  isoCode: "sa"
+  timeZoneIds: "Asia/Riyadh"
+}
+countryMappings {
+  isoCode: "sb"
+  timeZoneIds: "Pacific/Guadalcanal"
+}
+countryMappings {
+  isoCode: "sc"
+  timeZoneIds: "Indian/Mahe"
+}
+countryMappings {
+  isoCode: "sd"
+  timeZoneIds: "Africa/Khartoum"
+}
+countryMappings {
+  isoCode: "se"
+  timeZoneIds: "Europe/Stockholm"
+}
+countryMappings {
+  isoCode: "sg"
+  timeZoneIds: "Asia/Singapore"
+  timeZoneLinks {
+    alternativeId: "Singapore"
+    preferredId: "Asia/Singapore"
+  }
+}
+countryMappings {
+  isoCode: "sh"
+  timeZoneIds: "Atlantic/St_Helena"
+}
+countryMappings {
+  isoCode: "si"
+  timeZoneIds: "Europe/Ljubljana"
+}
+countryMappings {
+  isoCode: "sj"
+  timeZoneIds: "Arctic/Longyearbyen"
+}
+countryMappings {
+  isoCode: "sk"
+  timeZoneIds: "Europe/Bratislava"
+}
+countryMappings {
+  isoCode: "sl"
+  timeZoneIds: "Africa/Freetown"
+}
+countryMappings {
+  isoCode: "sm"
+  timeZoneIds: "Europe/San_Marino"
+}
+countryMappings {
+  isoCode: "sn"
+  timeZoneIds: "Africa/Dakar"
+}
+countryMappings {
+  isoCode: "so"
+  timeZoneIds: "Africa/Mogadishu"
+}
+countryMappings {
+  isoCode: "sr"
+  timeZoneIds: "America/Paramaribo"
+}
+countryMappings {
+  isoCode: "ss"
+  timeZoneIds: "Africa/Juba"
+}
+countryMappings {
+  isoCode: "st"
+  timeZoneIds: "Africa/Sao_Tome"
+}
+countryMappings {
+  isoCode: "sv"
+  timeZoneIds: "America/El_Salvador"
+}
+countryMappings {
+  isoCode: "sx"
+  timeZoneIds: "America/Lower_Princes"
+}
+countryMappings {
+  isoCode: "sy"
+  timeZoneIds: "Asia/Damascus"
+}
+countryMappings {
+  isoCode: "sz"
+  timeZoneIds: "Africa/Mbabane"
+}
+countryMappings {
+  isoCode: "tc"
+  timeZoneIds: "America/Grand_Turk"
+}
+countryMappings {
+  isoCode: "td"
+  timeZoneIds: "Africa/Ndjamena"
+}
+countryMappings {
+  isoCode: "tf"
+  timeZoneIds: "Indian/Kerguelen"
+}
+countryMappings {
+  isoCode: "tg"
+  timeZoneIds: "Africa/Lome"
+}
+countryMappings {
+  isoCode: "th"
+  timeZoneIds: "Asia/Bangkok"
+}
+countryMappings {
+  isoCode: "tj"
+  timeZoneIds: "Asia/Dushanbe"
+}
+countryMappings {
+  isoCode: "tk"
+  timeZoneIds: "Pacific/Fakaofo"
+}
+countryMappings {
+  isoCode: "tl"
+  timeZoneIds: "Asia/Dili"
+}
+countryMappings {
+  isoCode: "tm"
+  timeZoneIds: "Asia/Ashgabat"
+  timeZoneLinks {
+    alternativeId: "Asia/Ashkhabad"
+    preferredId: "Asia/Ashgabat"
+  }
+}
+countryMappings {
+  isoCode: "tn"
+  timeZoneIds: "Africa/Tunis"
+}
+countryMappings {
+  isoCode: "to"
+  timeZoneIds: "Pacific/Tongatapu"
+}
+countryMappings {
+  isoCode: "tr"
+  timeZoneIds: "Europe/Istanbul"
+  timeZoneLinks {
+    alternativeId: "Turkey"
+    preferredId: "Europe/Istanbul"
+  }
+}
+countryMappings {
+  isoCode: "tt"
+  timeZoneIds: "America/Port_of_Spain"
+  timeZoneLinks {
+    alternativeId: "America/Virgin"
+    preferredId: "America/Port_of_Spain"
+  }
+}
+countryMappings {
+  isoCode: "tv"
+  timeZoneIds: "Pacific/Funafuti"
+}
+countryMappings {
+  isoCode: "tw"
+  timeZoneIds: "Asia/Taipei"
+  timeZoneLinks {
+    alternativeId: "ROC"
+    preferredId: "Asia/Taipei"
+  }
+}
+countryMappings {
+  isoCode: "tz"
+  timeZoneIds: "Africa/Dar_es_Salaam"
+}
+countryMappings {
+  isoCode: "ua"
+  timeZoneIds: "Europe/Kiev"
+  timeZoneIds: "Europe/Simferopol"
+  timeZoneReplacements {
+    replacedId: "Europe/Zaporozhye"
+    replacementId: "Europe/Kiev"
+    fromMillis: 686102400000
+  }
+  timeZoneReplacements {
+    replacedId: "Europe/Uzhgorod"
+    replacementId: "Europe/Kiev"
+    fromMillis: 686091600000
+  }
+}
+countryMappings {
+  isoCode: "ug"
+  timeZoneIds: "Africa/Kampala"
+}
+countryMappings {
+  isoCode: "um"
+  timeZoneIds: "Pacific/Wake"
+  timeZoneIds: "Pacific/Midway"
+}
+countryMappings {
+  isoCode: "us"
+  timeZoneIds: "America/New_York"
+  timeZoneIds: "America/Chicago"
+  timeZoneIds: "America/Denver"
+  timeZoneIds: "America/Phoenix"
+  timeZoneIds: "America/Los_Angeles"
+  timeZoneIds: "America/Anchorage"
+  timeZoneIds: "Pacific/Honolulu"
+  timeZoneIds: "America/Adak"
+  timeZoneLinks {
+    alternativeId: "US/Eastern"
+    preferredId: "America/New_York"
+  }
+  timeZoneLinks {
+    alternativeId: "America/Louisville"
+    preferredId: "America/Kentucky/Louisville"
+  }
+  timeZoneLinks {
+    alternativeId: "US/Michigan"
+    preferredId: "America/Detroit"
+  }
+  timeZoneLinks {
+    alternativeId: "America/Fort_Wayne"
+    preferredId: "America/Indiana/Indianapolis"
+  }
+  timeZoneLinks {
+    alternativeId: "America/Indianapolis"
+    preferredId: "America/Indiana/Indianapolis"
+  }
+  timeZoneLinks {
+    alternativeId: "US/East-Indiana"
+    preferredId: "America/Indiana/Indianapolis"
+  }
+  timeZoneLinks {
+    alternativeId: "US/Central"
+    preferredId: "America/Chicago"
+  }
+  timeZoneLinks {
+    alternativeId: "America/Knox_IN"
+    preferredId: "America/Indiana/Knox"
+  }
+  timeZoneLinks {
+    alternativeId: "US/Indiana-Starke"
+    preferredId: "America/Indiana/Knox"
+  }
+  timeZoneLinks {
+    alternativeId: "America/Shiprock"
+    preferredId: "America/Denver"
+  }
+  timeZoneLinks {
+    alternativeId: "Navajo"
+    preferredId: "America/Denver"
+  }
+  timeZoneLinks {
+    alternativeId: "US/Mountain"
+    preferredId: "America/Denver"
+  }
+  timeZoneLinks {
+    alternativeId: "US/Arizona"
+    preferredId: "America/Phoenix"
+  }
+  timeZoneLinks {
+    alternativeId: "US/Pacific"
+    preferredId: "America/Los_Angeles"
+  }
+  timeZoneLinks {
+    alternativeId: "US/Alaska"
+    preferredId: "America/Anchorage"
+  }
+  timeZoneLinks {
+    alternativeId: "Pacific/Johnston"
+    preferredId: "Pacific/Honolulu"
+  }
+  timeZoneLinks {
+    alternativeId: "US/Hawaii"
+    preferredId: "Pacific/Honolulu"
+  }
+  timeZoneLinks {
+    alternativeId: "America/Atka"
+    preferredId: "America/Adak"
+  }
+  timeZoneLinks {
+    alternativeId: "US/Aleutian"
+    preferredId: "America/Adak"
+  }
+  timeZoneReplacements {
+    replacedId: "America/Kentucky/Louisville"
+    replacementId: "America/New_York"
+    fromMillis: 152089200000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Detroit"
+    replacementId: "America/New_York"
+    fromMillis: 167814000000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Indiana/Indianapolis"
+    replacementId: "America/New_York"
+    fromMillis: 1130652000000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Indiana/Vincennes"
+    replacementId: "America/New_York"
+    fromMillis: 1194159600000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Kentucky/Monticello"
+    replacementId: "America/New_York"
+    fromMillis: 972802800000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Indiana/Petersburg"
+    replacementId: "America/New_York"
+    fromMillis: 247042800000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Indiana/Winamac"
+    replacementId: "America/New_York"
+    fromMillis: 1173600000000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Indiana/Vevay"
+    replacementId: "America/New_York"
+    fromMillis: 89186400000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Indiana/Marengo"
+    replacementId: "America/New_York"
+    fromMillis: 183535200000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Menominee"
+    replacementId: "America/Chicago"
+    fromMillis: 104918400000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Indiana/Tell_City"
+    replacementId: "America/Chicago"
+    fromMillis: 1143964800000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Indiana/Knox"
+    replacementId: "America/Chicago"
+    fromMillis: 688546800000
+  }
+  timeZoneReplacements {
+    replacedId: "America/North_Dakota/Beulah"
+    replacementId: "America/Chicago"
+    fromMillis: 1289116800000
+  }
+  timeZoneReplacements {
+    replacedId: "America/North_Dakota/New_Salem"
+    replacementId: "America/Chicago"
+    fromMillis: 1067155200000
+  }
+  timeZoneReplacements {
+    replacedId: "America/North_Dakota/Center"
+    replacementId: "America/Chicago"
+    fromMillis: 720000000000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Boise"
+    replacementId: "America/Phoenix"
+    fromMillis: 129114000000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Juneau"
+    replacementId: "America/Anchorage"
+    fromMillis: 436359600000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Sitka"
+    replacementId: "America/Anchorage"
+    fromMillis: 341402400000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Nome"
+    replacementId: "America/Anchorage"
+    fromMillis: 436363200000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Metlakatla"
+    replacementId: "America/Anchorage"
+    fromMillis: 1547978400000
+  }
+  timeZoneReplacements {
+    replacedId: "America/Yakutat"
+    replacementId: "America/Anchorage"
+    fromMillis: 436356000000
+  }
+}
+countryMappings {
+  isoCode: "uy"
+  timeZoneIds: "America/Montevideo"
+}
+countryMappings {
+  isoCode: "uz"
+  timeZoneIds: "Asia/Tashkent"
+  timeZoneReplacements {
+    replacedId: "Asia/Samarkand"
+    replacementId: "Asia/Tashkent"
+    fromMillis: 670366800000
+  }
+}
+countryMappings {
+  isoCode: "va"
+  timeZoneIds: "Europe/Vatican"
+}
+countryMappings {
+  isoCode: "vc"
+  timeZoneIds: "America/St_Vincent"
+}
+countryMappings {
+  isoCode: "ve"
+  timeZoneIds: "America/Caracas"
+}
+countryMappings {
+  isoCode: "vg"
+  timeZoneIds: "America/Tortola"
+}
+countryMappings {
+  isoCode: "vi"
+  timeZoneIds: "America/St_Thomas"
+}
+countryMappings {
+  isoCode: "vn"
+  timeZoneIds: "Asia/Ho_Chi_Minh"
+  timeZoneLinks {
+    alternativeId: "Asia/Saigon"
+    preferredId: "Asia/Ho_Chi_Minh"
+  }
+}
+countryMappings {
+  isoCode: "vu"
+  timeZoneIds: "Pacific/Efate"
+}
+countryMappings {
+  isoCode: "wf"
+  timeZoneIds: "Pacific/Wallis"
+}
+countryMappings {
+  isoCode: "ws"
+  timeZoneIds: "Pacific/Apia"
+}
+countryMappings {
+  isoCode: "ye"
+  timeZoneIds: "Asia/Aden"
+}
+countryMappings {
+  isoCode: "yt"
+  timeZoneIds: "Indian/Mayotte"
+}
+countryMappings {
+  isoCode: "za"
+  timeZoneIds: "Africa/Johannesburg"
+}
+countryMappings {
+  isoCode: "zm"
+  timeZoneIds: "Africa/Lusaka"
+}
+countryMappings {
+  isoCode: "zw"
+  timeZoneIds: "Africa/Harare"
+}
diff --git a/update-tzdata.py b/update-tzdata.py
index f35beca..9cf565b 100755
--- a/update-tzdata.py
+++ b/update-tzdata.py
@@ -191,19 +191,19 @@
                          header_string])
 
 
-def BuildTzlookupAndTzAliases(iana_data_dir, tzalias_dest_dir):
+def BuildTzlookupAndTzIds(iana_data_dir):
   countryzones_source_file = '%s/android/countryzones.txt' % timezone_input_data_dir
   tzlookup_dest_file = '%s/android/tzlookup.xml' % timezone_output_data_dir
-  tzaliases_dest_file = '%s/tzaliases.txt' % tzalias_dest_dir
+  tzids_dest_file = '%s/android/tzids.prototxt' % timezone_output_data_dir
 
-  print('Calling TzLookupGenerator to create tzlookup.xml / tzaliases.txt...')
+  print('Calling TzLookupGenerator to create tzlookup.xml / tzids.prototxt...')
   tzdatautil.InvokeSoong(android_build_top, ['tzlookup_generator'])
 
   zone_tab_file = '%s/zone.tab' % iana_data_dir
   backward_file = '%s/backward' % iana_data_dir
   command = '%s/bin/tzlookup_generator' % android_host_out
   subprocess.check_call([command, countryzones_source_file, zone_tab_file, backward_file,
-                         tzlookup_dest_file, tzaliases_dest_file])
+                         tzlookup_dest_file, tzids_dest_file])
 
 
 def BuildTelephonylookup():
@@ -272,9 +272,7 @@
   ExtractTarFile(iana_data_tar_file, iana_data_dir)
   BuildTzdata(zic_binary_file, iana_data_dir, iana_data_version)
 
-  tzaliases_out_dir = '%s/android_intermediates' % tmp_dir
-  os.mkdir(tzaliases_out_dir)
-  BuildTzlookupAndTzAliases(iana_data_dir, tzaliases_out_dir)
+  BuildTzlookupAndTzIds(iana_data_dir)
 
   BuildTelephonylookup()