diff options
7 files changed, 835 insertions, 5 deletions
diff --git a/src/com/android/calendarcommon2/EventRecurrence.java b/src/com/android/calendarcommon2/EventRecurrence.java index 45fd12f..df0c6eb 100644 --- a/src/com/android/calendarcommon2/EventRecurrence.java +++ b/src/com/android/calendarcommon2/EventRecurrence.java @@ -17,7 +17,6 @@ package com.android.calendarcommon2; import android.text.TextUtils; -import android.text.format.Time; import android.util.Log; import android.util.TimeFormatException; diff --git a/src/com/android/calendarcommon2/RecurrenceProcessor.java b/src/com/android/calendarcommon2/RecurrenceProcessor.java index d0a647a..2707eec 100644 --- a/src/com/android/calendarcommon2/RecurrenceProcessor.java +++ b/src/com/android/calendarcommon2/RecurrenceProcessor.java @@ -17,7 +17,6 @@ package com.android.calendarcommon2; -import android.text.format.Time; import android.util.Log; import java.util.TreeSet; diff --git a/src/com/android/calendarcommon2/RecurrenceSet.java b/src/com/android/calendarcommon2/RecurrenceSet.java index 86e6a2d..6c153db 100644 --- a/src/com/android/calendarcommon2/RecurrenceSet.java +++ b/src/com/android/calendarcommon2/RecurrenceSet.java @@ -20,7 +20,6 @@ import android.content.ContentValues; import android.database.Cursor; import android.provider.CalendarContract; import android.text.TextUtils; -import android.text.format.Time; import android.util.Log; import android.util.TimeFormatException; diff --git a/src/com/android/calendarcommon2/Time.java b/src/com/android/calendarcommon2/Time.java new file mode 100644 index 0000000..08189cc --- /dev/null +++ b/src/com/android/calendarcommon2/Time.java @@ -0,0 +1,218 @@ +/* + * 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 com.android.calendarcommon2; + +/** + * Helper class to make migration out of android.text.format.Time smoother. + */ +public class Time { + + public static final String TIMEZONE_UTC = "UTC"; + + /* + * Define symbolic constants for accessing the fields in this class. Used in + * getActualMaximum(). + */ + public static final int SECOND = android.text.format.Time.SECOND; + public static final int MINUTE = android.text.format.Time.MINUTE; + public static final int HOUR = android.text.format.Time.HOUR; + public static final int MONTH_DAY = android.text.format.Time.MONTH_DAY; + public static final int MONTH = android.text.format.Time.MONTH; + public static final int YEAR = android.text.format.Time.YEAR; + public static final int WEEK_DAY = android.text.format.Time.WEEK_DAY; + public static final int YEAR_DAY = android.text.format.Time.YEAR_DAY; + public static final int WEEK_NUM = android.text.format.Time.WEEK_NUM; + + public static final int SUNDAY = android.text.format.Time.SUNDAY; + public static final int MONDAY = android.text.format.Time.MONDAY; + public static final int TUESDAY = android.text.format.Time.TUESDAY; + public static final int WEDNESDAY = android.text.format.Time.WEDNESDAY; + public static final int THURSDAY = android.text.format.Time.THURSDAY; + public static final int FRIDAY = android.text.format.Time.FRIDAY; + public static final int SATURDAY = android.text.format.Time.SATURDAY; + + private final android.text.format.Time mInstance; + + public int year; + public int month; + public int hour; + public int minute; + public int second; + + public int yearDay; + public int monthDay; + public int weekDay; + + public String timezone; + public int isDst; + public long gmtoff; + public boolean allDay; + + public Time() { + mInstance = new android.text.format.Time(); + readFields(); + } + + public Time(String timezone) { + mInstance = new android.text.format.Time(timezone); + readFields(); + } + + private void readFields() { + year = mInstance.year; + month = mInstance.month; + hour = mInstance.hour; + minute = mInstance.minute; + second = mInstance.second; + + yearDay = mInstance.yearDay; + monthDay = mInstance.monthDay; + weekDay = mInstance.weekDay; + + timezone = mInstance.timezone; + isDst = mInstance.isDst; + gmtoff = mInstance.gmtoff; + allDay = mInstance.allDay; + } + + private void writeFields() { + mInstance.year = year; + mInstance.month = month; + mInstance.hour = hour; + mInstance.minute = minute; + mInstance.second = second; + + mInstance.yearDay = yearDay; + mInstance.monthDay = monthDay; + mInstance.weekDay = weekDay; + + mInstance.timezone = timezone; + mInstance.isDst = isDst; + mInstance.gmtoff = gmtoff; + mInstance.allDay = allDay; + } + + public void set(long millis) { + writeFields(); + mInstance.set(millis); + readFields(); + } + + public void set(Time other) { + other.writeFields(); + mInstance.set(other.mInstance); + readFields(); + } + + public void set(int day, int month, int year) { + writeFields(); + mInstance.set(day, month, year); + readFields(); + } + + public void set(int second, int minute, int hour, int day, int month, int year) { + writeFields(); + mInstance.set(second, minute, hour, day, month, year); + readFields(); + } + + public void setToNow() { + writeFields(); + mInstance.setToNow(); + readFields(); + } + + public long setJulianDay(int julianDay) { + writeFields(); + final long ms = mInstance.setJulianDay(julianDay); + readFields(); + return ms; + } + + public static int getJulianDay(long begin, long gmtOff) { + return android.text.format.Time.getJulianDay(begin, gmtOff); + } + + public int getWeekNumber() { + writeFields(); + final int num = mInstance.getWeekNumber(); + readFields(); + return num; + } + + public int getActualMaximum(int field) { + writeFields(); + return mInstance.getActualMaximum(field); + } + + public void switchTimezone(String timezone) { + writeFields(); + mInstance.switchTimezone(timezone); + readFields(); + } + + public long normalize(boolean ignoreDst) { + writeFields(); + final long ms = mInstance.normalize(ignoreDst); + readFields(); + return ms; + } + + public boolean parse(String time) { + writeFields(); + boolean success = mInstance.parse(time); + readFields(); + return success; + } + + public boolean parse3339(String time) { + writeFields(); + boolean success = mInstance.parse3339(time); + readFields(); + return success; + } + + public String format(String format) { + writeFields(); + return (new android.text.format.Time(mInstance)).format(format); + } + + public String format2445() { + writeFields(); + return (new android.text.format.Time(mInstance)).format2445(); + } + + public String format3339(boolean allDay) { + writeFields(); + return (new android.text.format.Time(mInstance)).format3339(allDay); + } + + public long toMillis(boolean ignoreDst) { + writeFields(); + return mInstance.toMillis(ignoreDst); + } + + public static int compare(Time a, Time b) { + a.writeFields(); + b.writeFields(); + return android.text.format.Time.compare(a.mInstance, b.mInstance); + } + + public void clear(String timezoneId) { + mInstance.clear(timezoneId); + readFields(); + } +} diff --git a/tests/src/com/android/calendarcommon2/RRuleTest.java b/tests/src/com/android/calendarcommon2/RRuleTest.java index 1d72366..a473263 100644 --- a/tests/src/com/android/calendarcommon2/RRuleTest.java +++ b/tests/src/com/android/calendarcommon2/RRuleTest.java @@ -24,7 +24,6 @@ import com.android.calendarcommon2.RecurrenceSet; import android.os.Debug; import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.Suppress; -import android.text.format.Time; import junit.framework.TestCase; /** diff --git a/tests/src/com/android/calendarcommon2/RecurrenceProcessorTest.java b/tests/src/com/android/calendarcommon2/RecurrenceProcessorTest.java index 3503aae..0687ec4 100644 --- a/tests/src/com/android/calendarcommon2/RecurrenceProcessorTest.java +++ b/tests/src/com/android/calendarcommon2/RecurrenceProcessorTest.java @@ -22,7 +22,6 @@ import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; import android.text.TextUtils; -import android.text.format.Time; import android.util.Log; import android.util.TimeFormatException; import junit.framework.TestCase; diff --git a/tests/src/com/android/calendarcommon2/TimeTest.java b/tests/src/com/android/calendarcommon2/TimeTest.java new file mode 100644 index 0000000..600506a --- /dev/null +++ b/tests/src/com/android/calendarcommon2/TimeTest.java @@ -0,0 +1,617 @@ +/* + * 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 com.android.calendarcommon2; + +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.TimeFormatException; + +import junit.framework.TestCase; + +/** + * Tests for com.android.calendarcommon2.Time. + * + * Some of these tests are borrowed from android.text.format.TimeTest. + */ +public class TimeTest extends TestCase { + + @SmallTest + public void testTimezone() { + Time t = new Time(Time.TIMEZONE_UTC); + assertEquals(Time.TIMEZONE_UTC, t.timezone); + } + + @SmallTest + public void testSwitchTimezone() { + Time t = new Time(Time.TIMEZONE_UTC); + String newTimezone = "America/Los_Angeles"; + t.switchTimezone(newTimezone); + assertEquals(newTimezone, t.timezone); + } + + @SmallTest + public void testGetActualMaximum() { + Time t = new Time(Time.TIMEZONE_UTC); + t.set(0, 0, 2020); + assertEquals(59, t.getActualMaximum(Time.SECOND)); + assertEquals(59, t.getActualMaximum(Time.MINUTE)); + assertEquals(23, t.getActualMaximum(Time.HOUR)); + assertEquals(31, t.getActualMaximum(Time.MONTH_DAY)); + assertEquals(11, t.getActualMaximum(Time.MONTH)); + assertEquals(2037, t.getActualMaximum(Time.YEAR)); + assertEquals(6, t.getActualMaximum(Time.WEEK_DAY)); + assertEquals(365, t.getActualMaximum(Time.YEAR_DAY)); // 2020 is a leap year + t.set(0, 0, 2019); + assertEquals(364, t.getActualMaximum(Time.YEAR_DAY)); + } + + @SmallTest + public void testClear() { + Time t = new Time(Time.TIMEZONE_UTC); + t.clear(Time.TIMEZONE_UTC); + + assertEquals(Time.TIMEZONE_UTC, t.timezone); + assertFalse(t.allDay); + assertEquals(0, t.second); + assertEquals(0, t.minute); + assertEquals(0, t.hour); + assertEquals(0, t.monthDay); + assertEquals(0, t.month); + assertEquals(0, t.year); + assertEquals(0, t.weekDay); + assertEquals(0, t.yearDay); + assertEquals(0, t.gmtoff); + assertEquals(-1, t.isDst); + } + + @SmallTest + public void testCompare() { + Time a = new Time(Time.TIMEZONE_UTC); + Time b = new Time("America/Los_Angeles"); + assertTrue(Time.compare(a, b) < 0); + } + + @SmallTest + public void testFormat() { + Time t = new Time(Time.TIMEZONE_UTC); + assertEquals("19700101T000000", t.format("%Y%m%dT%H%M%S")); + } + + @SmallTest + public void testFormat2445() { + Time t = new Time(Time.TIMEZONE_UTC); + assertEquals("19700101T000000Z", t.format2445()); + } + + @SmallTest + public void testFormat3339() { + Time t = new Time(Time.TIMEZONE_UTC); + assertEquals("1970-01-01", t.format3339(true)); + t.set(29, 1, 2020); + assertEquals("2020-02-29", t.format3339(true)); + } + + @SmallTest + public void testMillis0() { + Time t = new Time(Time.TIMEZONE_UTC); + t.set(0, 0, 0, 1, 1, 2020); + assertEquals(1580515200000L, t.toMillis(true)); + t.set(1, 0, 0, 1, 1, 2020); + assertEquals(1580515201000L, t.toMillis(true)); + } + + @SmallTest + public void testMillis1() { + Time t = new Time(Time.TIMEZONE_UTC); + t.set(1, 0, 0, 1, 0, 1970); + assertEquals(1000L, t.toMillis(true)); + } + + @SmallTest + public void testParse() { + Time t = new Time(Time.TIMEZONE_UTC); + assertTrue(t.parse("20201010T160000Z")); + assertFalse(t.parse("12345678T901234")); + } + + @SmallTest + public void testParse3339() { + Time t = new Time(Time.TIMEZONE_UTC); + + t.parse3339("1980-05-23"); + if (!t.allDay || t.year != 1980 || t.month != 4 || t.monthDay != 23) { + fail("Did not parse all-day date correctly"); + } + + t.parse3339("1980-05-23T09:50:50"); + if (t.allDay || t.year != 1980 || t.month != 4 || t.monthDay != 23 || t.hour != 9 + || t.minute != 50 || t.second != 50 || t.gmtoff != 0) { + fail("Did not parse timezone-offset-less date correctly"); + } + + t.parse3339("1980-05-23T09:50:50Z"); + if (t.allDay || t.year != 1980 || t.month != 4 || t.monthDay != 23 || t.hour != 9 + || t.minute != 50 || t.second != 50 || t.gmtoff != 0) { + fail("Did not parse UTC date correctly"); + } + + t.parse3339("1980-05-23T09:50:50.0Z"); + if (t.allDay || t.year != 1980 || t.month != 4 || t.monthDay != 23 || t.hour != 9 + || t.minute != 50 || t.second != 50 || t.gmtoff != 0) { + fail("Did not parse UTC date correctly"); + } + + t.parse3339("1980-05-23T09:50:50.12Z"); + if (t.allDay || t.year != 1980 || t.month != 4 || t.monthDay != 23 || t.hour != 9 + || t.minute != 50 || t.second != 50 || t.gmtoff != 0) { + fail("Did not parse UTC date correctly"); + } + + t.parse3339("1980-05-23T09:50:50.123Z"); + if (t.allDay || t.year != 1980 || t.month != 4 || t.monthDay != 23 || t.hour != 9 + || t.minute != 50 || t.second != 50 || t.gmtoff != 0) { + fail("Did not parse UTC date correctly"); + } + + // the time should be normalized to UTC + t.parse3339("1980-05-23T09:50:50-01:05"); + if (t.allDay || t.year != 1980 || t.month != 4 || t.monthDay != 23 || t.hour != 10 + || t.minute != 55 || t.second != 50 || t.gmtoff != 0) { + fail("Did not parse timezone-offset date correctly"); + } + + // the time should be normalized to UTC + t.parse3339("1980-05-23T09:50:50.123-01:05"); + if (t.allDay || t.year != 1980 || t.month != 4 || t.monthDay != 23 || t.hour != 10 + || t.minute != 55 || t.second != 50 || t.gmtoff != 0) { + fail("Did not parse timezone-offset date correctly"); + } + + try { + t.parse3339("1980"); + fail("Did not throw error on truncated input length"); + } catch (TimeFormatException e) { + // successful + } + + try { + t.parse3339("1980-05-23T09:50:50.123+"); + fail("Did not throw error on truncated timezone offset"); + } catch (TimeFormatException e1) { + // successful + } + + try { + t.parse3339("1980-05-23T09:50:50.123+05:0"); + fail("Did not throw error on truncated timezone offset"); + } catch (TimeFormatException e1) { + // successful + } + } + + @SmallTest + public void testSet0() { + Time t = new Time(Time.TIMEZONE_UTC); + + t.set(1000L); + assertEquals(1970, t.year); + assertEquals(1, t.second); + + t.set(2000L); + assertEquals(2, t.second); + assertEquals(0, t.minute); + + t.set(1000L * 60); + assertEquals(1, t.minute); + assertEquals(0, t.hour); + + t.set(1000L * 60 * 60); + assertEquals(1, t.hour); + assertEquals(1, t.monthDay); + + t.set((1000L * 60 * 60 * 24) + 1000L); + assertEquals(2, t.monthDay); + assertEquals(1970, t.year); + } + + @SmallTest + public void testSet1() { + Time t = new Time(Time.TIMEZONE_UTC); + t.set(1, 2, 2021); + assertEquals(1, t.monthDay); + assertEquals(2, t.month); + assertEquals(2021, t.year); + } + + @SmallTest + public void testSet2() { + Time t = new Time(Time.TIMEZONE_UTC); + t.set(1, 2, 3, 4, 5, 2021); + assertEquals(1, t.second); + assertEquals(2, t.minute); + assertEquals(3, t.hour); + assertEquals(4, t.monthDay); + assertEquals(5, t.month); + assertEquals(2021, t.year); + } + + @SmallTest + public void testSet3() { + Time t = new Time(Time.TIMEZONE_UTC); + t.set(1, 2, 3, 4, 5, 2021); + Time t2 = new Time(); + t2.set(t); + assertEquals(Time.TIMEZONE_UTC, t2.timezone); + assertEquals(1, t2.second); + assertEquals(2, t2.minute); + assertEquals(3, t2.hour); + assertEquals(4, t2.monthDay); + assertEquals(5, t2.month); + assertEquals(2021, t2.year); + } + + @SmallTest + public void testSetToNow() { + Time t = new Time(Time.TIMEZONE_UTC); + t.setToNow(); + long ms = t.toMillis(true); + long now = System.currentTimeMillis(); + // ensure millis returned are within 1 second of when they were set + assertTrue(ms < now && ms > (now - 1000)); + } + + @SmallTest + public void testGetWeekNumber() { + Time t = new Time(Time.TIMEZONE_UTC); + t.set(1000L); + assertEquals(1, t.getWeekNumber()); + t.set(1, 1, 2020); + assertEquals(5, t.getWeekNumber()); + } + + private static class DateTest { + public int year1; + public int month1; + public int day1; + public int hour1; + public int minute1; + public int dst1; + + public int offset; + + public int year2; + public int month2; + public int day2; + public int hour2; + public int minute2; + public int dst2; + + public DateTest(int year1, int month1, int day1, int hour1, int minute1, + int offset, int year2, int month2, int day2, int hour2, int minute2) { + this.year1 = year1; + this.month1 = month1; + this.day1 = day1; + this.hour1 = hour1; + this.minute1 = minute1; + this.dst1 = -1; + this.offset = offset; + this.year2 = year2; + this.month2 = month2; + this.day2 = day2; + this.hour2 = hour2; + this.minute2 = minute2; + this.dst2 = -1; + } + + public DateTest(int year1, int month1, int day1, int hour1, int minute1, int dst1, + int offset, int year2, int month2, int day2, int hour2, int minute2, + int dst2) { + this.year1 = year1; + this.month1 = month1; + this.day1 = day1; + this.hour1 = hour1; + this.minute1 = minute1; + this.dst1 = dst1; + this.offset = offset; + this.year2 = year2; + this.month2 = month2; + this.day2 = day2; + this.hour2 = hour2; + this.minute2 = minute2; + this.dst2 = dst2; + } + + public boolean equals(Time time) { + return time.year == year2 && time.month == month2 && time.monthDay == day2 + && time.hour == hour2 && time.minute == minute2; + } + + public boolean equalsWithDst(Time time) { + return time.year == year2 && time.month == month2 && time.monthDay == day2 + && time.hour == hour2 && time.minute == minute2 && time.isDst == dst2; + } + } + + /* These tests assume that DST changes on Nov 4, 2007 at 2am (to 1am). */ + + // The "offset" field in "dayTests" represents days. + // Use normalize(true) with these tests to change the date by 1 day. + // Note: the month numbers are 0-relative, so Jan=0, Feb=1,...Dec=11 + private DateTest[] dayTests = { + // Nov 4, 12am + 0 day = Nov 4, 12am + new DateTest(2007, 10, 4, 0, 0, 0, 2007, 10, 4, 0, 0), + // Nov 5, 12am + 0 day = Nov 5, 12am + new DateTest(2007, 10, 5, 0, 0, 0, 2007, 10, 5, 0, 0), + // Nov 3, 12am + 1 day = Nov 4, 12am + new DateTest(2007, 10, 3, 0, 0, 1, 2007, 10, 4, 0, 0), + // Nov 4, 12am + 1 day = Nov 5, 12am + new DateTest(2007, 10, 4, 0, 0, 1, 2007, 10, 5, 0, 0), + // Nov 5, 12am + 1 day = Nov 6, 12am + new DateTest(2007, 10, 5, 0, 0, 1, 2007, 10, 6, 0, 0), + // Nov 3, 1am + 1 day = Nov 4, 1am + new DateTest(2007, 10, 3, 1, 0, 1, 2007, 10, 4, 1, 0), + // Nov 4, 1am + 1 day = Nov 5, 1am + new DateTest(2007, 10, 4, 1, 0, 1, 2007, 10, 5, 1, 0), + // Nov 5, 1am + 1 day = Nov 6, 1am + new DateTest(2007, 10, 5, 1, 0, 1, 2007, 10, 6, 1, 0), + // Nov 3, 2am + 1 day = Nov 4, 2am + new DateTest(2007, 10, 3, 2, 0, 1, 2007, 10, 4, 2, 0), + // Nov 4, 2am + 1 day = Nov 5, 2am + new DateTest(2007, 10, 4, 2, 0, 1, 2007, 10, 5, 2, 0), + // Nov 5, 2am + 1 day = Nov 6, 2am + new DateTest(2007, 10, 5, 2, 0, 1, 2007, 10, 6, 2, 0), + }; + + // The "offset" field in "minuteTests" represents minutes. + // Use normalize(false) with these tests. + // Note: the month numbers are 0-relative, so Jan=0, Feb=1,...Dec=11 + private DateTest[] minuteTests = { + // Nov 4, 12am + 0 minutes = Nov 4, 12am + new DateTest(2007, 10, 4, 0, 0, 0, 2007, 10, 4, 0, 0), + // Nov 5, 12am + 0 minutes = Nov 5, 12am + new DateTest(2007, 10, 5, 0, 0, 0, 2007, 10, 5, 0, 0), + // Nov 3, 12am + 60 minutes = Nov 3, 1am + new DateTest(2007, 10, 3, 0, 0, 60, 2007, 10, 3, 1, 0), + // Nov 4, 12am + 60 minutes = Nov 4, 1am + new DateTest(2007, 10, 4, 0, 0, 60, 2007, 10, 4, 1, 0), + // Nov 5, 12am + 60 minutes = Nov 5, 1am + new DateTest(2007, 10, 5, 0, 0, 60, 2007, 10, 5, 1, 0), + // Nov 3, 1am + 60 minutes = Nov 3, 2am + new DateTest(2007, 10, 3, 1, 0, 60, 2007, 10, 3, 2, 0), + // Nov 4, 1am (PDT) + 30 minutes = Nov 4, 1:30am (PDT) + new DateTest(2007, 10, 4, 1, 0, 1, 30, 2007, 10, 4, 1, 30, 1), + // Nov 4, 1am (PDT) + 60 minutes = Nov 4, 1am (PST) + new DateTest(2007, 10, 4, 1, 0, 1, 60, 2007, 10, 4, 1, 0, 0), + // Nov 4, 1:30am (PDT) + 15 minutes = Nov 4, 1:45am (PDT) + new DateTest(2007, 10, 4, 1, 30, 1, 15, 2007, 10, 4, 1, 45, 1), + // Nov 4, 1:30am (PDT) + 30 minutes = Nov 4, 1:00am (PST) + new DateTest(2007, 10, 4, 1, 30, 1, 30, 2007, 10, 4, 1, 0, 0), + // Nov 4, 1:30am (PDT) + 60 minutes = Nov 4, 1:30am (PST) + new DateTest(2007, 10, 4, 1, 30, 1, 60, 2007, 10, 4, 1, 30, 0), + // Nov 4, 1:30am (PST) + 15 minutes = Nov 4, 1:45am (PST) + new DateTest(2007, 10, 4, 1, 30, 0, 15, 2007, 10, 4, 1, 45, 0), + // Nov 4, 1:30am (PST) + 30 minutes = Nov 4, 2:00am (PST) + new DateTest(2007, 10, 4, 1, 30, 0, 30, 2007, 10, 4, 2, 0, 0), + // Nov 5, 1am + 60 minutes = Nov 5, 2am + new DateTest(2007, 10, 5, 1, 0, 60, 2007, 10, 5, 2, 0), + // Nov 3, 2am + 60 minutes = Nov 3, 3am + new DateTest(2007, 10, 3, 2, 0, 60, 2007, 10, 3, 3, 0), + // Nov 4, 2am + 30 minutes = Nov 4, 2:30am + new DateTest(2007, 10, 4, 2, 0, 30, 2007, 10, 4, 2, 30), + // Nov 4, 2am + 60 minutes = Nov 4, 3am + new DateTest(2007, 10, 4, 2, 0, 60, 2007, 10, 4, 3, 0), + // Nov 5, 2am + 60 minutes = Nov 5, 3am + new DateTest(2007, 10, 5, 2, 0, 60, 2007, 10, 5, 3, 0), + }; + + @SmallTest + public void testNormalize0() { + Time t = new Time(Time.TIMEZONE_UTC); + t.parse("20060432T010203"); + assertEquals(1146531723000L, t.normalize(false)); + } + + @MediumTest + public void testNormalize1() { + Time local = new Time("America/Los_Angeles"); + + int len = dayTests.length; + for (int index = 0; index < len; index++) { + DateTest test = dayTests[index]; + local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1); + // call normalize() to make sure that isDst is set + local.normalize(false); + local.monthDay += test.offset; + local.normalize(true); + if (!test.equals(local)) { + String expectedTime = String.format("%d-%02d-%02d %02d:%02d", + test.year2, test.month2, test.day2, test.hour2, test.minute2); + String actualTime = String.format("%d-%02d-%02d %02d:%02d", + local.year, local.month, local.monthDay, local.hour, local.minute); + fail("Expected: " + expectedTime + "; Actual: " + actualTime); + } + + local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1); + // call normalize() to make sure that isDst is set + local.normalize(false); + local.monthDay += test.offset; + long millis = local.toMillis(true); + local.set(millis); + if (!test.equals(local)) { + String expectedTime = String.format("%d-%02d-%02d %02d:%02d", + test.year2, test.month2, test.day2, test.hour2, test.minute2); + String actualTime = String.format("%d-%02d-%02d %02d:%02d", + local.year, local.month, local.monthDay, local.hour, local.minute); + fail("Expected: " + expectedTime + "; Actual: " + actualTime); + } + } + + len = minuteTests.length; + for (int index = 0; index < len; index++) { + DateTest test = minuteTests[index]; + local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1); + local.isDst = test.dst1; + // call normalize() to make sure that isDst is set + local.normalize(false); + if (test.dst2 == -1) test.dst2 = local.isDst; + local.minute += test.offset; + local.normalize(false); + if (!test.equalsWithDst(local)) { + String expectedTime = String.format("%d-%02d-%02d %02d:%02d isDst: %d", + test.year2, test.month2, test.day2, test.hour2, test.minute2, test.dst2); + String actualTime = String.format("%d-%02d-%02d %02d:%02d isDst: %d", + local.year, local.month, local.monthDay, local.hour, local.minute, + local.isDst); + fail("Expected: " + expectedTime + "; Actual: " + actualTime); + } + + local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1); + local.isDst = test.dst1; + // call normalize() to make sure that isDst is set + local.normalize(false); + if (test.dst2 == -1) test.dst2 = local.isDst; + local.minute += test.offset; + long millis = local.toMillis(false); + local.set(millis); + if (!test.equalsWithDst(local)) { + String expectedTime = String.format("%d-%02d-%02d %02d:%02d isDst: %d", + test.year2, test.month2, test.day2, test.hour2, test.minute2, test.dst2); + String actualTime = String.format("%d-%02d-%02d %02d:%02d isDst: %d", + local.year, local.month, local.monthDay, local.hour, local.minute, + local.isDst); + fail("Expected: " + expectedTime + "; Actual: " + actualTime); + } + } + } + + // Timezones that cover the world. + // Some GMT offsets occur more than once in case some cities decide to change their GMT offset. + private static final String[] mTimeZones = { + "Pacific/Kiritimati", + "Pacific/Enderbury", + "Pacific/Fiji", + "Antarctica/South_Pole", + "Pacific/Norfolk", + "Pacific/Ponape", + "Asia/Magadan", + "Australia/Lord_Howe", + "Australia/Sydney", + "Australia/Adelaide", + "Asia/Tokyo", + "Asia/Seoul", + "Asia/Taipei", + "Asia/Singapore", + "Asia/Hong_Kong", + "Asia/Saigon", + "Asia/Bangkok", + "Indian/Cocos", + "Asia/Rangoon", + "Asia/Omsk", + "Antarctica/Mawson", + "Asia/Colombo", + "Asia/Calcutta", + "Asia/Oral", + "Asia/Kabul", + "Asia/Dubai", + "Asia/Tehran", + "Europe/Moscow", + "Asia/Baghdad", + "Africa/Mogadishu", + "Europe/Athens", + "Africa/Cairo", + "Europe/Rome", + "Europe/Berlin", + "Europe/Amsterdam", + "Africa/Tunis", + "Europe/London", + "Europe/Dublin", + "Atlantic/St_Helena", + "Africa/Monrovia", + "Africa/Accra", + "Atlantic/Azores", + "Atlantic/South_Georgia", + "America/Noronha", + "America/Sao_Paulo", + "America/Cayenne", + "America/St_Johns", + "America/Puerto_Rico", + "America/Aruba", + "America/New_York", + "America/Chicago", + "America/Denver", + "America/Los_Angeles", + "America/Anchorage", + "Pacific/Marquesas", + "America/Adak", + "Pacific/Honolulu", + "Pacific/Midway", + }; + + @MediumTest + public void testGetJulianDay() { + Time time = new Time(); + + // for a random day in the year 2020 and for a random timezone, get the Julian day for 12am + // and then check that if we change the time we get the same Julian day. + int monthDay = (int) (Math.random() * 365) + 1; + int zoneIndex = (int) (Math.random() * mTimeZones.length); + time.set(0, 0, 0, monthDay, 0, 2020); + time.timezone = mTimeZones[zoneIndex]; + long millis = time.normalize(true); + + int julianDay = Time.getJulianDay(millis, time.gmtoff); + + // change the time during the day and check that we get the same Julian day. + for (int hour = 0; hour < 24; hour++) { + for (int minute = 0; minute < 60; minute += 15) { + time.set(0, minute, hour, monthDay, 0, 2020); + millis = time.normalize(true); + int day = Time.getJulianDay(millis, time.gmtoff); + assertEquals(day, julianDay); + } + } + } + + @MediumTest + public void testSetJulianDay() { + Time time = new Time(); + + // for each day in the year 2020, pick a random timezone, and verify that we can + // set the Julian day correctly. + for (int monthDay = 1; monthDay <= 366; monthDay++) { + int zoneIndex = (int) (Math.random() * mTimeZones.length); + // leave the "month" as zero because we are changing the "monthDay" from 1 to 366. + // the call to normalize() will then change the "month" (but we don't really care). + time.set(0, 0, 0, monthDay, 0, 2020); + time.timezone = mTimeZones[zoneIndex]; + long millis = time.normalize(true); + int julianDay = Time.getJulianDay(millis, time.gmtoff); + + time.setJulianDay(julianDay); + + // some places change daylight saving time at 12am and so there is no 12am on some days + // in some timezones - in those cases, the time is set to 1am. + // some examples: Africa/Cairo, America/Sao_Paulo, Atlantic/Azores + assertTrue(time.hour == 0 || time.hour == 1); + assertEquals(0, time.minute); + assertEquals(0, time.second); + + millis = time.toMillis(false); + int day = Time.getJulianDay(millis, time.gmtoff); + assertEquals(day, julianDay); + } + } +} |