aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVarun Shah <varunshah@google.com>2020-08-14 12:17:17 -0700
committerVarun Shah <varunshah@google.com>2020-08-14 15:58:28 -0700
commitb55d9bce898b9742da80bff0ab1362ffd010b9fd (patch)
tree86b4407700e8231d7fb9b257abbf43f346d9e908
parent6e29ad1dfe9b353af1c41a30101673bf01d7a144 (diff)
downloadcalendar-b55d9bce898b9742da80bff0ab1362ffd010b9fd.tar.gz
Introduce Time class to help with migration.
This class will help migrate from android.text.format.Time. Currently, it has the same API so that no friction is caused in the inital transition. Unit tests have been added to ensure future changes don't cause regressions when transitioning to a more robust Time API. Bug: 159019762 Test: atest TimeTest Test: atest CalendarCommonTests Test: atest CalendarProviderTests Change-Id: Ic640712cd48f0f4f7abfd8111bdac7d9a430574d
-rw-r--r--src/com/android/calendarcommon2/EventRecurrence.java1
-rw-r--r--src/com/android/calendarcommon2/RecurrenceProcessor.java1
-rw-r--r--src/com/android/calendarcommon2/RecurrenceSet.java1
-rw-r--r--src/com/android/calendarcommon2/Time.java218
-rw-r--r--tests/src/com/android/calendarcommon2/RRuleTest.java1
-rw-r--r--tests/src/com/android/calendarcommon2/RecurrenceProcessorTest.java1
-rw-r--r--tests/src/com/android/calendarcommon2/TimeTest.java617
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);
+ }
+ }
+}