TimeTest improvements linked to Time class fix
Improve TimeTest to provide extra coverage and adjust for a
bug fix.
1) testGetJulianDay() has been extended to test outside of the
int32 seconds range. Doing this revealed a bug for times before
1970 (i.e. negative seconds since beginning of Unix epoch) and
this has been fixed here.
2) testIsEpoch() has been extended to demonstrate the existing
behavior. The isEpoch() method assumes the callers wants to know
whether the supplied Time is on 1970-01-01 in UTC. This seems
unusual but it has not been changed as the original intent is
unclear and the code is deprecated. Note that the fix for (1)
demonstrated a bug in the test (because it was using a negative
seconds value) so this has been fixed.
Bug: 16550209
Test: atest android.text.format.cts.TimeTest
Change-Id: Id84c054fd38cf085e91a69e170298e409f06852f
diff --git a/tests/tests/text/src/android/text/format/cts/TimeTest.java b/tests/tests/text/src/android/text/format/cts/TimeTest.java
index ad5c3bf..aecf05b 100644
--- a/tests/tests/text/src/android/text/format/cts/TimeTest.java
+++ b/tests/tests/text/src/android/text/format/cts/TimeTest.java
@@ -37,10 +37,14 @@
 
 import java.time.Duration;
 import java.time.Instant;
+import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.time.Month;
 import java.time.ZoneId;
+import java.time.ZoneOffset;
 import java.time.temporal.ChronoUnit;
+import java.time.temporal.JulianFields;
 import java.time.zone.ZoneOffsetTransition;
 import java.time.zone.ZoneRules;
 import java.util.ArrayList;
@@ -221,11 +225,44 @@
 
     @Test
     public void testIsEpoch() {
-        Time time = new Time();
+        // Create a Time that uses UTC to provide a behavior baseline.
+        Time time = new Time(Time.TIMEZONE_UTC);
+
+        // Time is initialized to 1970-01-01 00:00:00
         assertTrue(Time.isEpoch(time));
-        time.set(1, 2, 1970);
+
+        // 1970-01-01 23:59:59
+        checkIsEpochResult(time, 1970, 0 /* Jan */, 1, 23, 59, 59, true);
+
+        // 1970-01-02 00:00:00
+        checkIsEpochResult(time, 1970, 0 /* Jan */, 2, 0, 0, 0, false);
+
+        // 1969-12-31 23:59:59
+        checkIsEpochResult(time, 1969, 11 /* Dec */, 31, 23, 59, 59, false);
+
+        // Now demonstrate that the isEpoch() method just checks against the Julian day
+        // calculated for UTC. America/Los_Angeles is UTC-8 so all times have to be adjusted
+        // by 8 hours.
+        time.timezone = "America/Los_Angeles";
+
+        // 1969-12-31 15:59:59 == 1969-12-31 23:59:59 in UTC
+        checkIsEpochResult(time, 1969, 11 /* Dec */, 31, 15, 59, 59, false);
+
+        // 1969-12-31 16:00:00 == 1970-01-01 00:00:00 in UTC
+        checkIsEpochResult(time, 1969, 11 /* Dec */, 31, 16, 0, 0, true);
+
+        // 1970-01-01 15:59:59 == 1970-01-01 23:59:59 in UTC
+        checkIsEpochResult(time, 1970, 0 /* Jan */, 1, 15, 59, 59, true);
+
+        // 1970-01-01 16:00:00 == 1970-01-02 00:00:00 in UTC
+        checkIsEpochResult(time, 1970, 0 /* Jan */, 1, 16, 0, 0, false);
+    }
+
+    private void checkIsEpochResult(Time time, int year, int month, int monthDay, int hour,
+            int minute, int second, boolean expectedIsEpoch) {
+        time.set(second, minute, hour, monthDay, month, year);
         time.normalize(false);
-        assertFalse(Time.isEpoch(time));
+        assertEquals(expectedIsEpoch, Time.isEpoch(time));
     }
 
     @Test
@@ -1913,50 +1950,71 @@
         "Pacific/Midway",
     };
 
+    /**
+     * This test uses java.time classes to construct test times so that it can test various years
+     * including those outside of the int32 seconds range.
+     */
     @Test
     public void testGetJulianDay() {
-        Time time = new Time();
+        int[] years = { 2008, 1900, 1969, 2100 };
+        for (int year : years) {
+            for (String timeZone : mTimeZones) {
+                checkGetJulianDayForYearAndTimeZone(year, timeZone);
+            }
+        }
+    }
 
