blob: f84ae62fe2d270a05278622b5a121a1c855c8c76 [file] [log] [blame]
/*
* 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 android.tzdata.mts;
import static android.tzdata.mts.MtsTestSupport.assumeAtLeastR;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.icu.util.TimeZone;
import android.timezone.CountryTimeZones;
import android.timezone.TimeZoneFinder;
import org.junit.Test;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Functional tests for module APIs accessed via {@link TimeZoneFinder} that could be affected by
* the time zone data module.
*
* <p>These tests are located with the tzdata mainline module because they help to validate
* time zone data shipped via the tzdata module. At some point in the future, the class under test
* will be implemented by a mainline module and the test can move there.
*/
public class TimeZoneFinderTest {
/*
* TODO: Replace "assumeAtLeastR()" calls below with a standard alternative.
*/
// Only intended for R+. This tests APIs exposed in R.
@Test
public void getIanaVersion() {
assumeAtLeastR();
TimeZoneFinder timeZoneFinder = TimeZoneFinder.getInstance();
assertNotNull(timeZoneFinder.getIanaVersion());
}
// Only intended for R+. This tests APIs exposed in R.
@Test
public void lookupCountryTimeZones_caseInsensitive() {
assumeAtLeastR();
TimeZoneFinder timeZoneFinder = TimeZoneFinder.getInstance();
CountryTimeZones lowerCaseResult = timeZoneFinder.lookupCountryTimeZones("gb");
assertEquals(lowerCaseResult, timeZoneFinder.lookupCountryTimeZones("GB"));
}
// Only intended for R+. This tests APIs exposed in R.
@Test
public void lookupCountryTimeZones_unknown() {
assumeAtLeastR();
TimeZoneFinder timeZoneFinder = TimeZoneFinder.getInstance();
assertNull(timeZoneFinder.lookupCountryTimeZones("zz"));
}
// Only intended for R+. This tests APIs exposed in R.
@Test
public void countryTimeZones_gb() {
assumeAtLeastR();
TimeZoneFinder timeZoneFinder = TimeZoneFinder.getInstance();
// Assert results for a single-zone country that doesn't change very often.
CountryTimeZones countryTimeZones = timeZoneFinder.lookupCountryTimeZones("gb");
assertNotNull(countryTimeZones);
assertTrue(countryTimeZones.hasUtcZone(midnightUtcMillis(2020, 1, 1)));
assertFalse(countryTimeZones.isDefaultTimeZoneBoosted());
assertEquals("Europe/London", countryTimeZones.getDefaultTimeZoneId());
assertEquals(timeZone("Europe/London"), countryTimeZones.getDefaultTimeZone());
assertTrue(countryTimeZones.matchesCountryCode("gb"));
assertTrue(countryTimeZones.matchesCountryCode("GB"));
assertTrue(countryTimeZones.matchesCountryCode("gB"));
assertTrue(countryTimeZones.matchesCountryCode("Gb"));
}
// Only intended for R+. This tests APIs exposed in R.
@Test
public void countryTimeZones_getEffectiveMappingsAt_gb() {
assumeAtLeastR();
TimeZoneFinder timeZoneFinder = TimeZoneFinder.getInstance();
// Assert results for a single-zone country that doesn't change very often.
CountryTimeZones countryTimeZones = timeZoneFinder.lookupCountryTimeZones("gb");
assertNotNull(countryTimeZones);
List<CountryTimeZones.TimeZoneMapping> timeZoneMappings =
countryTimeZones.getEffectiveTimeZoneMappingsAt(midnightUtcMillis(2020, 1, 1));
assertEquals(1, timeZoneMappings.size());
CountryTimeZones.TimeZoneMapping timeZoneMapping = timeZoneMappings.get(0);
assertEquals("Europe/London", timeZoneMapping.getTimeZoneId());
assertEquals(timeZone("Europe/London"), timeZoneMapping.getTimeZone());
}
// Only intended for R+. This tests APIs exposed in R.
@Test
public void countryTimeZones_lookupByOffsetWithBias_gb() {
assumeAtLeastR();
TimeZoneFinder timeZoneFinder = TimeZoneFinder.getInstance();
// Assert results for a single-zone country that doesn't change very often.
CountryTimeZones countryTimeZones = timeZoneFinder.lookupCountryTimeZones("gb");
assertNotNull(countryTimeZones);
// No match.
{
int utcOffsetHours = 5;
CountryTimeZones.OffsetResult offsetResult = lookup1Jan2020Offset(countryTimeZones,
utcOffsetHours, null);
assertNull(offsetResult);
}
// Single match, no bias
{
CountryTimeZones.OffsetResult offsetResult = lookup1Jan2020Offset(countryTimeZones, 0,
null);
assertNotNull(offsetResult);
assertEquals(timeZone("Europe/London"), offsetResult.getTimeZone());
assertTrue(offsetResult.isOnlyMatch());
}
// Single match, with non-matching bias.
{
CountryTimeZones.OffsetResult offsetResult = lookup1Jan2020Offset(countryTimeZones, 0,
timeZone("Europe/Paris"));
assertNotNull(offsetResult);
assertEquals(timeZone("Europe/London"), offsetResult.getTimeZone());
assertTrue(offsetResult.isOnlyMatch());
}
}
// Only intended for R+. This tests APIs exposed in R.
@Test
public void countryTimeZones_us() {
assumeAtLeastR();
TimeZoneFinder timeZoneFinder = TimeZoneFinder.getInstance();
// Assert results for a multi-zone country that doesn't change very often.
CountryTimeZones countryTimeZones = timeZoneFinder.lookupCountryTimeZones("us");
assertNotNull(countryTimeZones);
assertFalse(countryTimeZones.hasUtcZone(midnightUtcMillis(2020, 1, 1)));
assertFalse(countryTimeZones.isDefaultTimeZoneBoosted());
String expectedZoneId = "America/New_York";
assertEquals(expectedZoneId, countryTimeZones.getDefaultTimeZoneId());
assertEquals(timeZone(expectedZoneId), countryTimeZones.getDefaultTimeZone());
assertTrue(countryTimeZones.matchesCountryCode("us"));
assertTrue(countryTimeZones.matchesCountryCode("US"));
assertTrue(countryTimeZones.matchesCountryCode("uS"));
assertTrue(countryTimeZones.matchesCountryCode("Us"));
}
// Only intended for R+. This tests APIs exposed in R.
@Test
public void countryTimeZones_getEffectiveMappingsAt_us() {
assumeAtLeastR();
TimeZoneFinder timeZoneFinder = TimeZoneFinder.getInstance();
// Assert results for a multi-zone country that doesn't change very often.
CountryTimeZones countryTimeZones = timeZoneFinder.lookupCountryTimeZones("us");
assertNotNull(countryTimeZones);
List<CountryTimeZones.TimeZoneMapping> timeZoneMappings =
countryTimeZones.getEffectiveTimeZoneMappingsAt(midnightUtcMillis(2020, 1, 1));
Set<String> expectedZoneIds = set(
"America/New_York",
"America/Chicago",
"America/Denver",
"America/Phoenix",
"America/Los_Angeles",
"America/Anchorage",
"Pacific/Honolulu",
"America/Adak");
Set<String> actualZoneIds = new HashSet<>();
timeZoneMappings.forEach(i -> {
actualZoneIds.add(i.getTimeZoneId());
});
assertEquals(expectedZoneIds, actualZoneIds);
}
// Only intended for R+. This tests APIs exposed in R.
@Test
public void countryTimeZones_lookupByOffsetWithBias_us() {
assumeAtLeastR();
TimeZoneFinder timeZoneFinder = TimeZoneFinder.getInstance();
// Assert results for a multi-zone country that doesn't change very often.
CountryTimeZones countryTimeZones = timeZoneFinder.lookupCountryTimeZones("us");
assertNotNull(countryTimeZones);
// No match.
{
CountryTimeZones.OffsetResult offsetResult = lookup1Jan2020Offset(
countryTimeZones, 5 /* utcOffsetHours */, null /* bias */);
assertNull(offsetResult);
}
// Single match, no bias
{
CountryTimeZones.OffsetResult offsetResult = lookup1Jan2020Offset(
countryTimeZones, -8 /* utcOffsetHours */, null /* bias */);
assertNotNull(offsetResult);
assertEquals(timeZone("America/Los_Angeles"), offsetResult.getTimeZone());
assertTrue(offsetResult.isOnlyMatch());
}
// Single match, with non-matching bias.
{
CountryTimeZones.OffsetResult offsetResult = lookup1Jan2020Offset(
countryTimeZones, -8 /* utcOffsetHours */,
timeZone("Europe/London") /* bias */);
assertNotNull(offsetResult);
assertEquals(timeZone("America/Los_Angeles"), offsetResult.getTimeZone());
assertTrue(offsetResult.isOnlyMatch());
}
// Multiple match. No bias.
{
CountryTimeZones.OffsetResult offsetResult = lookup1Jan2020Offset(
countryTimeZones, -7 /* utcOffsetHours */, null /* bias */);
assertNotNull(offsetResult);
TimeZone actualTimeZone = offsetResult.getTimeZone();
assertTrue(set("America/Denver", "America/Arizona").contains(actualTimeZone.getID()));
assertFalse(offsetResult.isOnlyMatch());
}
// Multiple match. With bias.
{
CountryTimeZones.OffsetResult offsetResult =
lookup1Jan2020Offset(countryTimeZones, -7 /* utcOffsetHours */,
timeZone("America/Denver") /* bias */);
assertNotNull(offsetResult);
assertEquals("America/Denver", offsetResult.getTimeZone().getID());
assertFalse(offsetResult.isOnlyMatch());
}
{
CountryTimeZones.OffsetResult offsetResult = lookup1Jan2020Offset(
countryTimeZones, -7 /* utcOffsetHours */,
timeZone("America/Phoenix") /* bias */);
assertNotNull(offsetResult);
assertEquals("America/Phoenix", offsetResult.getTimeZone().getID());
assertFalse(offsetResult.isOnlyMatch());
}
}
private static CountryTimeZones.OffsetResult lookup1Jan2020Offset(
CountryTimeZones countryTimeZones, int utcOffsetHours, TimeZone bias) {
return countryTimeZones.lookupByOffsetWithBias(
hourOffsetMillis(utcOffsetHours) /* totalOffsetMillis */,
null /* isDst */,
null /* dstOffsetMillis */,
midnightUtcMillis(2020, 1, 1) /* whenMillis */,
bias);
}
private static int hourOffsetMillis(int hours) {
return (int) Duration.ofHours(hours).toMillis();
}
private static Set<String> set(String... strings) {
return new HashSet<>(Arrays.asList(strings));
}
private static long midnightUtcMillis(int year, int month, int dayOfMonth) {
LocalDateTime localDateTime = LocalDateTime.of(year, month, dayOfMonth, 0, 0);
return localDateTime.toInstant(ZoneOffset.UTC).toEpochMilli();
}
private static TimeZone timeZone(String expectedZoneId) {
return TimeZone.getTimeZone(expectedZoneId);
}
}