diff --git a/CHANGELOG.md b/CHANGELOG.md index e21cd4365..381a5fd70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +### 1.7.8 (September 30, 2017) + +* Fix bug that caused reminders to show repeatedly on DST changes + ### 1.7.6 (July 18, 2017) * Fix bug that caused widgets not to render sometimes diff --git a/gradle.properties b/gradle.properties index 71ae360f9..bc47317b6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ -VERSION_CODE = 33 -VERSION_NAME = 1.7.6 +VERSION_CODE = 35 +VERSION_NAME = 1.7.8 MIN_SDK_VERSION = 19 TARGET_SDK_VERSION = 25 diff --git a/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.java index 2771f3500..ff52f47db 100644 --- a/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.java +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.java @@ -54,8 +54,7 @@ public abstract class DateUtils public static long applyTimezone(long localTimestamp) { TimeZone tz = getTimezone(); - long now = new Date(localTimestamp).getTime(); - return now - tz.getOffset(now); + return localTimestamp - tz.getOffset(localTimestamp - tz.getOffset(localTimestamp)); } public static String formatHeaderDate(GregorianCalendar day) @@ -188,8 +187,7 @@ public abstract class DateUtils public static long removeTimezone(long timestamp) { TimeZone tz = getTimezone(); - long now = new Date(timestamp).getTime(); - return now + tz.getOffset(now); + return timestamp + tz.getOffset(timestamp); } public static void setFixedLocalTime(Long timestamp) diff --git a/uhabits-core/src/test/java/org/isoron/uhabits/core/utils/DateUtilsTest.java b/uhabits-core/src/test/java/org/isoron/uhabits/core/utils/DateUtilsTest.java index ca6a3e73a..bbf0e5f94 100644 --- a/uhabits-core/src/test/java/org/isoron/uhabits/core/utils/DateUtilsTest.java +++ b/uhabits-core/src/test/java/org/isoron/uhabits/core/utils/DateUtilsTest.java @@ -26,8 +26,11 @@ import org.junit.*; import java.util.*; import static java.util.Calendar.*; +import static junit.framework.Assert.assertEquals; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.*; +import static org.isoron.uhabits.core.utils.DateUtils.applyTimezone; +import static org.isoron.uhabits.core.utils.DateUtils.removeTimezone; public class DateUtilsTest extends BaseUnitTest { @@ -154,4 +157,86 @@ public class DateUtilsTest extends BaseUnitTest assertThat(DateUtils.millisecondsUntilTomorrow(), equalTo(14400000L)); } + + @Test + public void test_applyTimezone() + { + DateUtils.setFixedTimeZone(TimeZone.getTimeZone("Australia/Sydney")); + assertEquals(applyTimezone(timestamp(2017, JULY, 30, 18, 0)), (timestamp(2017, JULY, 30, 8, 0))); + assertEquals(applyTimezone(timestamp(2017, SEPTEMBER, 30, 0, 0)), (timestamp(2017, SEPTEMBER, 29, 14, 0))); + assertEquals(applyTimezone(timestamp(2017, SEPTEMBER, 30, 10, 0)), (timestamp(2017, SEPTEMBER, 30, 0, 0))); + assertEquals(applyTimezone(timestamp(2017, SEPTEMBER, 30, 11, 0)), (timestamp(2017, SEPTEMBER, 30, 1, 0))); + assertEquals(applyTimezone(timestamp(2017, SEPTEMBER, 30, 12, 0)), (timestamp(2017, SEPTEMBER, 30, 2, 0))); + assertEquals(applyTimezone(timestamp(2017, SEPTEMBER, 30, 13, 0)), (timestamp(2017, SEPTEMBER, 30, 3, 0))); + assertEquals(applyTimezone(timestamp(2017, SEPTEMBER, 30, 22, 0)), (timestamp(2017, SEPTEMBER, 30, 12, 0))); + assertEquals(applyTimezone(timestamp(2017, SEPTEMBER, 30, 23, 0)), (timestamp(2017, SEPTEMBER, 30, 13, 0))); + assertEquals(applyTimezone(timestamp(2017, OCTOBER, 1, 0, 0)), (timestamp(2017, SEPTEMBER, 30, 14, 0))); + assertEquals(applyTimezone(timestamp(2017, OCTOBER, 1, 1, 0)), (timestamp(2017, SEPTEMBER, 30, 15, 0))); + assertEquals(applyTimezone(timestamp(2017, OCTOBER, 1, 1, 59)), (timestamp(2017, SEPTEMBER, 30, 15, 59))); + // DST begins + assertEquals(applyTimezone(timestamp(2017, OCTOBER, 1, 3, 0)), (timestamp(2017, SEPTEMBER, 30, 16, 0))); + assertEquals(applyTimezone(timestamp(2017, OCTOBER, 1, 4, 0)), (timestamp(2017, SEPTEMBER, 30, 17, 0))); + assertEquals(applyTimezone(timestamp(2017, OCTOBER, 1, 5, 0)), (timestamp(2017, SEPTEMBER, 30, 18, 0))); + assertEquals(applyTimezone(timestamp(2017, OCTOBER, 1, 11, 0)), (timestamp(2017, OCTOBER, 1, 0, 0))); + assertEquals(applyTimezone(timestamp(2017, OCTOBER, 1, 12, 0)), (timestamp(2017, OCTOBER, 1, 1, 0))); + assertEquals(applyTimezone(timestamp(2017, OCTOBER, 1, 13, 0)), (timestamp(2017, OCTOBER, 1, 2, 0))); + assertEquals(applyTimezone(timestamp(2017, OCTOBER, 1, 14, 0)), (timestamp(2017, OCTOBER, 1, 3, 0))); + assertEquals(applyTimezone(timestamp(2017, OCTOBER, 1, 15, 0)), (timestamp(2017, OCTOBER, 1, 4, 0))); + assertEquals(applyTimezone(timestamp(2017, OCTOBER, 1, 19, 0)), (timestamp(2017, OCTOBER, 1, 8, 0))); + assertEquals(applyTimezone(timestamp(2017, OCTOBER, 2, 19, 0)), (timestamp(2017, OCTOBER, 2, 8, 0))); + assertEquals(applyTimezone(timestamp(2017, NOVEMBER, 30, 19, 0)), (timestamp(2017, NOVEMBER, 30, 8, 0))); + assertEquals(applyTimezone(timestamp(2018, MARCH, 31, 0, 0)), (timestamp(2018, MARCH, 30, 13, 0))); + assertEquals(applyTimezone(timestamp(2018, MARCH, 31, 12, 0)), (timestamp(2018, MARCH, 31, 1, 0))); + assertEquals(applyTimezone(timestamp(2018, MARCH, 31, 18, 0)), (timestamp(2018, MARCH, 31, 7, 0))); + assertEquals(applyTimezone(timestamp(2018, APRIL, 1, 0, 0)), (timestamp(2018, MARCH, 31, 13, 0))); + assertEquals(applyTimezone(timestamp(2018, APRIL, 1, 1, 0)), (timestamp(2018, MARCH, 31, 14, 0))); + assertEquals(applyTimezone(timestamp(2018, APRIL, 1, 1, 59)), (timestamp(2018, MARCH, 31, 14, 59))); + // DST ends + assertEquals(applyTimezone(timestamp(2018, APRIL, 1, 2, 0)), (timestamp(2018, MARCH, 31, 16, 0))); + assertEquals(applyTimezone(timestamp(2018, APRIL, 1, 3, 0)), (timestamp(2018, MARCH, 31, 17, 0))); + assertEquals(applyTimezone(timestamp(2018, APRIL, 1, 4, 0)), (timestamp(2018, MARCH, 31, 18, 0))); + assertEquals(applyTimezone(timestamp(2018, APRIL, 1, 10, 0)), (timestamp(2018, APRIL, 1, 0, 0))); + assertEquals(applyTimezone(timestamp(2018, APRIL, 1, 18, 0)), (timestamp(2018, APRIL, 1, 8, 0))); + } + + @Test + public void test_removeTimezone() + { + DateUtils.setFixedTimeZone(TimeZone.getTimeZone("Australia/Sydney")); + assertEquals(removeTimezone(timestamp(2017, JULY, 30, 8, 0)), (timestamp(2017, JULY, 30, 18, 0))); + assertEquals(removeTimezone(timestamp(2017, SEPTEMBER, 29, 14, 0)), (timestamp(2017, SEPTEMBER, 30, 0, 0))); + assertEquals(removeTimezone(timestamp(2017, SEPTEMBER, 30, 0, 0)), (timestamp(2017, SEPTEMBER, 30, 10, 0))); + assertEquals(removeTimezone(timestamp(2017, SEPTEMBER, 30, 1, 0)), (timestamp(2017, SEPTEMBER, 30, 11, 0))); + assertEquals(removeTimezone(timestamp(2017, SEPTEMBER, 30, 2, 0)), (timestamp(2017, SEPTEMBER, 30, 12, 0))); + assertEquals(removeTimezone(timestamp(2017, SEPTEMBER, 30, 3, 0)), (timestamp(2017, SEPTEMBER, 30, 13, 0))); + assertEquals(removeTimezone(timestamp(2017, SEPTEMBER, 30, 12, 0)), (timestamp(2017, SEPTEMBER, 30, 22, 0))); + assertEquals(removeTimezone(timestamp(2017, SEPTEMBER, 30, 13, 0)), (timestamp(2017, SEPTEMBER, 30, 23, 0))); + assertEquals(removeTimezone(timestamp(2017, SEPTEMBER, 30, 14, 0)), (timestamp(2017, OCTOBER, 1, 0, 0))); + assertEquals(removeTimezone(timestamp(2017, SEPTEMBER, 30, 15, 0)), (timestamp(2017, OCTOBER, 1, 1, 0))); + assertEquals(removeTimezone(timestamp(2017, SEPTEMBER, 30, 15, 59)), (timestamp(2017, OCTOBER, 1, 1, 59))); + // DST begins + assertEquals(removeTimezone(timestamp(2017, SEPTEMBER, 30, 16, 0)), (timestamp(2017, OCTOBER, 1, 3, 0))); + assertEquals(removeTimezone(timestamp(2017, SEPTEMBER, 30, 17, 0)), (timestamp(2017, OCTOBER, 1, 4, 0))); + assertEquals(removeTimezone(timestamp(2017, SEPTEMBER, 30, 18, 0)), (timestamp(2017, OCTOBER, 1, 5, 0))); + assertEquals(removeTimezone(timestamp(2017, OCTOBER, 1, 0, 0)), (timestamp(2017, OCTOBER, 1, 11, 0))); + assertEquals(removeTimezone(timestamp(2017, OCTOBER, 1, 1, 0)), (timestamp(2017, OCTOBER, 1, 12, 0))); + assertEquals(removeTimezone(timestamp(2017, OCTOBER, 1, 2, 0)), (timestamp(2017, OCTOBER, 1, 13, 0))); + assertEquals(removeTimezone(timestamp(2017, OCTOBER, 1, 3, 0)), (timestamp(2017, OCTOBER, 1, 14, 0))); + assertEquals(removeTimezone(timestamp(2017, OCTOBER, 1, 4, 0)), (timestamp(2017, OCTOBER, 1, 15, 0))); + assertEquals(removeTimezone(timestamp(2017, OCTOBER, 1, 8, 0)), (timestamp(2017, OCTOBER, 1, 19, 0))); + assertEquals(removeTimezone(timestamp(2017, OCTOBER, 2, 8, 0)), (timestamp(2017, OCTOBER, 2, 19, 0))); + assertEquals(removeTimezone(timestamp(2017, NOVEMBER, 30, 8, 0)), (timestamp(2017, NOVEMBER, 30, 19, 0))); + assertEquals(removeTimezone(timestamp(2018, MARCH, 30, 13, 0)), (timestamp(2018, MARCH, 31, 0, 0))); + assertEquals(removeTimezone(timestamp(2018, MARCH, 31, 1, 0)), (timestamp(2018, MARCH, 31, 12, 0))); + assertEquals(removeTimezone(timestamp(2018, MARCH, 31, 7, 0)), (timestamp(2018, MARCH, 31, 18, 0))); + assertEquals(removeTimezone(timestamp(2018, MARCH, 31, 13, 0)), (timestamp(2018, APRIL, 1, 0, 0))); + assertEquals(removeTimezone(timestamp(2018, MARCH, 31, 14, 0)), (timestamp(2018, APRIL, 1, 1, 0))); + assertEquals(removeTimezone(timestamp(2018, MARCH, 31, 14, 59)), (timestamp(2018, APRIL, 1, 1, 59))); + // DST ends + assertEquals(removeTimezone(timestamp(2018, MARCH, 31, 16, 0)), (timestamp(2018, APRIL, 1, 2, 0))); + assertEquals(removeTimezone(timestamp(2018, MARCH, 31, 17, 0)), (timestamp(2018, APRIL, 1, 3, 0))); + assertEquals(removeTimezone(timestamp(2018, MARCH, 31, 18, 0)), (timestamp(2018, APRIL, 1, 4, 0))); + assertEquals(removeTimezone(timestamp(2018, APRIL, 1, 0, 0)), (timestamp(2018, APRIL, 1, 10, 0))); + assertEquals(removeTimezone(timestamp(2018, APRIL, 1, 8, 0)), (timestamp(2018, APRIL, 1, 18, 0))); + } }