| /* |
| * Copyright (C) 2016 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.java.util; |
| |
| import java.text.SimpleDateFormat; |
| import java.util.Calendar; |
| import java.util.Date; |
| import java.util.GregorianCalendar; |
| import java.util.Locale; |
| import java.util.SimpleTimeZone; |
| import java.util.TimeZone; |
| import junit.framework.TestCase; |
| |
| /** |
| * Tests for {@link SimpleTimeZone}. |
| * |
| * <p>The methods starting {@code testDstParis2014_...} and {@code testDstNewYork2014} check |
| * various different ways to specify the same instants when DST starts and ends in the associated |
| * real world time zone in 2014, i.e. Europe/Paris and America/New_York respectively. |
| */ |
| public class SimpleTimeZoneTest extends TestCase { |
| |
| private static final int NEW_YORK_RAW_OFFSET = -18000000; |
| |
| private static final int PARIS_RAW_OFFSET = 3600000; |
| |
| private static final TimeZone UTC = TimeZone.getTimeZone("UTC"); |
| |
| /** |
| * Sanity check to ensure that the standard TimeZone for Europe/Paris has the correct DST |
| * transition times. |
| */ |
| public void testStandardParis2014() { |
| TimeZone timeZone = TimeZone.getTimeZone("Europe/Paris"); |
| |
| checkDstParis2014(timeZone); |
| } |
| |
| public void testDstParis2014_LastSundayMarch_LastSundayOctober_UtcTime() { |
| TimeZone timeZone = new SimpleTimeZone(PARIS_RAW_OFFSET, "Europe/Paris", |
| Calendar.MARCH, -1, Calendar.SUNDAY, 3600000, SimpleTimeZone.UTC_TIME, |
| Calendar.OCTOBER, -1, Calendar.SUNDAY, 3600000, SimpleTimeZone.UTC_TIME, 3600000); |
| |
| checkDstParis2014(timeZone); |
| } |
| |
| public void testDstParis2014_SundayAfter25thMarch_SundayAfter25thOctober_UtcTime() { |
| TimeZone timeZone = new SimpleTimeZone(PARIS_RAW_OFFSET, "Europe/Paris", |
| Calendar.MARCH, 25, -Calendar.SUNDAY, 3600000, SimpleTimeZone.UTC_TIME, |
| Calendar.OCTOBER, 25, -Calendar.SUNDAY, 3600000, SimpleTimeZone.UTC_TIME, 3600000); |
| |
| checkDstParis2014(timeZone); |
| } |
| |
| public void testDstParis2014_30thMarch_26thOctober_UtcTime() { |
| TimeZone timeZone = new SimpleTimeZone(PARIS_RAW_OFFSET, "Europe/Paris", |
| Calendar.MARCH, 30, 0, 3600000, SimpleTimeZone.UTC_TIME, |
| Calendar.OCTOBER, 26, 0, 3600000, SimpleTimeZone.UTC_TIME, 3600000); |
| |
| checkDstParis2014(timeZone); |
| } |
| |
| /** |
| * Check that the DST transitions in the supplied {@link TimeZone} are as expected for |
| * Europe/Paris in 2014. |
| */ |
| private void checkDstParis2014(TimeZone timeZone) { |
| checkDstTransitionTimes(timeZone, 2014, |
| "2014-03-30T01:00:00.000+0000", |
| "2014-10-26T01:00:00.000+0000"); |
| } |
| |
| public void testDst_1stSundayApril_1stSundayOctober_DefaultTime() { |
| TimeZone timeZone = new SimpleTimeZone(-18000000, "EST", |
| Calendar.APRIL, 1, -Calendar.SUNDAY, 7200000, |
| Calendar.OCTOBER, -1, Calendar.SUNDAY, 7200000, |
| 3600000); |
| |
| checkDstTransitionTimes(timeZone, 1998, |
| "1998-04-05T07:00:00.000+0000", |
| "1998-10-25T06:00:00.000+0000"); |
| |
| checkDstTransitionTimes(timeZone, 2014, |
| "2014-04-06T07:00:00.000+0000", |
| "2014-10-26T06:00:00.000+0000"); |
| } |
| |
| /** |
| * Sanity check to ensure that the standard TimeZone for America/New_York has the correct DST |
| * transition times. |
| */ |
| public void testStandardNewYork2014() { |
| TimeZone timeZone = TimeZone.getTimeZone("America/New_York"); |
| |
| checkDstNewYork2014(timeZone); |
| } |
| |
| public void testDstNewYork2014_2ndSundayMarch_1stSundayNovember_StandardTime() { |
| TimeZone timeZone = new SimpleTimeZone(NEW_YORK_RAW_OFFSET, "EST", |
| Calendar.MARCH, 2, Calendar.SUNDAY, 7200000, SimpleTimeZone.STANDARD_TIME, |
| Calendar.NOVEMBER, 1, Calendar.SUNDAY, 3600000, SimpleTimeZone.STANDARD_TIME, |
| 3600000); |
| |
| checkDstNewYork2014(timeZone); |
| } |
| |
| public void testDstNewYork2014_2ndSundayMarch_1stSundayNovember_UtcTime() { |
| TimeZone timeZone = new SimpleTimeZone(NEW_YORK_RAW_OFFSET, "EST", |
| Calendar.MARCH, 2, Calendar.SUNDAY, 25200000, SimpleTimeZone.UTC_TIME, |
| Calendar.NOVEMBER, 1, Calendar.SUNDAY, 21600000, SimpleTimeZone.UTC_TIME, |
| 3600000); |
| |
| checkDstNewYork2014(timeZone); |
| } |
| |
| public void testDstNewYork2014_2ndSundayMarch_1stSundayNovember_WallTime() { |
| TimeZone timeZone = new SimpleTimeZone(NEW_YORK_RAW_OFFSET, "EST", |
| Calendar.MARCH, 2, Calendar.SUNDAY, 7200000, SimpleTimeZone.WALL_TIME, |
| Calendar.NOVEMBER, 1, Calendar.SUNDAY, 7200000, SimpleTimeZone.WALL_TIME, |
| 3600000); |
| |
| checkDstNewYork2014(timeZone); |
| } |
| |
| public void testDstNewYork2014_2ndSundayMarch_1stSundayNovember_DefaultTime() { |
| TimeZone timeZone = new SimpleTimeZone(NEW_YORK_RAW_OFFSET, "EST", |
| Calendar.MARCH, 2, Calendar.SUNDAY, 7200000, |
| Calendar.NOVEMBER, 1, Calendar.SUNDAY, 7200000, |
| 3600000); |
| |
| checkDstNewYork2014(timeZone); |
| } |
| |
| public void testDstNewYork2014_9thMarch_2ndNovember_StandardTime() { |
| TimeZone timeZone = new SimpleTimeZone(NEW_YORK_RAW_OFFSET, "EST", |
| Calendar.MARCH, 9, 0, 7200000, SimpleTimeZone.STANDARD_TIME, |
| Calendar.NOVEMBER, 2, 0, 3600000, SimpleTimeZone.STANDARD_TIME, |
| 3600000); |
| |
| checkDstNewYork2014(timeZone); |
| } |
| |
| public void testDstNewYork2014_9thMarch_2ndNovember_UtcTime() { |
| TimeZone timeZone = new SimpleTimeZone(NEW_YORK_RAW_OFFSET, "EST", |
| Calendar.MARCH, 9, 0, 25200000, SimpleTimeZone.UTC_TIME, |
| Calendar.NOVEMBER, 2, 0, 21600000, SimpleTimeZone.UTC_TIME, |
| 3600000); |
| |
| checkDstNewYork2014(timeZone); |
| } |
| |
| public void testDstNewYork2014_9thMarch_2ndNovember_WallTime() { |
| TimeZone timeZone = new SimpleTimeZone(NEW_YORK_RAW_OFFSET, "EST", |
| Calendar.MARCH, 9, 0, 7200000, SimpleTimeZone.WALL_TIME, |
| Calendar.NOVEMBER, 2, 0, 7200000, SimpleTimeZone.WALL_TIME, |
| 3600000); |
| |
| checkDstNewYork2014(timeZone); |
| } |
| |
| public void testDstNewYork2014_9thMarch_2ndNovember_DefaultTime() { |
| TimeZone timeZone = new SimpleTimeZone(NEW_YORK_RAW_OFFSET, "EST", |
| Calendar.MARCH, 9, 0, 7200000, |
| Calendar.NOVEMBER, 2, 0, 7200000, |
| 3600000); |
| |
| checkDstNewYork2014(timeZone); |
| } |
| |
| /** |
| * Check that the DST transitions in the supplied {@link TimeZone} are as expected for |
| * America/New_York in 2014. |
| */ |
| private void checkDstNewYork2014(TimeZone timeZone) { |
| checkDstTransitionTimes(timeZone, 2014, |
| "2014-03-09T07:00:00.000+0000", |
| "2014-11-02T06:00:00.000+0000"); |
| } |
| |
| /** |
| * Scan from the start of the year to the end to find the DST transition points. |
| * |
| * @param timeZone the {@link TimeZone} whose transition points are being found. |
| * @param startOfYearMillis the start of the calendar year in {@code timeZone} to scan, in |
| * milliseconds. |
| * @return an array of the entry and exit time in millis. |
| */ |
| private static long[] findDstEntryAndExit(TimeZone timeZone, long startOfYearMillis) { |
| if (!timeZone.useDaylightTime()) { |
| throw new IllegalStateException("Time zone " + timeZone |
| + " doesn't support daylight savings time"); |
| } |
| |
| long[] transitions = new long[2]; |
| |
| GregorianCalendar cal = new GregorianCalendar(timeZone, Locale.ENGLISH); |
| cal.setTimeInMillis(startOfYearMillis); |
| int year = cal.get(Calendar.YEAR); |
| while (!timeZone.inDaylightTime(new Date(cal.getTimeInMillis()))) { |
| // Make sure that this doesn't loop forever. |
| if (cal.get(Calendar.YEAR) != year) { |
| throw new IllegalStateException( |
| "Doesn't enter daylight savings time in " + year + " in " + timeZone); |
| } |
| cal.add(Calendar.HOUR_OF_DAY, 1); |
| } |
| |
| cal.add(Calendar.MILLISECOND, -1); |
| assertFalse(timeZone.inDaylightTime(cal.getTime())); |
| cal.add(Calendar.MILLISECOND, 1); |
| long entryPoint = cal.getTimeInMillis(); |
| |
| while (timeZone.inDaylightTime(new Date(cal.getTimeInMillis()))) { |
| if (cal.get(Calendar.YEAR) != year) { |
| throw new IllegalStateException( |
| "Doesn't exit daylight savings time in " + year + " in " + timeZone); |
| } |
| cal.add(Calendar.HOUR_OF_DAY, 1); |
| } |
| |
| cal.add(Calendar.MILLISECOND, -1); |
| assertTrue(timeZone.inDaylightTime(cal.getTime())); |
| cal.add(Calendar.MILLISECOND, 1); |
| long exitPoint = cal.getTimeInMillis(); |
| |
| transitions[0] = entryPoint; |
| transitions[1] = exitPoint; |
| return transitions; |
| } |
| |
| public static String formatCalendar(Calendar cal) { |
| SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", |
| Locale.ENGLISH); |
| format.setTimeZone(cal.getTimeZone()); |
| return format.format(new Date(cal.getTimeInMillis())); |
| } |
| |
| private static String formatTime(TimeZone timeZone, long millis) { |
| Calendar cal = new GregorianCalendar(timeZone, Locale.ENGLISH); |
| cal.setTimeInMillis(millis); |
| return formatCalendar(cal); |
| } |
| |
| private void checkDstTransitionTimes(TimeZone timeZone, int year, |
| String expectedUtcEntryTime, String expectedUtcExitTime) { |
| |
| // Find the start of the year in the supplied time zone. |
| GregorianCalendar calendar = new GregorianCalendar(timeZone, Locale.ENGLISH); |
| calendar.set(year, Calendar.JANUARY, 1, 0, 0, 0); |
| calendar.set(Calendar.MILLISECOND, 0); |
| long start = calendar.getTimeInMillis(); |
| |
| // Find the DST transitions instants. |
| long[] simpleTransitions = findDstEntryAndExit(timeZone, start); |
| |
| String actualUtcEntryTime = formatTime(UTC, simpleTransitions[0]); |
| |
| String actualUtcExitTime = formatTime(UTC, simpleTransitions[1]); |
| |
| assertEquals("Transition point mismatch: ", |
| describeTransitions(expectedUtcEntryTime, expectedUtcExitTime), |
| describeTransitions(actualUtcEntryTime, actualUtcExitTime)); |
| } |
| |
| /** |
| * Create a string representation of the transition information to allow all aspects to be |
| * compared in one go providing a better error message. |
| */ |
| private String describeTransitions(String utcEntryTime, String utcExitTime) { |
| return "{Entry: " + utcEntryTime + ", Exit: " + utcExitTime + "}"; |
| } |
| } |