Bug 1253338 - Add DateUtil.getTimezoneOffsetInMinutes*. r=ahunt, a=ritu
authorMichael Comella <michael.l.comella@gmail.com>
Mon, 09 May 2016 11:41:02 -0700
changeset 332970 1645797a1315ad323dd3fe08e11ccb350350a1ce
parent 332969 fdf13c9edc864bebb7258816b4033af8b5dccc48
child 332971 f3139434149bf0a0c11bc6bffa5c0b3a1c5bc104
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersahunt, ritu
bugs1253338
milestone48.0a2
Bug 1253338 - Add DateUtil.getTimezoneOffsetInMinutes*. r=ahunt, a=ritu I initially wrote the "ForGivenDate" variant for testing purposes - so I could verify the daylight savings time APIs worked the way I expected them to. However, I ended up using it in my next patch and leaving in the generic "for current time" variant because it seems useful. MozReview-Commit-ID: 4hNGD4uDuOj
mobile/android/base/java/org/mozilla/gecko/util/DateUtil.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/util/TestDateUtil.java
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/util/DateUtil.java
@@ -0,0 +1,55 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+package org.mozilla.gecko.util;
+
+import android.support.annotation.NonNull;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Utilities to help with manipulating Java's dates and calendars.
+ */
+public class DateUtil {
+    private DateUtil() {}
+
+    /**
+     * @param date the date to convert to HTTP format
+     * @return the date as specified in rfc 1123, e.g. "Tue, 01 Feb 2011 14:00:00 GMT"
+     */
+    public static String getDateInHTTPFormat(@NonNull final Date date) {
+        final DateFormat df = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z", Locale.US);
+        df.setTimeZone(TimeZone.getTimeZone("GMT"));
+        return df.format(date);
+    }
+
+    /**
+     * Returns the timezone offset for the current date in minutes. See
+     * {@link #getTimezoneOffsetInMinutesForGivenDate(Calendar)} for more details.
+     */
+    public static int getTimezoneOffsetInMinutes(@NonNull final TimeZone timezone) {
+        return getTimezoneOffsetInMinutesForGivenDate(Calendar.getInstance(timezone));
+    }
+
+    /**
+     * Returns the time zone offset for the given date in minutes. The date makes a difference due to daylight
+     * savings time in some regions. We return minutes because we can accurately represent time zones that are
+     * offset by non-integer hour values, e.g. parts of New Zealand at UTC+12:45.
+     *
+     * @param calendar A calendar with the appropriate time zone & date already set.
+     */
+    public static int getTimezoneOffsetInMinutesForGivenDate(@NonNull final Calendar calendar) {
+        // via Date.getTimezoneOffset deprecated docs (note: it had incorrect order of operations).
+        // Also, we cast to int because we should never overflow here - the max should be GMT+14 = 840.
+        return (int) TimeUnit.MILLISECONDS.toMinutes(calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET));
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/util/TestDateUtil.java
@@ -0,0 +1,89 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+package org.mozilla.gecko.util;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mozilla.gecko.background.testhelpers.TestRunner;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Unit tests for date utilities.
+ */
+@RunWith(TestRunner.class)
+public class TestDateUtil {
+    @Test
+    public void testGetDateInHTTPFormatGMT() {
+        final TimeZone gmt = TimeZone.getTimeZone("GMT");
+        final GregorianCalendar calendar = new GregorianCalendar(gmt, Locale.US);
+        calendar.set(2011, Calendar.FEBRUARY, 1, 14, 0, 0);
+        final String expectedDate = "Tue, 01 Feb 2011 14:00:00 GMT";
+
+        final String actualDate = DateUtil.getDateInHTTPFormat(calendar.getTime());
+        assertEquals("Returned date is expected", expectedDate, actualDate);
+    }
+
+    @Test
+    public void testGetDateInHTTPFormatNonGMT() {
+        final TimeZone kst = TimeZone.getTimeZone("Asia/Seoul"); // no daylight savings time.
+        final GregorianCalendar calendar = new GregorianCalendar(kst, Locale.US);
+        calendar.set(2011, Calendar.FEBRUARY, 1, 14, 0, 0);
+        final String expectedDate = "Tue, 01 Feb 2011 05:00:00 GMT";
+
+        final String actualDate = DateUtil.getDateInHTTPFormat(calendar.getTime());
+        assertEquals("Returned date is expected", expectedDate, actualDate);
+    }
+
+    @Test
+    public void testGetTimezoneOffsetInMinutes() {
+        assertEquals("GMT has no offset", 0, DateUtil.getTimezoneOffsetInMinutes(TimeZone.getTimeZone("GMT")));
+
+        // We use custom timezones because they don't have daylight savings time.
+        assertEquals("Offset for GMT-8 is correct",
+                -480, DateUtil.getTimezoneOffsetInMinutes(TimeZone.getTimeZone("GMT-8")));
+        assertEquals("Offset for GMT+12:45 is correct",
+                765, DateUtil.getTimezoneOffsetInMinutes(TimeZone.getTimeZone("GMT+12:45")));
+
+        // We use a non-custom timezone without DST.
+        assertEquals("Offset for KST is correct",
+                540, DateUtil.getTimezoneOffsetInMinutes(TimeZone.getTimeZone("Asia/Seoul")));
+    }
+
+    @Test
+    public void testGetTimezoneOffsetInMinutesForGivenDateNoDaylightSavingsTime() {
+        final TimeZone kst = TimeZone.getTimeZone("Asia/Seoul");
+        final Calendar[] calendars =
+                new Calendar[] { getCalendarForMonth(Calendar.DECEMBER), getCalendarForMonth(Calendar.AUGUST) };
+        for (final Calendar cal : calendars) {
+            cal.setTimeZone(kst);
+            assertEquals("Offset for KST does not change with daylight savings time",
+                    540, DateUtil.getTimezoneOffsetInMinutesForGivenDate(cal));
+        }
+    }
+
+    @Test
+    public void testGetTimezoneOffsetInMinutesForGivenDateDaylightSavingsTime() {
+        final TimeZone pacificTimeZone = TimeZone.getTimeZone("America/Los_Angeles");
+        final Calendar pstCalendar = getCalendarForMonth(Calendar.DECEMBER);
+        final Calendar pdtCalendar = getCalendarForMonth(Calendar.AUGUST);
+        pstCalendar.setTimeZone(pacificTimeZone);
+        pdtCalendar.setTimeZone(pacificTimeZone);
+        assertEquals("Offset for PST is correct", -480, DateUtil.getTimezoneOffsetInMinutesForGivenDate(pstCalendar));
+        assertEquals("Offset for PDT is correct", -420, DateUtil.getTimezoneOffsetInMinutesForGivenDate(pdtCalendar));
+
+    }
+
+    private Calendar getCalendarForMonth(final int month) {
+        return new GregorianCalendar(2000, month, 1);
+    }
+}