-        // For every 15th day of 2008, and for each of the timezones listed above,
-        // get the Julian day for 12am and then check that if we change the time we get the
-        // same Julian day. Note that one of the many problems with the Time class
-        // is its lack of error handling. If we accidentally hit a time that doesn't
-        // exist (because it was skipped by a daylight savings transition), rather than
-        // an error, you'll silently get 1970-01-01. We should @deprecate Time.
-        for (int monthDay = 1; monthDay <= 366; monthDay += 15) {
-            for (int zoneIndex = 0; zoneIndex < mTimeZones.length; zoneIndex++) {
-                // We 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, 12, monthDay, 0, 2008);
-                time.timezone = mTimeZones[zoneIndex];
-                long millis = time.normalize(true);
-                if (zoneIndex == 0) {
-                    Log.i(TAG, time.format("%B %d, %Y"));
-                }
+    private static void checkGetJulianDayForYearAndTimeZone(int year, String timeZone) {
+        final LocalTime midday = LocalTime.of(12, 0);
 
-                // This is the Julian day for 12pm for this day of the year
-                int julianDay = Time.getJulianDay(millis, time.gmtoff);
+        // For every 15th day of the year get the Julian day for 12pm and then check that if we
+        // change the time we get the same Julian day.
+        final LocalDate startDate = LocalDate.of(year, Month.JANUARY, 1);
+        final LocalDate stopDate = startDate.plusYears(1);
+        for (LocalDate testDate = startDate; testDate.isBefore(stopDate);
+                testDate = testDate.plusDays(15)) {
 
-                // 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, 2008);
-                        millis = time.normalize(true);
-                        if (millis == -1) {
-                            // millis == -1 means the wall time does not exist in the chosen
-                            // timezone due to a DST change. We cannot calculate a JulianDay for -1.
-                            continue;
-                        }
+            LocalDateTime middayLocalDateTime = LocalDateTime.of(testDate, midday);
+            ZoneOffset middayOffset = ZoneId.of(timeZone).getRules().getOffset(middayLocalDateTime);
+            Instant middayInstant = middayLocalDateTime.toInstant(middayOffset);
 
-                        int day = Time.getJulianDay(millis, time.gmtoff);
-                        assertEquals("Julian day: " + day + " at time "
-                                + time.hour + ":" + time.minute
-                                + " != today's Julian day: " + julianDay
-                                + " timezone: " + time.timezone, day, julianDay);
-                    }
-                }
+            // Record the Julian day for the date/time given. Since we want to know the local
+            // calendar date we have to provide the time zone's UTC offset too.
+            int middayJulianDay =
+                    Time.getJulianDay(middayInstant.toEpochMilli(), middayOffset.getTotalSeconds());
+
+            // Check Time.getJulianDay() agrees with java.time's Julian day calculations.
+            assertEquals(middayJulianDay, JulianFields.JULIAN_DAY.getFrom(middayLocalDateTime));
+
+            checkGetJulianDayVariousTimes(timeZone, testDate);
+        }
+    }
+
+    private static void checkGetJulianDayVariousTimes(String timeZone, LocalDate testDate) {
+        long expectedJulianDay = JulianFields.JULIAN_DAY.getFrom(testDate);
+
+        // Change the time during the day and check that we get the same Julian day as the one
+        // for midday.
+        for (int hour = 0; hour < 24; hour++) {
+            for (int minute = 0; minute < 60; minute += 15) {
+                LocalTime localTime = LocalTime.of(hour, minute);
+                LocalDateTime localDateTime = LocalDateTime.of(testDate, localTime);
+                ZoneOffset localDateTimeOffset =
+                        ZoneId.of(timeZone).getRules().getOffset(localDateTime);
+                Instant instant = localDateTime.toInstant(localDateTimeOffset);
+                long millis = instant.toEpochMilli();
+
+                // Find the Julian day for the date/time by supplying the offset. Since we want
+                // to know the local calendar date we have to provide the UTC offset too.
+                int julianDay = Time.getJulianDay(millis, localDateTimeOffset.getTotalSeconds());
+
+                assertEquals("Julian day: " + julianDay + " at time "
+                                + hour + ":" + minute
+                                + " != today's Julian day: " + expectedJulianDay
+                                + " millis: " + millis
+                                + " localDatetime: " + localDateTime
+                                + " timeZone: " + timeZone,
+                        julianDay, expectedJulianDay);
             }
         }
     }