13cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller/* 23cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller * Copyright (C) 2017 The Android Open Source Project 33cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller * 43cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller * Licensed under the Apache License, Version 2.0 (the "License"); 53cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller * you may not use this file except in compliance with the License. 63cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller * You may obtain a copy of the License at 73cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller * 83cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller * http://www.apache.org/licenses/LICENSE-2.0 93cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller * 103cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller * Unless required by applicable law or agreed to in writing, software 113cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller * distributed under the License is distributed on an "AS IS" BASIS, 123cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller * See the License for the specific language governing permissions and 143cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller * limitations under the License. 153cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller */ 163cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 173cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerpackage libcore.util; 183cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 193cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport org.junit.After; 203cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport org.junit.Before; 213cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport org.junit.Test; 223cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 233cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport android.icu.util.TimeZone; 243cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 253cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport java.io.IOException; 263cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport java.nio.charset.StandardCharsets; 273cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport java.nio.file.FileVisitResult; 283cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport java.nio.file.Files; 293cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport java.nio.file.Path; 303cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport java.nio.file.SimpleFileVisitor; 313cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport java.nio.file.attribute.BasicFileAttributes; 323cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport java.util.Arrays; 333cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport java.util.HashMap; 343cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport java.util.HashSet; 353cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport java.util.List; 363cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport java.util.Map; 373cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport java.util.Set; 383cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport java.util.stream.Collectors; 393cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 403cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport static org.junit.Assert.assertEquals; 413cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport static org.junit.Assert.assertNull; 423cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerimport static org.junit.Assert.fail; 433cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 443cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fullerpublic class TimeZoneFinderTest { 453cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 463cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private static final int HOUR_MILLIS = 60 * 60 * 1000; 473cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 483cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // Zones used in the tests. NEW_YORK_TZ and LONDON_TZ chosen because they never overlap but both 493cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // have DST. 503cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private static final TimeZone NEW_YORK_TZ = TimeZone.getTimeZone("America/New_York"); 513cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private static final TimeZone LONDON_TZ = TimeZone.getTimeZone("Europe/London"); 523cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // A zone that matches LONDON_TZ for WHEN_NO_DST. It does not have DST so differs for WHEN_DST. 533cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private static final TimeZone REYKJAVIK_TZ = TimeZone.getTimeZone("Atlantic/Reykjavik"); 543cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // Another zone that matches LONDON_TZ for WHEN_NO_DST. It does not have DST so differs for 553cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // WHEN_DST. 563cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private static final TimeZone UTC_TZ = TimeZone.getTimeZone("Etc/UTC"); 573cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 583cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // 22nd July 2017, 13:14:15 UTC (DST time in all the timezones used in these tests that observe 593cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // DST). 603cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private static final long WHEN_DST = 1500729255000L; 613cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // 22nd January 2018, 13:14:15 UTC (non-DST time in all timezones used in these tests). 623cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private static final long WHEN_NO_DST = 1516626855000L; 633cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 643cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private static final int LONDON_DST_OFFSET_MILLIS = HOUR_MILLIS; 653cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private static final int LONDON_NO_DST_OFFSET_MILLIS = 0; 663cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 673cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private static final int NEW_YORK_DST_OFFSET_MILLIS = -4 * HOUR_MILLIS; 683cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private static final int NEW_YORK_NO_DST_OFFSET_MILLIS = -5 * HOUR_MILLIS; 693cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 703cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private Path testDir; 713cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 723cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Before 733cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void setUp() throws Exception { 743cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller testDir = Files.createTempDirectory("TimeZoneFinderTest"); 753cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 763cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 773cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @After 783cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void tearDown() throws Exception { 793cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // Delete the testDir and all contents. 803cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller Files.walkFileTree(testDir, new SimpleFileVisitor<Path>() { 813cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Override 823cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) 833cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller throws IOException { 843cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller Files.delete(file); 853cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller return FileVisitResult.CONTINUE; 863cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 873cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 883cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Override 893cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public FileVisitResult postVisitDirectory(Path dir, IOException exc) 903cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller throws IOException { 913cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller Files.delete(dir); 923cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller return FileVisitResult.CONTINUE; 933cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 943cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller }); 953cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 963cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 973cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Test 983cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void createInstanceWithFallback() throws Exception { 993cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller String validXml1 = "<timezones>\n" 1003cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 1013cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n" 1023cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 1033cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 1043cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 1053cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"; 1063cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller String validXml2 = "<timezones>\n" 1073cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 1083cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n" 1093cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/Paris</id>\n" 1103cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 1113cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 1123cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"; 1133cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 1143cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller String invalidXml = "<foo></foo>\n"; 1153cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller checkValidateThrowsParserException(invalidXml); 1163cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 1173cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller String validFile1 = createFile(validXml1); 1183cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller String validFile2 = createFile(validXml2); 1193cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller String invalidFile = createFile(invalidXml); 1203cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller String missingFile = createMissingFile(); 1213cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 1223cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder file1ThenFile2 = 1233cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder.createInstanceWithFallback(validFile1, validFile2); 1243cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZonesEqual(zones("Europe/London"), file1ThenFile2.lookupTimeZonesByCountry("gb")); 1253cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 1263cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder missingFileThenFile1 = 1273cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder.createInstanceWithFallback(missingFile, validFile1); 1283cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZonesEqual(zones("Europe/London"), 1293cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller missingFileThenFile1.lookupTimeZonesByCountry("gb")); 1303cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 1313cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder file2ThenFile1 = 1323cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder.createInstanceWithFallback(validFile2, validFile1); 1333cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZonesEqual(zones("Europe/Paris"), file2ThenFile1.lookupTimeZonesByCountry("gb")); 1343cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 1353cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // We assume the file has been validated so an invalid file is not checked ahead of time. 1363cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // We will find out when we look something up. 1373cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder invalidThenValid = 1383cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder.createInstanceWithFallback(invalidFile, validFile1); 1393cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(invalidThenValid.lookupTimeZonesByCountry("gb")); 1403cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 1413cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // This is not a normal case: It would imply a define shipped without a file in /system! 1423cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder missingFiles = 1433cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder.createInstanceWithFallback(missingFile, missingFile); 1443cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(missingFiles.lookupTimeZonesByCountry("gb")); 1453cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 1463cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 1473cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Test 1483cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void xmlParsing_emptyFile() throws Exception { 1493cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller checkValidateThrowsParserException(""); 1503cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 1513cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 1523cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Test 1533cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void xmlParsing_unexpectedRootElement() throws Exception { 1543cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller checkValidateThrowsParserException("<foo></foo>\n"); 1553cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 1563cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 1573cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Test 1583cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void xmlParsing_missingCountryZones() throws Exception { 1593cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller checkValidateThrowsParserException("<timezones></timezones>\n"); 1603cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 1613cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 1623cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Test 1633cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void xmlParsing_noCountriesOk() throws Exception { 1643cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller validate("<timezones>\n" 1653cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 1663cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 1673cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 1683cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 1693cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 1703cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Test 1713cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void xmlParsing_unexpectedComments() throws Exception { 1723cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder finder = validate("<timezones>\n" 1733cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 1743cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n" 1753cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <!-- This is a comment -->" 1763cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 1773cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 1783cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 1793cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 1803cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb")); 1813cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 1823cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // This is a crazy comment, but also helps prove that TEXT nodes are coalesced by the 1833cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // parser. 1843cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller finder = validate("<timezones>\n" 1853cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 1863cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n" 1873cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/<!-- Don't freak out! -->London</id>\n" 1883cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 1893cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 1903cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 1913cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb")); 1923cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 1933cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 1943cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Test 1953cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void xmlParsing_unexpectedElementsIgnored() throws Exception { 1963cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller String unexpectedElement = "<unexpected-element>\n<a /></unexpected-element>\n"; 1973cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder finder = validate("<timezones>\n" 1983cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " " + unexpectedElement 1993cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 2003cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n" 2013cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 2023cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 2033cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 2043cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 2053cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb")); 2063cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 2073cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller finder = validate("<timezones>\n" 2083cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 2093cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " " + unexpectedElement 2103cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n" 2113cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 2123cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 2133cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 2143cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 2153cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb")); 2163cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 2173cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller finder = validate("<timezones>\n" 2183cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 2193cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n" 2203cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " " + unexpectedElement 2213cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 2223cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 2233cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 2243cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 2253cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb")); 2263cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 2273cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller finder = validate("<timezones>\n" 2283cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 2293cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n" 2303cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 2313cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " " + unexpectedElement 2323cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/Paris</id>\n" 2333cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 2343cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 2353cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 2363cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZonesEqual(zones("Europe/London", "Europe/Paris"), 2373cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller finder.lookupTimeZonesByCountry("gb")); 2383cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 2393cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller finder = validate("<timezones>\n" 2403cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 2413cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n" 2423cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 2433cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 2443cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " " + unexpectedElement 2453cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 2463cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 2473cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb")); 2483cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 2493cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // This test is important because it ensures we can extend the format in future with 2503cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // more information. 2513cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller finder = validate("<timezones>\n" 2523cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 2533cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n" 2543cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 2553cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 2563cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 2573cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " " + unexpectedElement 2583cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 2593cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb")); 2603cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 2613cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 2623cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Test 2633cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void xmlParsing_unexpectedTextIgnored() throws Exception { 2643cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller String unexpectedText = "unexpected-text"; 2653cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder finder = validate("<timezones>\n" 2663cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " " + unexpectedText 2673cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 2683cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n" 2693cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 2703cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 2713cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 2723cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 2733cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb")); 2743cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 2753cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller finder = validate("<timezones>\n" 2763cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 2773cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " " + unexpectedText 2783cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n" 2793cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 2803cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 2813cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 2823cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 2833cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb")); 2843cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 2853cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller finder = validate("<timezones>\n" 2863cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 2873cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n" 2883cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " " + unexpectedText 2893cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 2903cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 2913cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 2923cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 2933cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb")); 2943cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 2953cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller finder = validate("<timezones>\n" 2963cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 2973cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n" 2983cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 2993cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " " + unexpectedText 3003cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/Paris</id>\n" 3013cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 3023cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 3033cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 3043cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZonesEqual(zones("Europe/London", "Europe/Paris"), 3053cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller finder.lookupTimeZonesByCountry("gb")); 3063cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 3073cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 3083cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Test 3093cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void xmlParsing_truncatedInput() throws Exception { 3103cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller checkValidateThrowsParserException("<timezones>\n"); 3113cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 3123cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller checkValidateThrowsParserException("<timezones>\n" 3133cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n"); 3143cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 3153cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller checkValidateThrowsParserException("<timezones>\n" 3163cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 3173cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n"); 3183cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 3193cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller checkValidateThrowsParserException("<timezones>\n" 3203cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 3213cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n" 3223cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n"); 3233cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 3243cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller checkValidateThrowsParserException("<timezones>\n" 3253cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 3263cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n" 3273cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 3283cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n"); 3293cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 3303cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller checkValidateThrowsParserException("<timezones>\n" 3313cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 3323cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n" 3333cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 3343cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 3353cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n"); 3363cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 3373cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 3383cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Test 3393cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void xmlParsing_unexpectedChildInTimeZoneIdThrows() throws Exception { 3403cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller checkValidateThrowsParserException("<timezones>\n" 3413cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 3423cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n" 3433cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id><unexpected-element /></id>\n" 3443cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 3453cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 3463cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 3473cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 3483cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 3493cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Test 3503cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void xmlParsing_unknownTimeZoneIdIgnored() throws Exception { 3513cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder finder = validate("<timezones>\n" 3523cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 3533cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n" 3543cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Unknown_Id</id>\n" 3553cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 3563cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 3573cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 3583cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 3593cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZonesEqual(zones("Europe/London"), finder.lookupTimeZonesByCountry("gb")); 3603cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 3613cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 3623cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Test 3633cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void xmlParsing_missingCountryCode() throws Exception { 3643cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller checkValidateThrowsParserException("<timezones>\n" 3653cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 3663cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country>\n" 3673cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 3683cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 3693cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 3703cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 3713cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 3723cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 3733cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Test 3743cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void xmlParsing_unknownCountryReturnsNull() throws Exception { 3753cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder finder = validate("<timezones>\n" 3763cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 3773cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 3783cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 3793cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(finder.lookupTimeZonesByCountry("gb")); 3803cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 3813cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 3823cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Test 3833cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void lookupTimeZonesByCountry_structuresAreImmutable() throws Exception { 3843cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder finder = validate("<timezones>\n" 3853cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 3863cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"gb\">\n" 3873cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 3883cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 3893cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 3903cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 3913cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 3923cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller List<TimeZone> gbList = finder.lookupTimeZonesByCountry("gb"); 3933cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertEquals(1, gbList.size()); 3943cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertImmutableList(gbList); 3953cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertImmutableTimeZone(gbList.get(0)); 3963cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 3973cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(finder.lookupTimeZonesByCountry("unknown")); 3983cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 3993cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 4003cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Test 4013cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void lookupTimeZoneByCountryAndOffset_unknownCountry() throws Exception { 4023cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder finder = validate("<timezones>\n" 4033cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 4043cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"xx\">\n" 4053cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 4063cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 4073cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 4083cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 4093cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 4103cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // Demonstrate the arguments work for a known country. 4113cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(LONDON_TZ, 4123cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_DST_OFFSET_MILLIS, 4133cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller true /* isDst */, WHEN_DST, null /* bias */)); 4143cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 4153cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // Test with an unknown country. 4163cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller String unknownCountryCode = "yy"; 4173cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(finder.lookupTimeZoneByCountryAndOffset(unknownCountryCode, 4183cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller LONDON_DST_OFFSET_MILLIS, true /* isDst */, WHEN_DST, null /* bias */)); 4193cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 4203cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(finder.lookupTimeZoneByCountryAndOffset(unknownCountryCode, 4213cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller LONDON_DST_OFFSET_MILLIS, true /* isDst */, WHEN_DST, LONDON_TZ /* bias */)); 4223cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 4233cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 4243cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Test 4253cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void lookupTimeZoneByCountryAndOffset_oneCandidate() throws Exception { 4263cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder finder = validate("<timezones>\n" 4273cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 4283cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"xx\">\n" 4293cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 4303cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 4313cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 4323cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 4333cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 4343cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // The three parameters match the configured zone: offset, isDst and when. 4353cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(LONDON_TZ, 4363cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_DST_OFFSET_MILLIS, 4373cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller true /* isDst */, WHEN_DST, null /* bias */)); 4383cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(LONDON_TZ, 4393cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_NO_DST_OFFSET_MILLIS, 4403cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller false /* isDst */, WHEN_NO_DST, null /* bias */)); 4413cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 4423cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // Some lookup failure cases where the offset, isDst and when do not match the configured 4433cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // zone. 4443cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZone noDstMatch1 = finder.lookupTimeZoneByCountryAndOffset("xx", 4453cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller LONDON_DST_OFFSET_MILLIS, true /* isDst */, WHEN_NO_DST, null /* bias */); 4463cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(noDstMatch1); 4473cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 4483cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZone noDstMatch2 = finder.lookupTimeZoneByCountryAndOffset("xx", 4493cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller LONDON_DST_OFFSET_MILLIS, false /* isDst */, WHEN_NO_DST, null /* bias */); 4503cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(noDstMatch2); 4513cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 4523cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZone noDstMatch3 = finder.lookupTimeZoneByCountryAndOffset("xx", 4533cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller LONDON_NO_DST_OFFSET_MILLIS, true /* isDst */, WHEN_DST, null /* bias */); 4543cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(noDstMatch3); 4553cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 4563cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZone noDstMatch4 = finder.lookupTimeZoneByCountryAndOffset("xx", 4573cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller LONDON_NO_DST_OFFSET_MILLIS, true /* isDst */, WHEN_NO_DST, null /* bias */); 4583cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(noDstMatch4); 4593cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 4603cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZone noDstMatch5 = finder.lookupTimeZoneByCountryAndOffset("xx", 4613cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller LONDON_DST_OFFSET_MILLIS, false /* isDst */, WHEN_DST, null /* bias */); 4623cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(noDstMatch5); 4633cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 4643cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZone noDstMatch6 = finder.lookupTimeZoneByCountryAndOffset("xx", 4653cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller LONDON_NO_DST_OFFSET_MILLIS, false /* isDst */, WHEN_DST, null /* bias */); 4663cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(noDstMatch6); 4673cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 4683cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // Some bias cases below. 4693cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 4703cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // The bias is irrelevant here: it matches what would be returned anyway. 4713cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(LONDON_TZ, 4723cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_DST_OFFSET_MILLIS, 4733cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller true /* isDst */, WHEN_DST, LONDON_TZ /* bias */)); 4743cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(LONDON_TZ, 4753cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_NO_DST_OFFSET_MILLIS, 4763cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller false /* isDst */, WHEN_NO_DST, LONDON_TZ /* bias */)); 4773cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // A sample of a non-matching case with bias. 4783cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_DST_OFFSET_MILLIS, 4793cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller true /* isDst */, WHEN_NO_DST, LONDON_TZ /* bias */)); 4803cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 4813cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // The bias should be ignored: it doesn't match any of the country's zones. 4823cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(LONDON_TZ, 4833cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_DST_OFFSET_MILLIS, 4843cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller true /* isDst */, WHEN_DST, NEW_YORK_TZ /* bias */)); 4853cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 4863cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // The bias should still be ignored even though it matches the offset information given: 4873cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // it doesn't match any of the country's configured zones. 4883cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(finder.lookupTimeZoneByCountryAndOffset("xx", NEW_YORK_DST_OFFSET_MILLIS, 4893cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller true /* isDst */, WHEN_DST, NEW_YORK_TZ /* bias */)); 4903cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 4913cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 4923cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Test 4933cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void lookupTimeZoneByCountryAndOffset_multipleNonOverlappingCandidates() 4943cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller throws Exception { 4953cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder finder = validate("<timezones>\n" 4963cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 4973cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"xx\">\n" 4983cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>America/New_York</id>\n" 4993cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 5003cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 5013cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 5023cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 5033cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 5043cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // The three parameters match the configured zone: offset, isDst and when. 5053cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(LONDON_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", 5063cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller LONDON_DST_OFFSET_MILLIS, true /* isDst */, WHEN_DST, null /* bias */)); 5073cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(LONDON_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", 5083cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller LONDON_NO_DST_OFFSET_MILLIS, false /* isDst */, WHEN_NO_DST, null /* bias */)); 5093cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(NEW_YORK_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", 5103cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller NEW_YORK_DST_OFFSET_MILLIS, true /* isDst */, WHEN_DST, null /* bias */)); 5113cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(NEW_YORK_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", 5123cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller NEW_YORK_NO_DST_OFFSET_MILLIS, false /* isDst */, WHEN_NO_DST, null /* bias */)); 5133cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 5143cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // Some lookup failure cases where the offset, isDst and when do not match the configured 5153cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // zone. This is a sample, not complete. 5163cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZone noDstMatch1 = finder.lookupTimeZoneByCountryAndOffset("xx", 5173cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller LONDON_DST_OFFSET_MILLIS, true /* isDst */, WHEN_NO_DST, null /* bias */); 5183cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(noDstMatch1); 5193cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 5203cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZone noDstMatch2 = finder.lookupTimeZoneByCountryAndOffset("xx", 5213cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller LONDON_DST_OFFSET_MILLIS, false /* isDst */, WHEN_NO_DST, null /* bias */); 5223cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(noDstMatch2); 5233cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 5243cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZone noDstMatch3 = finder.lookupTimeZoneByCountryAndOffset("xx", 5253cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller NEW_YORK_NO_DST_OFFSET_MILLIS, true /* isDst */, WHEN_DST, null /* bias */); 5263cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(noDstMatch3); 5273cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 5283cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZone noDstMatch4 = finder.lookupTimeZoneByCountryAndOffset("xx", 5293cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller NEW_YORK_NO_DST_OFFSET_MILLIS, true /* isDst */, WHEN_NO_DST, null /* bias */); 5303cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(noDstMatch4); 5313cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 5323cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZone noDstMatch5 = finder.lookupTimeZoneByCountryAndOffset("xx", 5333cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller LONDON_DST_OFFSET_MILLIS, false /* isDst */, WHEN_DST, null /* bias */); 5343cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(noDstMatch5); 5353cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 5363cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZone noDstMatch6 = finder.lookupTimeZoneByCountryAndOffset("xx", 5373cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller LONDON_NO_DST_OFFSET_MILLIS, false /* isDst */, WHEN_DST, null /* bias */); 5383cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(noDstMatch6); 5393cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 5403cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // Some bias cases below. 5413cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 5423cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // The bias is irrelevant here: it matches what would be returned anyway. 5433cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(LONDON_TZ, 5443cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_DST_OFFSET_MILLIS, 5453cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller true /* isDst */, WHEN_DST, LONDON_TZ /* bias */)); 5463cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(LONDON_TZ, 5473cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_NO_DST_OFFSET_MILLIS, 5483cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller false /* isDst */, WHEN_NO_DST, LONDON_TZ /* bias */)); 5493cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // A sample of a non-matching case with bias. 5503cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_DST_OFFSET_MILLIS, 5513cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller true /* isDst */, WHEN_NO_DST, LONDON_TZ /* bias */)); 5523cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 5533cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // The bias should be ignored: it matches a configured zone, but the offset is wrong so 5543cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // should not be considered a match. 5553cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(LONDON_TZ, 5563cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller finder.lookupTimeZoneByCountryAndOffset("xx", LONDON_DST_OFFSET_MILLIS, 5573cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller true /* isDst */, WHEN_DST, NEW_YORK_TZ /* bias */)); 5583cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 5593cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 5603cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // This is an artificial case very similar to America/Denver and America/Phoenix in the US: both 5613cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // have the same offset for 6 months of the year but diverge. Australia/Lord_Howe too. 5623cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Test 5633cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void lookupTimeZoneByCountryAndOffset_multipleOverlappingCandidates() throws Exception { 5643cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // Three zones that have the same offset for some of the year. Europe/London changes 5653cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // offset WHEN_DST, the others do not. 5663cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder finder = validate("<timezones>\n" 5673cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <countryzones>\n" 5683cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <country code=\"xx\">\n" 5693cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Atlantic/Reykjavik</id>\n" 5703cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Europe/London</id>\n" 5713cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " <id>Etc/UTC</id>\n" 5723cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </country>\n" 5733cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + " </countryzones>\n" 5743cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller + "</timezones>\n"); 5753cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 5763cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // This is the no-DST offset for LONDON_TZ, REYKJAVIK_TZ. UTC_TZ. 5773cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller final int noDstOffset = LONDON_NO_DST_OFFSET_MILLIS; 5783cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // This is the DST offset for LONDON_TZ. 5793cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller final int dstOffset = LONDON_DST_OFFSET_MILLIS; 5803cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 5813cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // The three parameters match the configured zone: offset, isDst and when. 5823cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(LONDON_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", dstOffset, 5833cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller true /* isDst */, WHEN_DST, null /* bias */)); 5843cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(REYKJAVIK_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", noDstOffset, 5853cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller false /* isDst */, WHEN_NO_DST, null /* bias */)); 5863cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(LONDON_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", dstOffset, 5873cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller true /* isDst */, WHEN_DST, null /* bias */)); 5883cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(REYKJAVIK_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", noDstOffset, 5893cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller false /* isDst */, WHEN_NO_DST, null /* bias */)); 5903cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(REYKJAVIK_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", noDstOffset, 5913cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller false /* isDst */, WHEN_DST, null /* bias */)); 5923cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 5933cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // Some lookup failure cases where the offset, isDst and when do not match the configured 5943cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // zones. 5953cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZone noDstMatch1 = finder.lookupTimeZoneByCountryAndOffset("xx", dstOffset, 5963cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller true /* isDst */, WHEN_NO_DST, null /* bias */); 5973cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(noDstMatch1); 5983cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 5993cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZone noDstMatch2 = finder.lookupTimeZoneByCountryAndOffset("xx", noDstOffset, 6003cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller true /* isDst */, WHEN_DST, null /* bias */); 6013cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(noDstMatch2); 6023cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 6033cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZone noDstMatch3 = finder.lookupTimeZoneByCountryAndOffset("xx", noDstOffset, 6043cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller true /* isDst */, WHEN_NO_DST, null /* bias */); 6053cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(noDstMatch3); 6063cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 6073cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZone noDstMatch4 = finder.lookupTimeZoneByCountryAndOffset("xx", dstOffset, 6083cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller false /* isDst */, WHEN_DST, null /* bias */); 6093cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertNull(noDstMatch4); 6103cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 6113cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 6123cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // Some bias cases below. 6133cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 6143cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // The bias is relevant here: it overrides what would be returned naturally. 6153cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(REYKJAVIK_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", noDstOffset, 6163cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller false /* isDst */, WHEN_NO_DST, null /* bias */)); 6173cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(LONDON_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", noDstOffset, 6183cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller false /* isDst */, WHEN_NO_DST, LONDON_TZ /* bias */)); 6193cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(UTC_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", noDstOffset, 6203cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller false /* isDst */, WHEN_NO_DST, UTC_TZ /* bias */)); 6213cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 6223cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // The bias should be ignored: it matches a configured zone, but the offset is wrong so 6233cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // should not be considered a match. 6243cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertZoneEquals(LONDON_TZ, finder.lookupTimeZoneByCountryAndOffset("xx", 6253cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller LONDON_DST_OFFSET_MILLIS, true /* isDst */, WHEN_DST, REYKJAVIK_TZ /* bias */)); 6263cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 6273cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 6283cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller @Test 6293cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller public void consistencyTest() throws Exception { 6303cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // Confirm that no new zones have been added to zones.tab without also adding them to the 6313cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // configuration used to drive TimeZoneFinder. 6323cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 6333cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // zone.tab is a tab separated ASCII file provided by IANA and included in Android's tzdata 6343cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // file. Each line contains a mapping from country code -> zone ID. The ordering used by 6353cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // TimeZoneFinder is Android-specific, but we can use zone.tab to make sure we know about 6363cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // all country zones. Any update to tzdata that adds, renames, or removes zones should be 6373cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // reflected in the file used by TimeZoneFinder. 6383cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller Map<String, Set<String>> zoneTabMappings = new HashMap<>(); 6393cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller for (String line : ZoneInfoDB.getInstance().getZoneTab().split("\n")) { 6403cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller int countryCodeEnd = line.indexOf('\t', 1); 6413cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller int olsonIdStart = line.indexOf('\t', 4) + 1; 6423cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller int olsonIdEnd = line.indexOf('\t', olsonIdStart); 6433cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller if (olsonIdEnd == -1) { 6443cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller olsonIdEnd = line.length(); // Not all zone.tab lines have a comment. 6453cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 6463cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller String countryCode = line.substring(0, countryCodeEnd); 6473cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller String olsonId = line.substring(olsonIdStart, olsonIdEnd); 6483cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller Set<String> zoneIds = zoneTabMappings.get(countryCode); 6493cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller if (zoneIds == null) { 6503cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller zoneIds = new HashSet<>(); 6513cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller zoneTabMappings.put(countryCode, zoneIds); 6523cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 6533cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller zoneIds.add(olsonId); 6543cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 6553cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 6563cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder timeZoneFinder = TimeZoneFinder.getInstance(); 6573cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller for (Map.Entry<String, Set<String>> countryEntry : zoneTabMappings.entrySet()) { 6583cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller String countryCode = countryEntry.getKey(); 6593cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // Android uses lower case, IANA uses upper. 6603cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller countryCode = countryCode.toLowerCase(); 6613cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 6623cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller List<String> ianaZoneIds = countryEntry.getValue().stream().sorted() 6633cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller .collect(Collectors.toList()); 6643cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller List<TimeZone> androidZones = timeZoneFinder.lookupTimeZonesByCountry(countryCode); 6653cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller List<String> androidZoneIds = 6663cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller androidZones.stream().map(TimeZone::getID).sorted() 6673cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller .collect(Collectors.toList()); 6683cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 6693cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertEquals("Android zones for " + countryCode + " do not match IANA data", 6703cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller ianaZoneIds, androidZoneIds); 6713cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 6723cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 6733cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 6743cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private void assertImmutableTimeZone(TimeZone timeZone) { 6753cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller try { 6763cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller timeZone.setRawOffset(1000); 6773cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller fail(); 6783cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } catch (UnsupportedOperationException expected) { 6793cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 6803cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 6813cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 6823cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private static void assertImmutableList(List<TimeZone> timeZones) { 6833cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller try { 6843cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller timeZones.add(null); 6853cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller fail(); 6863cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } catch (UnsupportedOperationException expected) { 6873cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 6883cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 6893cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 6903cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private static void assertZoneEquals(TimeZone expected, TimeZone actual) { 6913cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // TimeZone.equals() only checks the ID, but that's ok for these tests. 6923cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertEquals(expected, actual); 6933cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 6943cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 6953cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private static void assertZonesEqual(List<TimeZone> expected, List<TimeZone> actual) { 6963cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller // TimeZone.equals() only checks the ID, but that's ok for these tests. 6973cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller assertEquals(expected, actual); 6983cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 6993cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 7003cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private static void checkValidateThrowsParserException(String xml) throws Exception { 7013cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller try { 7023cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller validate(xml); 7033cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller fail(); 7043cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } catch (IOException expected) { 7053cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 7063cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 7073cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 7083cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private static TimeZoneFinder validate(String xml) throws IOException { 7093cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller TimeZoneFinder timeZoneFinder = TimeZoneFinder.createInstanceForTests(xml); 7103cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller timeZoneFinder.validate(); 7113cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller return timeZoneFinder; 7123cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 7133cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 7143cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private static List<TimeZone> zones(String... ids) { 7153cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller return Arrays.stream(ids).map(TimeZone::getTimeZone).collect(Collectors.toList()); 7163cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 7173cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 7183cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private String createFile(String fileContent) throws IOException { 7193cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller Path filePath = Files.createTempFile(testDir, null, null); 7203cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller Files.write(filePath, fileContent.getBytes(StandardCharsets.UTF_8)); 7213cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller return filePath.toString(); 7223cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 7233cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller 7243cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller private String createMissingFile() throws IOException { 7253cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller Path filePath = Files.createTempFile(testDir, null, null); 7263cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller Files.delete(filePath); 7273cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller return filePath.toString(); 7283cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller } 7293cbfb6e3a3d8c50b6512df250b24dbfffbf6d3a7Neil Fuller} 730