blob: e2cf30731721599e81111b64b11f237e377c40c5 [file] [log] [blame]
/*
* Copyright 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 com.android.internal.telephony;
import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
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 libcore.timezone.TimeZoneFinder;
import org.junit.Before;
import org.junit.Test;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
public class TimeZoneLookupHelperTest {
// Note: Historical dates are used to avoid the test breaking due to data changes.
/* Arbitrary summer date in the Northern hemisphere. */
private static final long NH_SUMMER_TIME_MILLIS = createUtcTime(2015, 6, 20, 1, 2, 3);
/* Arbitrary winter date in the Northern hemisphere. */
private static final long NH_WINTER_TIME_MILLIS = createUtcTime(2015, 1, 20, 1, 2, 3);
private TimeZoneLookupHelper mTimeZoneLookupHelper;
@Before
public void setUp() {
mTimeZoneLookupHelper = new TimeZoneLookupHelper();
}
@Test
public void testLookupByNitzdByNitz() {
// Historical dates are used to avoid the test breaking due to data changes.
// However, algorithm updates may change the exact time zone returned, though it shouldn't
// ever be a less exact match.
long nhSummerTimeMillis = createUtcTime(2015, 6, 20, 1, 2, 3);
long nhWinterTimeMillis = createUtcTime(2015, 1, 20, 1, 2, 3);
String nhSummerTimeString = "15/06/20,01:02:03";
String nhWinterTimeString = "15/01/20,01:02:03";
// Tests for London, UK.
{
String lonSummerTimeString = nhSummerTimeString + "+4";
int lonSummerOffsetMillis = (int) TimeUnit.HOURS.toMillis(1);
int lonSummerDstOffsetMillis = (int) TimeUnit.HOURS.toMillis(1);
String lonWinterTimeString = nhWinterTimeString + "+0";
int lonWinterOffsetMillis = 0;
int lonWinterDstOffsetMillis = 0;
OffsetResult lookupResult;
// Summer, known DST state (DST == true).
NitzData lonSummerNitzDataWithOffset = NitzData.parse(lonSummerTimeString + ",4");
lookupResult = mTimeZoneLookupHelper.lookupByNitz(lonSummerNitzDataWithOffset);
assertOffsetResultZoneOffsets(nhSummerTimeMillis, lonSummerOffsetMillis,
lonSummerDstOffsetMillis, lookupResult);
assertOffsetResultMetadata(false, lookupResult);
// Winter, known DST state (DST == false).
NitzData lonWinterNitzDataWithOffset = NitzData.parse(lonWinterTimeString + ",0");
lookupResult = mTimeZoneLookupHelper.lookupByNitz(lonWinterNitzDataWithOffset);
assertOffsetResultZoneOffsets(nhWinterTimeMillis, lonWinterOffsetMillis,
lonWinterDstOffsetMillis, lookupResult);
assertOffsetResultMetadata(false, lookupResult);
// Summer, unknown DST state
NitzData lonSummerNitzDataWithoutOffset = NitzData.parse(lonSummerTimeString);
lookupResult = mTimeZoneLookupHelper.lookupByNitz(lonSummerNitzDataWithoutOffset);
assertOffsetResultZoneOffsets(nhSummerTimeMillis, lonSummerOffsetMillis, null,
lookupResult);
assertOffsetResultMetadata(false, lookupResult);
// Winter, unknown DST state
NitzData lonWinterNitzDataWithoutOffset = NitzData.parse(lonWinterTimeString);
lookupResult = mTimeZoneLookupHelper.lookupByNitz(lonWinterNitzDataWithoutOffset);
assertOffsetResultZoneOffsets(nhWinterTimeMillis, lonWinterOffsetMillis, null,
lookupResult);
assertOffsetResultMetadata(false, lookupResult);
}
// Tests for Mountain View, CA, US.
{
String mtvSummerTimeString = nhSummerTimeString + "-32";
int mtvSummerOffsetMillis = (int) TimeUnit.HOURS.toMillis(-8);
int mtvSummerDstOffsetMillis = (int) TimeUnit.HOURS.toMillis(1);
String mtvWinterTimeString = nhWinterTimeString + "-28";
int mtvWinterOffsetMillis = (int) TimeUnit.HOURS.toMillis(-7);
int mtvWinterDstOffsetMillis = 0;
OffsetResult lookupResult;
// Summer, known DST state (DST == true).
NitzData mtvSummerNitzDataWithOffset = NitzData.parse(mtvSummerTimeString + ",4");
lookupResult = mTimeZoneLookupHelper.lookupByNitz(mtvSummerNitzDataWithOffset);
assertOffsetResultZoneOffsets(nhSummerTimeMillis, mtvSummerOffsetMillis,
mtvSummerDstOffsetMillis, lookupResult);
assertOffsetResultMetadata(false, lookupResult);
// Winter, known DST state (DST == false).
NitzData mtvWinterNitzDataWithOffset = NitzData.parse(mtvWinterTimeString + ",0");
lookupResult = mTimeZoneLookupHelper.lookupByNitz(mtvWinterNitzDataWithOffset);
assertOffsetResultZoneOffsets(nhWinterTimeMillis, mtvWinterOffsetMillis,
mtvWinterDstOffsetMillis, lookupResult);
assertOffsetResultMetadata(false, lookupResult);
// Summer, unknown DST state
NitzData mtvSummerNitzDataWithoutOffset = NitzData.parse(mtvSummerTimeString);
lookupResult = mTimeZoneLookupHelper.lookupByNitz(mtvSummerNitzDataWithoutOffset);
assertOffsetResultZoneOffsets(nhSummerTimeMillis, mtvSummerOffsetMillis, null,
lookupResult);
assertOffsetResultMetadata(false, lookupResult);
// Winter, unknown DST state
NitzData mtvWinterNitzDataWithoutOffset = NitzData.parse(mtvWinterTimeString);
lookupResult = mTimeZoneLookupHelper.lookupByNitz(mtvWinterNitzDataWithoutOffset);
assertOffsetResultZoneOffsets(nhWinterTimeMillis, mtvWinterOffsetMillis, null,
lookupResult);
assertOffsetResultMetadata(false, lookupResult);
}
}
@Test
public void testLookupByNitzCountry() {
// Historical dates are used to avoid the test breaking due to data changes.
// However, algorithm updates may change the exact time zone returned, though it shouldn't
// ever be a less exact match.
long nhSummerTimeMillis = createUtcTime(2015, 6, 20, 1, 2, 3);
long nhWinterTimeMillis = createUtcTime(2015, 1, 20, 1, 2, 3);
// Two countries in the northern hemisphere that share the same Winter and Summer DST
// offsets at the dates being used.
String deIso = "DE"; // Germany
String adIso = "AD"; // Andora
String summerTimeNitzString = "15/06/20,01:02:03+8";
String winterTimeNitzString = "15/01/20,01:02:03+4";
// Summer, known DST state (DST == true).
{
String summerTimeNitzStringWithDst = summerTimeNitzString + ",1";
NitzData nitzData = NitzData.parse(summerTimeNitzStringWithDst);
int expectedUtcOffset = (int) TimeUnit.HOURS.toMillis(2);
Integer expectedDstOffset = (int) TimeUnit.HOURS.toMillis(1);
assertEquals(expectedUtcOffset, nitzData.getLocalOffsetMillis());
assertEquals(expectedDstOffset, nitzData.getDstAdjustmentMillis());
OffsetResult expectedResult;
OffsetResult deSummerWithDstResult =
mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, deIso);
expectedResult = new OffsetResult("Europe/Berlin", false /* isOnlyMatch */);
assertEquals(expectedResult, deSummerWithDstResult);
assertOffsetResultZoneOffsets(nhSummerTimeMillis, expectedUtcOffset, expectedDstOffset,
deSummerWithDstResult);
OffsetResult adSummerWithDstResult =
mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, adIso);
expectedResult = new OffsetResult("Europe/Andorra", true /* isOnlyMatch */);
assertEquals(expectedResult, adSummerWithDstResult);
assertOffsetResultZoneOffsets(nhSummerTimeMillis, expectedUtcOffset, expectedDstOffset,
adSummerWithDstResult);
assertOffsetResultZoneCountry(adIso, adSummerWithDstResult);
}
// Winter, known DST state (DST == false)
{
String winterTimeNitzStringWithDst = winterTimeNitzString + ",0";
NitzData nitzData = NitzData.parse(winterTimeNitzStringWithDst);
int expectedUtcOffset = (int) TimeUnit.HOURS.toMillis(1);
Integer expectedDstOffset = 0;
assertEquals(expectedUtcOffset, nitzData.getLocalOffsetMillis());
assertEquals(expectedDstOffset, nitzData.getDstAdjustmentMillis());
OffsetResult expectedResult;
OffsetResult deWinterWithDstResult =
mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, deIso);
expectedResult = new OffsetResult("Europe/Berlin", false /* isOnlyMatch */);
assertEquals(expectedResult, deWinterWithDstResult);
assertOffsetResultZoneOffsets(nhWinterTimeMillis, expectedUtcOffset, expectedDstOffset,
deWinterWithDstResult);
OffsetResult adWinterWithDstResult =
mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, adIso);
expectedResult = new OffsetResult("Europe/Andorra", true /* isOnlyMatch */);
assertEquals(expectedResult, adWinterWithDstResult);
assertOffsetResultZoneOffsets(nhWinterTimeMillis, expectedUtcOffset, expectedDstOffset,
adWinterWithDstResult);
}
// Summer, unknown DST state
// For historic reasons, GuessZoneIdByNitzCountry() does not handle unknown DST state - it
// assumes that "unknown DST" means "no DST": This leads to no match when DST is actually in
// force.
{
NitzData nitzData = NitzData.parse(summerTimeNitzString);
int expectedUtcOffset = (int) TimeUnit.HOURS.toMillis(2);
Integer expectedDstOffset = null; // Unknown
assertEquals(expectedUtcOffset, nitzData.getLocalOffsetMillis());
assertEquals(expectedDstOffset, nitzData.getDstAdjustmentMillis());
OffsetResult deSummerUnknownDstResult =
mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, deIso);
assertNull(deSummerUnknownDstResult);
OffsetResult adSummerUnknownDstResult =
mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, adIso);
assertNull(adSummerUnknownDstResult);
}
// Winter, unknown DST state
{
NitzData nitzData = NitzData.parse(winterTimeNitzString);
int expectedUtcOffset = (int) TimeUnit.HOURS.toMillis(1);
Integer expectedDstOffset = null; // Unknown
assertEquals(expectedUtcOffset, nitzData.getLocalOffsetMillis());
assertEquals(expectedDstOffset, nitzData.getDstAdjustmentMillis());
OffsetResult expectedResult;
OffsetResult deWinterUnknownDstResult =
mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, deIso);
expectedResult = new OffsetResult("Europe/Berlin", false /* isOnlyMatch */);
assertEquals(expectedResult, deWinterUnknownDstResult);
assertOffsetResultZoneOffsets(nhWinterTimeMillis, expectedUtcOffset, expectedDstOffset,
deWinterUnknownDstResult);
OffsetResult adWinterUnknownDstResult =
mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, adIso);
expectedResult = new OffsetResult("Europe/Andorra", true /* isOnlyMatch */);
assertEquals(expectedResult, adWinterUnknownDstResult);
assertOffsetResultZoneOffsets(nhWinterTimeMillis, expectedUtcOffset, expectedDstOffset,
adWinterUnknownDstResult);
}
}
@Test
public void testLookupByCountry() {
// Historical dates are used to avoid the test breaking due to data changes.
long nhSummerTimeMillis = createUtcTime(2015, 6, 20, 1, 2, 3);
long nhWinterTimeMillis = createUtcTime(2015, 1, 20, 1, 2, 3);
CountryResult expectedResult;
// GB has one time zone.
expectedResult = new CountryResult("Europe/London", true /* allZonesHaveSameOffset */,
nhSummerTimeMillis);
assertEquals(expectedResult,
mTimeZoneLookupHelper.lookupByCountry("gb", nhSummerTimeMillis));
expectedResult = new CountryResult("Europe/London", true /* allZonesHaveSameOffset */,
nhWinterTimeMillis);
assertEquals(expectedResult,
mTimeZoneLookupHelper.lookupByCountry("gb", nhWinterTimeMillis));
// DE has two time zones according to data, but they agree on offset.
expectedResult = new CountryResult("Europe/Berlin", true /* allZonesHaveSameOffset */,
nhSummerTimeMillis);
assertEquals(expectedResult,
mTimeZoneLookupHelper.lookupByCountry("de", nhSummerTimeMillis));
expectedResult = new CountryResult("Europe/Berlin", true /* allZonesHaveSameOffset */,
nhWinterTimeMillis);
assertEquals(expectedResult,
mTimeZoneLookupHelper.lookupByCountry("de", nhWinterTimeMillis));
// US has many time zones that have different offsets.
expectedResult = new CountryResult("America/New_York", false /* allZonesHaveSameOffset */,
nhSummerTimeMillis);
assertEquals(expectedResult,
mTimeZoneLookupHelper.lookupByCountry("us", nhSummerTimeMillis));
expectedResult = new CountryResult("America/New_York", false /* allZonesHaveSameOffset */,
nhWinterTimeMillis);
assertEquals(expectedResult,
mTimeZoneLookupHelper.lookupByCountry("us", nhWinterTimeMillis));
}
@Test
public void testCountryUsesUtc() {
assertFalse(mTimeZoneLookupHelper.countryUsesUtc("us", NH_SUMMER_TIME_MILLIS));
assertFalse(mTimeZoneLookupHelper.countryUsesUtc("us", NH_WINTER_TIME_MILLIS));
assertFalse(mTimeZoneLookupHelper.countryUsesUtc("gb", NH_SUMMER_TIME_MILLIS));
assertTrue(mTimeZoneLookupHelper.countryUsesUtc("gb", NH_WINTER_TIME_MILLIS));
}
private static void assertOffsetResultZoneCountry(
String isoCountryCode, OffsetResult lookupResult) {
String timeZoneId = lookupResult.zoneId;
List<String> zoneIdsByCountry =
TimeZoneFinder.getInstance().lookupTimeZoneIdsByCountry(isoCountryCode);
assertTrue(timeZoneId + " must be used in " + isoCountryCode,
zoneIdsByCountry.contains(timeZoneId));
}
/**
* Assert the time zone in the OffsetResult has the expected properties at the specified time.
*/
private static void assertOffsetResultZoneOffsets(long time, int expectedOffsetAtTime,
Integer expectedDstAtTime, OffsetResult lookupResult) {
TimeZone timeZone = TimeZone.getTimeZone(lookupResult.zoneId);
GregorianCalendar calendar = new GregorianCalendar(timeZone);
calendar.setTimeInMillis(time);
int actualOffsetAtTime =
calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET);
assertEquals(expectedOffsetAtTime, actualOffsetAtTime);
if (expectedDstAtTime != null) {
Date date = new Date(time);
assertEquals(expectedDstAtTime > 0, timeZone.inDaylightTime(date));
// The code under test assumes DST means +1 in all cases,
// This code makes fewer assumptions.
assertEquals(expectedDstAtTime.intValue(), calendar.get(Calendar.DST_OFFSET));
}
}
private static void assertOffsetResultMetadata(boolean isOnlyMatch, OffsetResult lookupResult) {
assertEquals(isOnlyMatch, lookupResult.isOnlyMatch);
}
private static long createUtcTime(
int year, int monthOfYear, int dayOfMonth, int hourOfDay, int minute, int second) {
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
calendar.clear(); // Clear millis, etc.
calendar.set(year, monthOfYear - 1, dayOfMonth, hourOfDay, minute, second);
return calendar.getTimeInMillis();
}
}