From a35d0f5f3dd8b6386aa9a521381a1cb2019bec1c Mon Sep 17 00:00:00 2001 From: Stephen Colebourne Date: Wed, 23 Nov 2022 21:49:10 +0000 Subject: [PATCH] Do not resolve most zone IDs This major change alters the timezone compiler Most Links are no longer auto-resolved Pre 1993 IDs that include a country name are still auto-resolved Code also added to handle new IDs where the JDK may not be updated --- src/main/java/org/joda/time/DateTimeZone.java | 15 +- .../org/joda/time/tz/ZoneInfoCompiler.java | 148 +++++++++++++++++- .../java/org/joda/time/TestDateTimeZone.java | 25 +-- .../java/org/joda/time/tz/TestCompiler.java | 62 +++++++- 4 files changed, 228 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/joda/time/DateTimeZone.java b/src/main/java/org/joda/time/DateTimeZone.java index 735c3ad7c..62628f3ed 100644 --- a/src/main/java/org/joda/time/DateTimeZone.java +++ b/src/main/java/org/joda/time/DateTimeZone.java @@ -1198,8 +1198,19 @@ public long adjustOffset(long instant, boolean earlierOrLater) { * * @return the closest matching TimeZone object */ - public java.util.TimeZone toTimeZone() { - return java.util.TimeZone.getTimeZone(iID); + public TimeZone toTimeZone() { + TimeZone converted = TimeZone.getTimeZone(iID); + // handle recent renames (since 2019) where the JDK may not have been updated yet + if (converted.getID().equals("GMT")) { + if (iID.equals("Europe/Kyiv")) { + converted = TimeZone.getTimeZone("Europe/Kiev"); + } else if (iID.equals("Pacific/Kanton")) { + converted = TimeZone.getTimeZone("Pacific/Enderbury"); + } else if (iID.equals("America/Nuuk")) { + converted = TimeZone.getTimeZone("America/Godthab"); + } + } + return converted; } /** diff --git a/src/main/java/org/joda/time/tz/ZoneInfoCompiler.java b/src/main/java/org/joda/time/tz/ZoneInfoCompiler.java index 4461b4486..c562e6684 100644 --- a/src/main/java/org/joda/time/tz/ZoneInfoCompiler.java +++ b/src/main/java/org/joda/time/tz/ZoneInfoCompiler.java @@ -25,6 +25,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -61,6 +62,143 @@ * @since 1.0 */ public class ZoneInfoCompiler { + + private static final List ANCIENT_IDS = Arrays.asList( + // changed in 1993 + "Australia/ACT", + "Australia/LHI", + "Australia/NSW", + "Australia/North", + "Australia/Queensland", + "Australia/South", + "Australia/Tasmania", + "Australia/Victoria", + "Australia/West", + "Australia/Yancowinna", + "Brazil/Acre", + "Brazil/DeNoronha", + "Brazil/East", + "Brazil/West", + "Canada/Atlantic", + "Canada/Central", + "Canada/Eastern", + "Canada/Mountain", + "Canada/Newfoundland", + "Canada/Pacific", + "Canada/Saskatchewan", + "Canada/Yukon", + "Chile/Continental", + "Chile/EasterIsland", + "Cuba", + "Egypt", + "Eire", + "Etc/GMT+0", + "Etc/GMT-0", + "Etc/GMT0", + "Etc/Greenwich", + "Etc/UCT", + "Etc/Universal", + "Etc/Zulu", + "GB", + "GB-Eire", + "GMT+0", + "GMT-0", + "GMT0", + "GMT", + "Greenwich", + "Hongkong", + "Iceland", + "Iran", + "Israel", + "Jamaica", + "Japan", + "Kwajalein", + "Libya", + "Mexico/BajaNorte", + "Mexico/BajaSur", + "Mexico/General", + "NZ", + "NZ-CHAT", + "Navajo", + "PRC", + "Poland", + "Portugal", + "ROC", + "ROK", + "Singapore", + "Turkey", + "UCT", + "US/Alaska", + "US/Aleutian", + "US/Arizona", + "US/Central", + "US/East-Indiana", + "US/Eastern", + "US/Hawaii", + "US/Indiana-Starke", + "US/Michigan", + "US/Mountain", + "US/Pacific", + "US/Samoa", + "UCT", + "UTC", + "Universal", + "W-SU", + "Zulu", + // removed + "US/Pacific-New"); + + // allowing these to be "good links" + // all these follow the modern naming convention that avoids country names +// Australia/Canberra +// America/Buenos_Aires +// America/Catamarca +// America/Cordoba +// America/Indianapolis +// America/Jujuy +// America/Knox_IN +// America/Louisville +// America/Mendoza +// America/Virgin +// Pacific/Samoa +// Africa/Asmera +// Africa/Timbuktu +// America/Argentina/ComodRivadavia +// America/Atka +// America/Coral_Harbour +// America/Ensenada +// America/Fort_Wayne +// America/Godthab +// America/Montreal +// America/Porto_Acre +// America/Rosario +// America/Santa_Isabel +// America/Shiprock +// Antarctica/South_Pole +// Asia/Ashkhabad +// Asia/Calcutta +// Asia/Chongqing +// Asia/Chungking +// Asia/Dacca +// Asia/Harbin +// Asia/Kashgar +// Asia/Katmandu +// Asia/Macao +// Asia/Rangoon +// Asia/Saigon +// Asia/Tel_Aviv +// Asia/Thimbu +// Asia/Ujung_Pandang +// Asia/Ulan_Bator +// Atlantic/Faeroe +// Atlantic/Jan_Mayen +// Europe/Belfast +// Europe/Tiraspol +// Pacific/Johnston +// Pacific/Ponape +// Pacific/Truk +// Pacific/Yap + static DateTimeOfYear cStartOfYear; static Chronology cLenientISO; @@ -353,8 +491,8 @@ static boolean test(String id, DateTimeZone tz) { public ZoneInfoCompiler() { iRuleSets = new HashMap(); iZones = new ArrayList(); - iGoodLinks = new ArrayList(); - iBackLinks = new ArrayList(); + iGoodLinks = new ArrayList(); // list of pairs, (good, bad)* + iBackLinks = new ArrayList(); // list of pairs, (good, bad)* } /** @@ -362,6 +500,8 @@ public ZoneInfoCompiler() { * * @param outputDir optional directory to write compiled data files to * @param sources optional list of source files to parse + * @return the map of ID to zone + * @throws IOException if an IO error occurs */ public Map compile(File outputDir, File[] sources) throws IOException { if (sources != null) { @@ -430,7 +570,7 @@ public Map compile(File outputDir, File[] sources) throws } } - // store "back" links as aliases (where name is permanently mapped + // store "back" links as aliases (where name is permanently mapped) for (int pass = 0; pass < 2; pass++) { for (int i = 0; i < iBackLinks.size(); i += 2) { String id = iBackLinks.get(i); @@ -551,7 +691,7 @@ public void parseDataFile(BufferedReader in, boolean backward) throws IOExceptio // links in "backward" are deprecated names // links in other files should be kept // special case a few to try to repair terrible damage to tzdb - if (backward || alias.equals("US/Pacific-New") || alias.startsWith("Etc/") || alias.equals("GMT")) { + if (alias.startsWith("Etc/") || ANCIENT_IDS.contains(alias)) { iBackLinks.add(real); iBackLinks.add(alias); } else { diff --git a/src/test/java/org/joda/time/TestDateTimeZone.java b/src/test/java/org/joda/time/TestDateTimeZone.java index 642018ada..fdf3c7186 100644 --- a/src/test/java/org/joda/time/TestDateTimeZone.java +++ b/src/test/java/org/joda/time/TestDateTimeZone.java @@ -1052,9 +1052,19 @@ public void testIsLocalDateTimeGap_NewYork() { //----------------------------------------------------------------------- public void testToTimeZone() { - DateTimeZone zone = DateTimeZone.forID("Europe/Paris"); - TimeZone tz = zone.toTimeZone(); - assertEquals("Europe/Paris", tz.getID()); + assertEquals("Europe/Paris", DateTimeZone.forID("Europe/Paris").toTimeZone().getID()); + + String kanton = DateTimeZone.forID("Pacific/Kanton").toTimeZone().getID(); + assertTrue(kanton.equals("Pacific/Kanton") || kanton.equals("Pacific/Enderbury")); + System.out.println(kanton); + + String gothab = DateTimeZone.forID("America/Nuuk").toTimeZone().getID(); + assertTrue(gothab.equals("America/Nuuk") || gothab.equals("America/Godthab")); + System.out.println(gothab); + + String kyiv = DateTimeZone.forID("Europe/Kyiv").toTimeZone().getID(); + assertTrue(kyiv.equals("Europe/Kyiv") || kyiv.equals("Europe/Kiev")); + System.out.println(kyiv); } //----------------------------------------------------------------------- @@ -1323,13 +1333,4 @@ public void testPatchedNameKeysGazaHistoric() throws Exception { assertEquals(false, str1.equals(str2)); } - //----------------------------------------------------------------------- - public void testIdNotAutoMapped_Asia_Yangon() throws Exception { - DateTimeZone zoneOld = DateTimeZone.forID("Asia/Rangoon"); - assertEquals(zoneOld.getID(), "Asia/Yangon"); - - DateTimeZone zoneNew = DateTimeZone.forID("Asia/Yangon"); - assertEquals(zoneNew.getID(), "Asia/Yangon"); - } - } diff --git a/src/test/java/org/joda/time/tz/TestCompiler.java b/src/test/java/org/joda/time/tz/TestCompiler.java index 9199eb3e2..129d59e47 100644 --- a/src/test/java/org/joda/time/tz/TestCompiler.java +++ b/src/test/java/org/joda/time/tz/TestCompiler.java @@ -16,21 +16,24 @@ package org.joda.time.tz; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.util.StringTokenizer; +import java.nio.charset.Charset; import java.util.NoSuchElementException; - -import junit.framework.TestCase; -import junit.framework.TestSuite; +import java.util.StringTokenizer; import org.joda.time.DateTime; import org.joda.time.DateTimeConstants; import org.joda.time.DateTimeZone; import org.joda.time.tz.ZoneInfoCompiler.DateTimeOfYear; +import junit.framework.TestCase; +import junit.framework.TestSuite; + /** * Test cases for ZoneInfoCompiler. * @@ -136,6 +139,57 @@ public void testCompile() throws Exception { TestBuilder.testReverseTransitions(tz, TestBuilder.AMERICA_LOS_ANGELES_DATA); } + public void testCompileAllTzdb() throws Exception { + String[] files = { + "src/main/java/org/joda/time/tz/src/africa", + "src/main/java/org/joda/time/tz/src/antarctica", + "src/main/java/org/joda/time/tz/src/asia", + "src/main/java/org/joda/time/tz/src/australasia", + "src/main/java/org/joda/time/tz/src/europe", + "src/main/java/org/joda/time/tz/src/northamerica", + "src/main/java/org/joda/time/tz/src/southamerica", + "src/main/java/org/joda/time/tz/src/etcetera", + "src/main/java/org/joda/time/tz/src/backward", + }; + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < files.length; i++) { + byte[] bytes = readAllBytes(files[i]); + buf.append(new String(bytes, Charset.forName("UTF-8"))); + } + + Provider provider = compileAndLoad(buf.toString()); + + // check some links + assertEquals("Europe/Kiev", provider.getZone("Europe/Kiev").getID()); + assertEquals("Europe/Kyiv", provider.getZone("Europe/Kyiv").getID()); + assertEquals("Asia/Rangoon", provider.getZone("Asia/Rangoon").getID()); + assertEquals("Asia/Yangon", provider.getZone("Asia/Yangon").getID()); + assertEquals("Europe/Lisbon", provider.getZone("Portugal").getID()); + assertEquals("Africa/Tripoli", provider.getZone("Libya").getID()); + assertEquals("Etc/GMT", provider.getZone("GMT").getID()); + assertEquals("Etc/GMT", provider.getZone("Etc/GMT").getID()); + assertEquals("Etc/GMT", provider.getZone("Etc/GMT0").getID()); + assertEquals("Etc/GMT+1", provider.getZone("Etc/GMT+1").getID()); + assertEquals("UTC", provider.getZone("UTC").getID()); + assertEquals("Etc/UTC", provider.getZone("Etc/UTC").getID()); + assertEquals("Australia/Currie", provider.getZone("Australia/Currie").getID()); + } + + private static byte[] readAllBytes(String fileName) throws IOException { + InputStream in = new FileInputStream(fileName); + try { + byte[] b = new byte[1024]; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int c; + while ((c = in.read(b)) != -1) { + out.write(b, 0, c); + } + return out.toByteArray(); + } finally { + in.close(); + } + } + public void testCompileOnBrokenTimeZoneFile() throws Exception { try { Provider provider = compileAndLoad(BROKEN_TIMEZONE_FILE);