blob: 5131247c418dcef716a4aa2a5b87b64e2373a469 [file] [log] [blame]
/*
* Copyright (C) 2017 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 libcore.libcore.timezone;
import org.junit.Test;
import android.icu.util.TimeZone;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import libcore.timezone.CountryTimeZones;
import libcore.timezone.CountryTimeZones.OffsetResult;
import libcore.timezone.CountryTimeZones.TimeZoneMapping;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class CountryTimeZonesTest {
private static final int HOUR_MILLIS = 60 * 60 * 1000;
private static final String INVALID_TZ_ID = "Moon/Tranquility_Base";
// Zones used in the tests. NEW_YORK_TZ and LONDON_TZ chosen because they never overlap but both
// have DST.
private static final TimeZone NEW_YORK_TZ = TimeZone.getTimeZone("America/New_York");
private static final TimeZone LONDON_TZ = TimeZone.getTimeZone("Europe/London");
// A zone that matches LONDON_TZ for WHEN_NO_DST. It does not have DST so differs for WHEN_DST.
private static final TimeZone REYKJAVIK_TZ = TimeZone.getTimeZone("Atlantic/Reykjavik");
// Another zone that matches LONDON_TZ for WHEN_NO_DST. It does not have DST so differs for
// WHEN_DST.
private static final TimeZone UTC_TZ = TimeZone.getTimeZone("Etc/UTC");
// 22nd July 2017, 13:14:15 UTC (DST time in all the timezones used in these tests that observe
// DST).
private static final long WHEN_DST = 1500729255000L;
// 22nd January 2018, 13:14:15 UTC (non-DST time in all timezones used in these tests).
private static final long WHEN_NO_DST = 1516626855000L;
// The offset applied to most zones during DST.
private static final int NORMAL_DST_ADJUSTMENT = HOUR_MILLIS;
private static final int LONDON_NO_DST_OFFSET_MILLIS = 0;
private static final int LONDON_DST_OFFSET_MILLIS = LONDON_NO_DST_OFFSET_MILLIS
+ NORMAL_DST_ADJUSTMENT;
private static final int NEW_YORK_NO_DST_OFFSET_MILLIS = -5 * HOUR_MILLIS;
private static final int NEW_YORK_DST_OFFSET_MILLIS = NEW_YORK_NO_DST_OFFSET_MILLIS
+ NORMAL_DST_ADJUSTMENT;
@Test
public void createValidated() throws Exception {
CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
"gb", "Europe/London", true /* everUsesUtc */, timeZoneMappings("Europe/London"),
"test");
assertTrue(countryTimeZones.isForCountryCode("gb"));
assertEquals("Europe/London", countryTimeZones.getDefaultTimeZoneId());
assertZoneEquals(zone("Europe/London"), countryTimeZones.getDefaultTimeZone());
assertEquals(timeZoneMappings("Europe/London"), countryTimeZones.getTimeZoneMappings());
assertEquals(timeZoneMappings("Europe/London"),
countryTimeZones.getEffectiveTimeZoneMappingsAt(0 /* whenMillis */));
}
@Test
public void createValidated_nullDefault() throws Exception {
CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
"gb", null, true /* everUsesUtc */, timeZoneMappings("Europe/London"), "test");
assertNull(countryTimeZones.getDefaultTimeZoneId());
assertNull(countryTimeZones.getDefaultTimeZone());
}
@Test
public void createValidated_invalidDefault() throws Exception {
CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
"gb", INVALID_TZ_ID, true /* everUsesUtc */,
timeZoneMappings("Europe/London", INVALID_TZ_ID), "test");
assertNull(countryTimeZones.getDefaultTimeZoneId());
assertNull(countryTimeZones.getDefaultTimeZone());
assertEquals(timeZoneMappings("Europe/London"), countryTimeZones.getTimeZoneMappings());
assertEquals(timeZoneMappings("Europe/London"),
countryTimeZones.getEffectiveTimeZoneMappingsAt(0 /* whenMillis */));
}
@Test
public void createValidated_unknownTimeZoneIdIgnored() throws Exception {
CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
"gb", "Europe/London", true /* everUsesUtc */,
timeZoneMappings("Unknown_Id", "Europe/London"), "test");
assertEquals(timeZoneMappings("Europe/London"), countryTimeZones.getTimeZoneMappings());
assertEquals(timeZoneMappings("Europe/London"),
countryTimeZones.getEffectiveTimeZoneMappingsAt(0 /* whenMillis */));
}
@Test
public void isForCountryCode() throws Exception {
CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
"gb", "Europe/London", true /* everUsesUtc */, timeZoneMappings("Europe/London"),
"test");
assertTrue(countryTimeZones.isForCountryCode("GB"));
assertTrue(countryTimeZones.isForCountryCode("Gb"));
assertTrue(countryTimeZones.isForCountryCode("gB"));
}
@Test
public void structuresAreImmutable() throws Exception {
CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
"gb", "Europe/London", true /* everUsesUtc */, timeZoneMappings("Europe/London"),
"test");
assertImmutableTimeZone(countryTimeZones.getDefaultTimeZone());
List<TimeZoneMapping> timeZoneMappings = countryTimeZones.getTimeZoneMappings();
assertEquals(1, timeZoneMappings.size());
assertImmutableList(timeZoneMappings);
List<TimeZoneMapping> effectiveTimeZoneMappings =
countryTimeZones.getEffectiveTimeZoneMappingsAt(0 /* whenMillis */);
assertEquals(1, effectiveTimeZoneMappings.size());
assertImmutableList(effectiveTimeZoneMappings);
}
@Test
public void lookupByOffsetWithBiasDeprecated_oneCandidate() throws Exception {
CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
"gb", "Europe/London", true /* everUsesUtc */,
timeZoneMappings("Europe/London"), "test");
OffsetResult expectedResult = new OffsetResult(LONDON_TZ, true /* oneMatch */);
// The three parameters match the configured zone: offset, isDst and time.
assertOffsetResultEquals(expectedResult,
countryTimeZones.lookupByOffsetWithBias(LONDON_DST_OFFSET_MILLIS,
true /* isDst */, WHEN_DST, null /* bias */));
assertOffsetResultEquals(expectedResult,
countryTimeZones.lookupByOffsetWithBias(LONDON_NO_DST_OFFSET_MILLIS,
false /* isDst */, WHEN_NO_DST, null /* bias */));
// Some lookup failure cases where the offset, isDst and time do not match the configured
// zone.
OffsetResult noDstMatch1 = countryTimeZones.lookupByOffsetWithBias(
LONDON_DST_OFFSET_MILLIS, true /* isDst */, WHEN_NO_DST, null /* bias */);
assertNull(noDstMatch1);
OffsetResult noDstMatch2 = countryTimeZones.lookupByOffsetWithBias(
LONDON_DST_OFFSET_MILLIS, false /* isDst */, WHEN_NO_DST, null /* bias */);
assertNull(noDstMatch2);
OffsetResult noDstMatch3 = countryTimeZones.lookupByOffsetWithBias(
LONDON_NO_DST_OFFSET_MILLIS, true /* isDst */, WHEN_DST, null /* bias */);
assertNull(noDstMatch3);
OffsetResult noDstMatch4 = countryTimeZones.lookupByOffsetWithBias(
LONDON_NO_DST_OFFSET_MILLIS, true /* isDst */, WHEN_NO_DST, null /* bias */);
assertNull(noDstMatch4);
OffsetResult noDstMatch5 = countryTimeZones.lookupByOffsetWithBias(
LONDON_DST_OFFSET_MILLIS, false /* isDst */, WHEN_DST, null /* bias */);
assertNull(noDstMatch5);
OffsetResult noDstMatch6 = countryTimeZones.lookupByOffsetWithBias(
LONDON_NO_DST_OFFSET_MILLIS, false /* isDst */, WHEN_DST, null /* bias */);
assertNull(noDstMatch6);
// Some bias cases below.
// The bias is irrelevant here: it matches what would be returned anyway.
assertOffsetResultEquals(expectedResult,
countryTimeZones.lookupByOffsetWithBias(LONDON_DST_OFFSET_MILLIS,
true /* isDst */, WHEN_DST, LONDON_TZ /* bias */));
assertOffsetResultEquals(expectedResult,
countryTimeZones.lookupByOffsetWithBias(LONDON_NO_DST_OFFSET_MILLIS,
false /* isDst */, WHEN_NO_DST, LONDON_TZ /* bias */));
// A sample of a non-matching case with bias.
assertNull(countryTimeZones.lookupByOffsetWithBias(LONDON_DST_OFFSET_MILLIS,
true /* isDst */, WHEN_NO_DST, LONDON_TZ /* bias */));
// The bias should be ignored: it doesn't match any of the country's zones.
assertOffsetResultEquals(expectedResult,
countryTimeZones.lookupByOffsetWithBias(LONDON_DST_OFFSET_MILLIS,
true /* isDst */, WHEN_DST, NEW_YORK_TZ /* bias */));
// The bias should still be ignored even though it matches the offset information given:
// it doesn't match any of the country's configured zones.
assertNull(countryTimeZones.lookupByOffsetWithBias(NEW_YORK_DST_OFFSET_MILLIS,
true /* isDst */, WHEN_DST, NEW_YORK_TZ /* bias */));
}
@Test
public void lookupByOffsetWithBiasDeprecated_multipleNonOverlappingCandidates()
throws Exception {
CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
"xx", "Europe/London", true /* everUsesUtc */,
timeZoneMappings("America/New_York", "Europe/London"), "test");
OffsetResult expectedLondonResult = new OffsetResult(LONDON_TZ, true /* oneMatch */);
OffsetResult expectedNewYorkResult = new OffsetResult(NEW_YORK_TZ, true /* oneMatch */);
// The three parameters match the configured zone: offset, isDst and time.
assertOffsetResultEquals(expectedLondonResult, countryTimeZones.lookupByOffsetWithBias(
LONDON_DST_OFFSET_MILLIS, true /* isDst */, WHEN_DST, null /* bias */));
assertOffsetResultEquals(expectedLondonResult, countryTimeZones.lookupByOffsetWithBias(
LONDON_NO_DST_OFFSET_MILLIS, false /* isDst */, WHEN_NO_DST, null /* bias */));
assertOffsetResultEquals(expectedNewYorkResult, countryTimeZones.lookupByOffsetWithBias(
NEW_YORK_DST_OFFSET_MILLIS, true /* isDst */, WHEN_DST, null /* bias */));
assertOffsetResultEquals(expectedNewYorkResult, countryTimeZones.lookupByOffsetWithBias(
NEW_YORK_NO_DST_OFFSET_MILLIS, false /* isDst */, WHEN_NO_DST, null /* bias */));
// Some lookup failure cases where the offset, isDst and time do not match the configured
// zone. This is a sample, not complete.
OffsetResult noDstMatch1 = countryTimeZones.lookupByOffsetWithBias(
LONDON_DST_OFFSET_MILLIS, true /* isDst */, WHEN_NO_DST, null /* bias */);
assertNull(noDstMatch1);
OffsetResult noDstMatch2 = countryTimeZones.lookupByOffsetWithBias(
LONDON_DST_OFFSET_MILLIS, false /* isDst */, WHEN_NO_DST, null /* bias */);
assertNull(noDstMatch2);
OffsetResult noDstMatch3 = countryTimeZones.lookupByOffsetWithBias(
NEW_YORK_NO_DST_OFFSET_MILLIS, true /* isDst */, WHEN_DST, null /* bias */);
assertNull(noDstMatch3);
OffsetResult noDstMatch4 = countryTimeZones.lookupByOffsetWithBias(
NEW_YORK_NO_DST_OFFSET_MILLIS, true /* isDst */, WHEN_NO_DST, null /* bias */);
assertNull(noDstMatch4);
OffsetResult noDstMatch5 = countryTimeZones.lookupByOffsetWithBias(
LONDON_DST_OFFSET_MILLIS, false /* isDst */, WHEN_DST, null /* bias */);
assertNull(noDstMatch5);
OffsetResult noDstMatch6 = countryTimeZones.lookupByOffsetWithBias(
LONDON_NO_DST_OFFSET_MILLIS, false /* isDst */, WHEN_DST, null /* bias */);
assertNull(noDstMatch6);
// Some bias cases below.
// The bias is irrelevant here: it matches what would be returned anyway.
assertOffsetResultEquals(expectedLondonResult, countryTimeZones.lookupByOffsetWithBias(
LONDON_DST_OFFSET_MILLIS, true /* isDst */, WHEN_DST, LONDON_TZ /* bias */));
assertOffsetResultEquals(expectedLondonResult, countryTimeZones.lookupByOffsetWithBias(
LONDON_NO_DST_OFFSET_MILLIS, false /* isDst */, WHEN_NO_DST, LONDON_TZ /* bias */));
// A sample of a non-matching case with bias.
assertNull(countryTimeZones.lookupByOffsetWithBias(
LONDON_DST_OFFSET_MILLIS, true /* isDst */, WHEN_NO_DST, LONDON_TZ /* bias */));
// The bias should be ignored: it matches a configured zone, but the offset is wrong so
// should not be considered a match.
assertOffsetResultEquals(expectedLondonResult, countryTimeZones.lookupByOffsetWithBias(
LONDON_DST_OFFSET_MILLIS, true /* isDst */, WHEN_DST, NEW_YORK_TZ /* bias */));
}
// This is an artificial case very similar to America/Denver and America/Phoenix in the US: both
// have the same offset for 6 months of the year but diverge. Australia/Lord_Howe too.
@Test
public void lookupByOffsetWithBiasDeprecated_multipleOverlappingCandidates() throws Exception {
// Three zones that have the same offset for some of the year. Europe/London changes
// offset WHEN_DST, the others do not.
CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
"xx", "Europe/London", true /* everUsesUtc */,
timeZoneMappings("Atlantic/Reykjavik", "Europe/London", "Etc/UTC"), "test");
// This is the no-DST offset for LONDON_TZ, REYKJAVIK_TZ. UTC_TZ.
final int noDstOffset = LONDON_NO_DST_OFFSET_MILLIS;
// This is the DST offset for LONDON_TZ.
final int dstOffset = LONDON_DST_OFFSET_MILLIS;
OffsetResult expectedLondonOnlyMatch = new OffsetResult(LONDON_TZ, true /* oneMatch */);
OffsetResult expectedReykjavikBestMatch =
new OffsetResult(REYKJAVIK_TZ, false /* oneMatch */);
// The three parameters match the configured zone: offset, isDst and when.
assertOffsetResultEquals(expectedLondonOnlyMatch,
countryTimeZones.lookupByOffsetWithBias(dstOffset, true /* isDst */, WHEN_DST,
null /* bias */));
assertOffsetResultEquals(expectedReykjavikBestMatch,
countryTimeZones.lookupByOffsetWithBias(noDstOffset, false /* isDst */, WHEN_NO_DST,
null /* bias */));
assertOffsetResultEquals(expectedLondonOnlyMatch,
countryTimeZones.lookupByOffsetWithBias(dstOffset, true /* isDst */, WHEN_DST,
null /* bias */));
assertOffsetResultEquals(expectedReykjavikBestMatch,
countryTimeZones.lookupByOffsetWithBias(noDstOffset, false /* isDst */, WHEN_NO_DST,
null /* bias */));
assertOffsetResultEquals(expectedReykjavikBestMatch,
countryTimeZones.lookupByOffsetWithBias(noDstOffset, false /* isDst */, WHEN_DST,
null /* bias */));
// Some lookup failure cases where the offset, isDst and time do not match the configured
// zones.
OffsetResult noDstMatch1 = countryTimeZones.lookupByOffsetWithBias(dstOffset,
true /* isDst */, WHEN_NO_DST, null /* bias */);
assertNull(noDstMatch1);
OffsetResult noDstMatch2 = countryTimeZones.lookupByOffsetWithBias(noDstOffset,
true /* isDst */, WHEN_DST, null /* bias */);
assertNull(noDstMatch2);
OffsetResult noDstMatch3 = countryTimeZones.lookupByOffsetWithBias(noDstOffset,
true /* isDst */, WHEN_NO_DST, null /* bias */);
assertNull(noDstMatch3);
OffsetResult noDstMatch4 = countryTimeZones.lookupByOffsetWithBias(dstOffset,
false /* isDst */, WHEN_DST, null /* bias */);
assertNull(noDstMatch4);
// Some bias cases below.
// Multiple zones match but Reykjavik is the bias.
assertOffsetResultEquals(expectedReykjavikBestMatch,
countryTimeZones.lookupByOffsetWithBias(noDstOffset, false /* isDst */, WHEN_NO_DST,
REYKJAVIK_TZ /* bias */));
// Multiple zones match but London is the bias.
OffsetResult expectedLondonBestMatch = new OffsetResult(LONDON_TZ, false /* oneMatch */);
assertOffsetResultEquals(expectedLondonBestMatch,
countryTimeZones.lookupByOffsetWithBias(noDstOffset, false /* isDst */, WHEN_NO_DST,
LONDON_TZ /* bias */));
// Multiple zones match but UTC is the bias.
OffsetResult expectedUtcResult = new OffsetResult(UTC_TZ, false /* oneMatch */);
assertOffsetResultEquals(expectedUtcResult,
countryTimeZones.lookupByOffsetWithBias(noDstOffset, false /* isDst */, WHEN_NO_DST,
UTC_TZ /* bias */));
// The bias should be ignored: it matches a configured zone, but the offset is wrong so
// should not be considered a match.
assertOffsetResultEquals(expectedLondonOnlyMatch,
countryTimeZones.lookupByOffsetWithBias(LONDON_DST_OFFSET_MILLIS, true /* isDst */,
WHEN_DST, REYKJAVIK_TZ /* bias */));
}
@Test
public void lookupByOffsetWithBias_oneCandidate() throws Exception {
CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
"gb", "Europe/London", true /* uses UTC */, timeZoneMappings("Europe/London"),
"test");
OffsetResult expectedResult = new OffsetResult(LONDON_TZ, true /* oneMatch */);
// The three parameters match the configured zone: offset, isDst and time.
assertOffsetResultEquals(expectedResult,
countryTimeZones.lookupByOffsetWithBias(LONDON_DST_OFFSET_MILLIS,
NORMAL_DST_ADJUSTMENT, WHEN_DST, null /* bias */));
assertOffsetResultEquals(expectedResult,
countryTimeZones.lookupByOffsetWithBias(LONDON_NO_DST_OFFSET_MILLIS,
0 /* no DST */, WHEN_NO_DST, null /* bias */));
assertOffsetResultEquals(expectedResult,
countryTimeZones.lookupByOffsetWithBias(LONDON_DST_OFFSET_MILLIS,
null /* unknown DST */, WHEN_DST, null /* bias */));
assertOffsetResultEquals(expectedResult,
countryTimeZones.lookupByOffsetWithBias(LONDON_NO_DST_OFFSET_MILLIS,
null /* unknown DST */, WHEN_NO_DST, null /* bias */));
// Some lookup failure cases where the offset, DST offset and time do not match the
// configured zone.
OffsetResult noDstMatch1 = countryTimeZones.lookupByOffsetWithBias(
LONDON_DST_OFFSET_MILLIS, NORMAL_DST_ADJUSTMENT, WHEN_NO_DST, null /* bias */);
assertNull(noDstMatch1);
OffsetResult noDstMatch2 = countryTimeZones.lookupByOffsetWithBias(
LONDON_DST_OFFSET_MILLIS, 0 /* no DST */, WHEN_NO_DST, null /* bias */);
assertNull(noDstMatch2);
OffsetResult noDstMatch3 = countryTimeZones.lookupByOffsetWithBias(
LONDON_NO_DST_OFFSET_MILLIS, NORMAL_DST_ADJUSTMENT, WHEN_DST, null /* bias */);
assertNull(noDstMatch3);
OffsetResult noDstMatch4 = countryTimeZones.lookupByOffsetWithBias(
LONDON_NO_DST_OFFSET_MILLIS, NORMAL_DST_ADJUSTMENT, WHEN_NO_DST, null /* bias */);
assertNull(noDstMatch4);
OffsetResult noDstMatch5 = countryTimeZones.lookupByOffsetWithBias(
LONDON_DST_OFFSET_MILLIS, 0 /* no DST */, WHEN_DST, null /* bias */);
assertNull(noDstMatch5);
OffsetResult noDstMatch6 = countryTimeZones.lookupByOffsetWithBias(
LONDON_NO_DST_OFFSET_MILLIS, 0 /* no DST */, WHEN_DST, null /* bias */);
assertNull(noDstMatch6);
// Some bias cases below.
// The bias is irrelevant here: it matches what would be returned anyway.
assertOffsetResultEquals(expectedResult,
countryTimeZones.lookupByOffsetWithBias(LONDON_DST_OFFSET_MILLIS,
NORMAL_DST_ADJUSTMENT, WHEN_DST, LONDON_TZ /* bias */));
assertOffsetResultEquals(expectedResult,
countryTimeZones.lookupByOffsetWithBias(LONDON_NO_DST_OFFSET_MILLIS,
0 /* no DST */, WHEN_NO_DST, LONDON_TZ /* bias */));
assertOffsetResultEquals(expectedResult,
countryTimeZones.lookupByOffsetWithBias(LONDON_NO_DST_OFFSET_MILLIS,
null /* unknown DST */, WHEN_NO_DST, LONDON_TZ /* bias */));
// A sample of a non-matching case with bias.
assertNull(countryTimeZones.lookupByOffsetWithBias(LONDON_DST_OFFSET_MILLIS,
NORMAL_DST_ADJUSTMENT, WHEN_NO_DST, LONDON_TZ /* bias */));
// The bias should be ignored: it doesn't match any of the country's zones.
assertOffsetResultEquals(expectedResult,
countryTimeZones.lookupByOffsetWithBias(LONDON_DST_OFFSET_MILLIS,
NORMAL_DST_ADJUSTMENT, WHEN_DST, NEW_YORK_TZ /* bias */));
// The bias should still be ignored even though it matches the offset information given:
// it doesn't match any of the country's configured zones.
assertNull(countryTimeZones.lookupByOffsetWithBias(NEW_YORK_DST_OFFSET_MILLIS,
NORMAL_DST_ADJUSTMENT, WHEN_DST, NEW_YORK_TZ /* bias */));
}
@Test
public void lookupByOffsetWithBias_multipleNonOverlappingCandidates()
throws Exception {
CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
"xx", "Europe/London", true /* uses UTC */,
timeZoneMappings("America/New_York", "Europe/London"), "test");
OffsetResult expectedLondonResult = new OffsetResult(LONDON_TZ, true /* oneMatch */);
OffsetResult expectedNewYorkResult = new OffsetResult(NEW_YORK_TZ, true /* oneMatch */);
// The three parameters match the configured zone: offset, DST offset and time.
assertOffsetResultEquals(expectedLondonResult, countryTimeZones.lookupByOffsetWithBias(
LONDON_DST_OFFSET_MILLIS, NORMAL_DST_ADJUSTMENT, WHEN_DST, null /* bias */));
assertOffsetResultEquals(expectedLondonResult, countryTimeZones.lookupByOffsetWithBias(
LONDON_NO_DST_OFFSET_MILLIS, 0 /* no DST */, WHEN_NO_DST, null /* bias */));
assertOffsetResultEquals(expectedLondonResult, countryTimeZones.lookupByOffsetWithBias(
LONDON_NO_DST_OFFSET_MILLIS, null /* unknown DST */, WHEN_NO_DST, null /* bias */));
assertOffsetResultEquals(expectedNewYorkResult, countryTimeZones.lookupByOffsetWithBias(
NEW_YORK_DST_OFFSET_MILLIS, NORMAL_DST_ADJUSTMENT, WHEN_DST, null /* bias */));
assertOffsetResultEquals(expectedNewYorkResult, countryTimeZones.lookupByOffsetWithBias(
NEW_YORK_NO_DST_OFFSET_MILLIS, 0 /* no DST */, WHEN_NO_DST, null /* bias */));
assertOffsetResultEquals(expectedNewYorkResult, countryTimeZones.lookupByOffsetWithBias(
NEW_YORK_NO_DST_OFFSET_MILLIS, null /* unknown DST */, WHEN_NO_DST,
null /* bias */));
// Some lookup failure cases where the offset, DST offset and time do not match the
// configured zone. This is a sample, not complete.
OffsetResult noDstMatch1 = countryTimeZones.lookupByOffsetWithBias(
LONDON_DST_OFFSET_MILLIS, NORMAL_DST_ADJUSTMENT, WHEN_NO_DST, null /* bias */);
assertNull(noDstMatch1);
OffsetResult noDstMatch2 = countryTimeZones.lookupByOffsetWithBias(
LONDON_DST_OFFSET_MILLIS, 0 /* no DST */, WHEN_NO_DST, null /* bias */);
assertNull(noDstMatch2);
OffsetResult noDstMatch3 = countryTimeZones.lookupByOffsetWithBias(
NEW_YORK_NO_DST_OFFSET_MILLIS, NORMAL_DST_ADJUSTMENT, WHEN_DST, null /* bias */);
assertNull(noDstMatch3);
OffsetResult noDstMatch4 = countryTimeZones.lookupByOffsetWithBias(
NEW_YORK_NO_DST_OFFSET_MILLIS, NORMAL_DST_ADJUSTMENT, WHEN_NO_DST, null /* bias */);
assertNull(noDstMatch4);
OffsetResult noDstMatch5 = countryTimeZones.lookupByOffsetWithBias(
LONDON_DST_OFFSET_MILLIS, 0 /* no DST */, WHEN_DST, null /* bias */);
assertNull(noDstMatch5);
OffsetResult noDstMatch6 = countryTimeZones.lookupByOffsetWithBias(
LONDON_NO_DST_OFFSET_MILLIS, 0 /* no DST */, WHEN_DST, null /* bias */);
assertNull(noDstMatch6);
// Some bias cases below.
// The bias is irrelevant here: it matches what would be returned anyway.
assertOffsetResultEquals(expectedLondonResult, countryTimeZones.lookupByOffsetWithBias(
LONDON_DST_OFFSET_MILLIS, NORMAL_DST_ADJUSTMENT, WHEN_DST, LONDON_TZ /* bias */));
assertOffsetResultEquals(expectedLondonResult, countryTimeZones.lookupByOffsetWithBias(
LONDON_NO_DST_OFFSET_MILLIS, 0 /* no DST */, WHEN_NO_DST, LONDON_TZ /* bias */));
assertOffsetResultEquals(expectedLondonResult, countryTimeZones.lookupByOffsetWithBias(
LONDON_NO_DST_OFFSET_MILLIS, null /* unknown DST */, WHEN_NO_DST,
LONDON_TZ /* bias */));
// A sample of a non-matching case with bias.
assertNull(countryTimeZones.lookupByOffsetWithBias(
LONDON_DST_OFFSET_MILLIS, NORMAL_DST_ADJUSTMENT, WHEN_NO_DST, LONDON_TZ /* bias */));
// The bias should be ignored: it matches a configured zone, but the offset is wrong so
// should not be considered a match.
assertOffsetResultEquals(expectedLondonResult, countryTimeZones.lookupByOffsetWithBias(
LONDON_DST_OFFSET_MILLIS, NORMAL_DST_ADJUSTMENT, WHEN_DST, NEW_YORK_TZ /* bias */));
}
// This is an artificial case very similar to America/Denver and America/Phoenix in the US: both
// have the same offset for 6 months of the year but diverge. Australia/Lord_Howe too.
@Test
public void lookupByOffsetWithBias_multipleOverlappingCandidates() throws Exception {
// Three zones that have the same offset for some of the year. Europe/London changes
// offset WHEN_DST, the others do not.
CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
"xx", "Europe/London", true /* uses UTC */,
timeZoneMappings("Atlantic/Reykjavik", "Europe/London", "Etc/UTC"), "test");
// This is the no-DST offset for LONDON_TZ, REYKJAVIK_TZ. UTC_TZ.
final int noDstOffset = LONDON_NO_DST_OFFSET_MILLIS;
// This is the DST offset for LONDON_TZ.
final int dstOffset = LONDON_DST_OFFSET_MILLIS;
OffsetResult expectedLondonOnlyMatch = new OffsetResult(LONDON_TZ, true /* oneMatch */);
OffsetResult expectedReykjavikBestMatch =
new OffsetResult(REYKJAVIK_TZ, false /* oneMatch */);
// The three parameters match the configured zone: offset, DST offset and time.
assertOffsetResultEquals(expectedLondonOnlyMatch,
countryTimeZones.lookupByOffsetWithBias(dstOffset, NORMAL_DST_ADJUSTMENT, WHEN_DST,
null /* bias */));
assertOffsetResultEquals(expectedReykjavikBestMatch,
countryTimeZones.lookupByOffsetWithBias(noDstOffset, 0 /* no DST */, WHEN_NO_DST,
null /* bias */));
assertOffsetResultEquals(expectedLondonOnlyMatch,
countryTimeZones.lookupByOffsetWithBias(dstOffset, NORMAL_DST_ADJUSTMENT, WHEN_DST,
null /* bias */));
assertOffsetResultEquals(expectedReykjavikBestMatch,
countryTimeZones.lookupByOffsetWithBias(noDstOffset, 0 /* no DST */, WHEN_NO_DST,
null /* bias */));
assertOffsetResultEquals(expectedReykjavikBestMatch,
countryTimeZones.lookupByOffsetWithBias(noDstOffset, 0 /* no DST */, WHEN_DST,
null /* bias */));
// Unknown DST cases
assertOffsetResultEquals(expectedReykjavikBestMatch,
countryTimeZones.lookupByOffsetWithBias(noDstOffset, null, WHEN_NO_DST,
null /* bias */));
assertOffsetResultEquals(expectedReykjavikBestMatch,
countryTimeZones.lookupByOffsetWithBias(noDstOffset, null, WHEN_DST,
null /* bias */));
assertNull(countryTimeZones.lookupByOffsetWithBias(dstOffset, null, WHEN_NO_DST,
null /* bias */));
assertOffsetResultEquals(expectedLondonOnlyMatch,
countryTimeZones.lookupByOffsetWithBias(dstOffset, null, WHEN_DST,
null /* bias */));
// Some lookup failure cases where the offset, DST offset and time do not match the
// configured zones.
OffsetResult noDstMatch1 = countryTimeZones.lookupByOffsetWithBias(dstOffset,
NORMAL_DST_ADJUSTMENT, WHEN_NO_DST, null /* bias */);
assertNull(noDstMatch1);
OffsetResult noDstMatch2 = countryTimeZones.lookupByOffsetWithBias(noDstOffset,
NORMAL_DST_ADJUSTMENT, WHEN_DST, null /* bias */);
assertNull(noDstMatch2);
OffsetResult noDstMatch3 = countryTimeZones.lookupByOffsetWithBias(noDstOffset,
NORMAL_DST_ADJUSTMENT, WHEN_NO_DST, null /* bias */);
assertNull(noDstMatch3);
OffsetResult noDstMatch4 = countryTimeZones.lookupByOffsetWithBias(dstOffset,
0 /* no DST */, WHEN_DST, null /* bias */);
assertNull(noDstMatch4);
// Some bias cases below.
// Multiple zones match but Reykjavik is the bias.
assertOffsetResultEquals(expectedReykjavikBestMatch,
countryTimeZones.lookupByOffsetWithBias(noDstOffset, 0 /* no DST */, WHEN_NO_DST,
REYKJAVIK_TZ /* bias */));
assertOffsetResultEquals(expectedReykjavikBestMatch,
countryTimeZones.lookupByOffsetWithBias(noDstOffset, null /* unknown DST */,
WHEN_NO_DST, REYKJAVIK_TZ /* bias */));
// Multiple zones match but London is the bias.
OffsetResult expectedLondonBestMatch = new OffsetResult(LONDON_TZ, false /* oneMatch */);
assertOffsetResultEquals(expectedLondonBestMatch,
countryTimeZones.lookupByOffsetWithBias(noDstOffset, 0 /* no DST */, WHEN_NO_DST,
LONDON_TZ /* bias */));
assertOffsetResultEquals(expectedLondonBestMatch,
countryTimeZones.lookupByOffsetWithBias(noDstOffset, null /* unknown DST */,
WHEN_NO_DST, LONDON_TZ /* bias */));
// Multiple zones match but UTC is the bias.
OffsetResult expectedUtcResult = new OffsetResult(UTC_TZ, false /* oneMatch */);
assertOffsetResultEquals(expectedUtcResult,
countryTimeZones.lookupByOffsetWithBias(noDstOffset, 0 /* no DST */, WHEN_NO_DST,
UTC_TZ /* bias */));
assertOffsetResultEquals(expectedUtcResult,
countryTimeZones.lookupByOffsetWithBias(noDstOffset, null /* unknown DST */,
WHEN_NO_DST, UTC_TZ /* bias */));
// The bias should be ignored: it matches a configured zone, but the offset is wrong so
// should not be considered a match.
assertOffsetResultEquals(expectedLondonOnlyMatch,
countryTimeZones.lookupByOffsetWithBias(LONDON_DST_OFFSET_MILLIS,
NORMAL_DST_ADJUSTMENT, WHEN_DST, REYKJAVIK_TZ /* bias */));
}
@Test
public void getEffectiveTimeZonesAt_noZones() {
CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
"xx", "Europe/London", true /* everUsesUtc */, timeZoneMappings(), "test");
assertEquals(timeZoneMappings(),
countryTimeZones.getEffectiveTimeZoneMappingsAt(0 /* whenMillis */));
assertEquals(timeZoneMappings(),
countryTimeZones.getEffectiveTimeZoneMappingsAt(Long.MIN_VALUE));
assertEquals(timeZoneMappings(),
countryTimeZones.getEffectiveTimeZoneMappingsAt(Long.MAX_VALUE));
}
@Test
public void getEffectiveTimeZonesAt_oneZone() {
CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
"xx", "Europe/London", true /* everUsesUtc */,
timeZoneMappings("Europe/London"), "test");
assertEquals(timeZoneMappings("Europe/London"),
countryTimeZones.getEffectiveTimeZoneMappingsAt(0));
assertEquals(timeZoneMappings("Europe/London"),
countryTimeZones.getEffectiveTimeZoneMappingsAt(Long.MIN_VALUE));
assertEquals(timeZoneMappings("Europe/London"),
countryTimeZones.getEffectiveTimeZoneMappingsAt(Long.MAX_VALUE));
}
@Test
public void getEffectiveTimeZonesAt_filtering() {
TimeZoneMapping alwaysUsed = timeZoneMapping("Europe/London", null /* notUsedAfter */);
long mappingNotUsedAfterMillis = 0L;
TimeZoneMapping notAlwaysUsed = timeZoneMapping("Europe/Paris",
mappingNotUsedAfterMillis /* notUsedAfter */);
List<TimeZoneMapping> timeZoneMappings = list(alwaysUsed, notAlwaysUsed);
CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
"xx", "Europe/London", true /* everUsesUtc */, timeZoneMappings, "test");
// Before and at mappingNotUsedAfterMillis, both mappings are "effective".
assertEquals(list(alwaysUsed, notAlwaysUsed),
countryTimeZones.getEffectiveTimeZoneMappingsAt(Long.MIN_VALUE));
assertEquals(list(alwaysUsed, notAlwaysUsed),
countryTimeZones.getEffectiveTimeZoneMappingsAt(mappingNotUsedAfterMillis));
// The following should filter the second mapping because it's not "effective" after
// mappingNotUsedAfterMillis.
assertEquals(list(alwaysUsed),
countryTimeZones.getEffectiveTimeZoneMappingsAt(mappingNotUsedAfterMillis + 1));
assertEquals(list(alwaysUsed),
countryTimeZones.getEffectiveTimeZoneMappingsAt(Long.MAX_VALUE));
}
@Test
public void hasUtcZone_everUseUtcHintOverridesZoneInformation() {
// The country has a single zone. Europe/London uses UTC in Winter.
CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
"xx", "Etc/UTC", false /* everUsesUtc */, timeZoneMappings("Etc/UTC"), "test");
assertFalse(countryTimeZones.hasUtcZone(WHEN_DST));
assertFalse(countryTimeZones.hasUtcZone(WHEN_NO_DST));
}
@Test
public void hasUtcZone_singleZone() {
// The country has a single zone. Europe/London uses UTC in Winter.
CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
"xx", "Europe/London", true /* everUsesUtc */, timeZoneMappings("Europe/London"),
"test");
assertFalse(countryTimeZones.hasUtcZone(WHEN_DST));
assertTrue(countryTimeZones.hasUtcZone(WHEN_NO_DST));
}
@Test
public void hasUtcZone_multipleZonesWithUtc() {
// The country has multiple zones. Europe/London uses UTC in Winter.
CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
"xx", "America/Los_Angeles", true /* everUsesUtc */,
timeZoneMappings("America/Los_Angeles", "America/New_York", "Europe/London"),
"test");
assertFalse(countryTimeZones.hasUtcZone(WHEN_DST));
assertTrue(countryTimeZones.hasUtcZone(WHEN_NO_DST));
}
@Test
public void hasUtcZone_multipleZonesWithoutUtc() {
// The country has multiple zones, none of which use UTC.
CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
"xx", "Europe/Paris", false /* everUsesUtc */,
timeZoneMappings("America/Los_Angeles", "America/New_York", "Europe/Paris"),
"test");
assertFalse(countryTimeZones.hasUtcZone(WHEN_DST));
assertFalse(countryTimeZones.hasUtcZone(WHEN_NO_DST));
}
@Test
public void hasUtcZone_emptyZones() {
// The country has no valid zones.
CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
"xx", INVALID_TZ_ID, false /* everUsesUtc */, timeZoneMappings(INVALID_TZ_ID),
"test");
assertTrue(countryTimeZones.getTimeZoneMappings().isEmpty());
assertFalse(countryTimeZones.hasUtcZone(WHEN_DST));
assertFalse(countryTimeZones.hasUtcZone(WHEN_NO_DST));
}
@Test
public void timeZoneMapping_getTimeZone_badZoneId() {
TimeZoneMapping timeZoneMapping =
TimeZoneMapping.createForTests("DOES_NOT_EXIST", true, 1234L);
assertNull(timeZoneMapping.getTimeZone());
}
@Test
public void timeZoneMapping_getTimeZone_validZoneId() {
TimeZoneMapping timeZoneMapping =
TimeZoneMapping.createForTests("Europe/London", true, 1234L);
TimeZone timeZone = timeZoneMapping.getTimeZone();
assertTrue(timeZone.isFrozen());
assertEquals("Europe/London", timeZone.getID());
}
private void assertImmutableTimeZone(TimeZone timeZone) {
try {
timeZone.setRawOffset(1000);
fail();
} catch (UnsupportedOperationException expected) {
}
}
private static <X> void assertImmutableList(List<X> list) {
try {
list.add(null);
fail();
} catch (UnsupportedOperationException expected) {
}
}
private static void assertZoneEquals(TimeZone expected, TimeZone actual) {
// TimeZone.equals() only checks the ID, but that's ok for these tests.
assertEquals(expected, actual);
}
private static void assertOffsetResultEquals(OffsetResult expected, OffsetResult actual) {
assertEquals(expected.mTimeZone.getID(), actual.mTimeZone.getID());
assertEquals(expected.mOneMatch, actual.mOneMatch);
}
/**
* Creates a list of default {@link TimeZoneMapping} objects with the specified time zone IDs.
*/
private static TimeZoneMapping timeZoneMapping(String timeZoneId, Long notUsedAfterMillis) {
return TimeZoneMapping.createForTests(
timeZoneId, true /* picker */, notUsedAfterMillis);
}
/**
* Creates a list of default {@link TimeZoneMapping} objects with the specified time zone IDs.
*/
private static List<TimeZoneMapping> timeZoneMappings(String... timeZoneIds) {
return Arrays.stream(timeZoneIds)
.map(x -> TimeZoneMapping.createForTests(
x, true /* picker */, null /* notUsedAfter */))
.collect(Collectors.toList());
}
private static TimeZone zone(String id) {
return TimeZone.getFrozenTimeZone(id);
}
private static <X> List<X> list(X... xes) {
return Arrays.asList(xes);
}
}