diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.kt b/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.kt index 38ba3a1b4..7aab1463f 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.kt @@ -21,13 +21,13 @@ package org.isoron.uhabits.receivers import android.content.Context import android.content.Intent import android.net.Uri +import org.isoron.platform.time.LocalDate.Companion.getUpcomingTimeInMillis import org.isoron.uhabits.core.AppScope import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.Timestamp import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.core.reminders.ReminderScheduler import org.isoron.uhabits.core.ui.NotificationTray -import org.isoron.uhabits.core.utils.DateUtils.Companion.getUpcomingTimeInMillis import org.isoron.uhabits.notifications.SnoozeDelayPickerActivity import javax.inject.Inject diff --git a/uhabits-core/src/commonMain/kotlin/org/isoron/platform/time/Dates.kt b/uhabits-core/src/commonMain/kotlin/org/isoron/platform/time/Dates.kt index ba6569a77..94f415b8a 100644 --- a/uhabits-core/src/commonMain/kotlin/org/isoron/platform/time/Dates.kt +++ b/uhabits-core/src/commonMain/kotlin/org/isoron/platform/time/Dates.kt @@ -22,8 +22,11 @@ package org.isoron.platform.time import io.fluidsonic.locale.Locale import kotlinx.datetime.Clock import kotlinx.datetime.Instant +import kotlinx.datetime.LocalDateTime import kotlinx.datetime.TimeZone import kotlinx.datetime.offsetAt +import kotlinx.datetime.toInstant +import kotlinx.datetime.toLocalDateTime import org.isoron.platform.i18n.getDefault import kotlin.math.abs import kotlin.math.ceil @@ -232,14 +235,44 @@ data class LocalDate(val daysSince2000: Int) { return localTimestamp - offsetDifference } - fun removeTimezone(timestamp: Long): Long { + fun removeTimezone(timestampInMillis: Long): Long { val tz = getTimeZone() - return timestamp + ( + return timestampInMillis + ( tz.offsetAt( - Instant.fromEpochMilliseconds(timestamp) + Instant.fromEpochMilliseconds(timestampInMillis) ).totalSeconds * 1000 ) } + + fun getStartOfTodayLocalDateTime(): LocalDateTime = getLocalDateTime(getStartOfToday()) + + private fun getLocalDateTime(timestamp: Long): LocalDateTime { + return Instant.fromEpochMilliseconds(timestamp).toLocalDateTime(TimeZone.UTC) + } + + fun getUpcomingTimeInMillis( + hour: Int, + minute: Int + ): Long { + val startOfToday = getStartOfTodayLocalDateTime() + var time = + LocalDateTime( + year = startOfToday.year, + month = startOfToday.month, + dayOfMonth = startOfToday.dayOfMonth, + hour = hour, + minute = minute, + second = 0 + ).toInstant( + getTimeZone() + ).toEpochMilliseconds() + + if (getLocalTime() > time) { + time += DAY_LENGTH + } + + return applyTimezone(time) + } } } diff --git a/uhabits-core/src/commonTest/kotlin/org/isoron/platform/time/DatesTest.kt b/uhabits-core/src/commonTest/kotlin/org/isoron/platform/time/DatesTest.kt index b8d5605ad..d27599f8f 100644 --- a/uhabits-core/src/commonTest/kotlin/org/isoron/platform/time/DatesTest.kt +++ b/uhabits-core/src/commonTest/kotlin/org/isoron/platform/time/DatesTest.kt @@ -19,16 +19,20 @@ package org.isoron.platform.time +import kotlinx.datetime.Instant import kotlinx.datetime.LocalDateTime import kotlinx.datetime.Month import kotlinx.datetime.TimeZone import kotlinx.datetime.toInstant +import kotlinx.datetime.toLocalDateTime import org.isoron.platform.core.BaseUnitTest import org.isoron.platform.time.LocalDate.Companion.applyTimezone import org.isoron.platform.time.LocalDate.Companion.getStartOfDay import org.isoron.platform.time.LocalDate.Companion.getStartOfDayWithOffset import org.isoron.platform.time.LocalDate.Companion.getStartOfToday +import org.isoron.platform.time.LocalDate.Companion.getStartOfTodayLocalDateTime import org.isoron.platform.time.LocalDate.Companion.getStartOfTodayWithOffset +import org.isoron.platform.time.LocalDate.Companion.getUpcomingTimeInMillis import org.isoron.platform.time.LocalDate.Companion.getWeekdaySequence import org.isoron.platform.time.LocalDate.Companion.removeTimezone import kotlin.test.Test @@ -117,6 +121,16 @@ class DatesTest : BaseUnitTest() { assertEquals(startOfToday, startOfTodayWithOffset) } + @Test + fun testGetStartOfTodayLocalDateTime() { + LocalDate.fixedLocalTime = FIXED_LOCAL_TIME + val startOfDay = unixTime(2015, Month.JANUARY, 25, 0, 0) + val expectedLocalDateTime = Instant.fromEpochMilliseconds(startOfDay).toLocalDateTime( + TimeZone.UTC + ) + assertEquals(expectedLocalDateTime, getStartOfTodayLocalDateTime()) + } + @Test fun test_applyTimezone() { LocalDate.fixedTimeZone = TimeZone.of("Australia/Sydney") @@ -258,7 +272,7 @@ class DatesTest : BaseUnitTest() { @Test fun test_removeTimezone() { - LocalDate.fixedTimeZone = kotlinx.datetime.TimeZone.of("Australia/Sydney") + LocalDate.fixedTimeZone = TimeZone.of("Australia/Sydney") assertEquals( removeTimezone(unixTime(2017, Month.JULY, 30, 8, 0)), unixTime(2017, Month.JULY, 30, 18, 0) @@ -395,6 +409,15 @@ class DatesTest : BaseUnitTest() { ) } + @Test + fun testGetUpcomingTimeInMillis() { + LocalDate.fixedLocalTime = FIXED_LOCAL_TIME + LocalDate.fixedTimeZone = TimeZone.UTC + val expected = unixTime(2015, Month.JANUARY, 25, 10, 1) + val upcomingTimeMillis = getUpcomingTimeInMillis(10, 1) + assertEquals(expected, upcomingTimeMillis) + } + private fun unixTime(year: Int, month: Month, day: Int): Long { return unixTime(year, month, day, 0, 0) } diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Reminder.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Reminder.kt index fcd1ff73b..6de63bbf6 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Reminder.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Reminder.kt @@ -18,7 +18,7 @@ */ package org.isoron.uhabits.core.models -import org.isoron.uhabits.core.utils.DateUtils +import org.isoron.platform.time.LocalDate.Companion.getUpcomingTimeInMillis data class Reminder( val hour: Int, @@ -26,5 +26,5 @@ data class Reminder( val days: WeekdayList, ) { val timeInMillis: Long - get() = DateUtils.getUpcomingTimeInMillis(hour, minute) + get() = getUpcomingTimeInMillis(hour, minute) } diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/utils/DateUtils.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/utils/DateUtils.kt index 82e65a5aa..dfb7b1a67 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/utils/DateUtils.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/utils/DateUtils.kt @@ -19,12 +19,10 @@ package org.isoron.uhabits.core.utils import org.isoron.platform.time.LocalDate -import org.isoron.platform.time.LocalDate.Companion.DAY_LENGTH -import org.isoron.platform.time.LocalDate.Companion.applyTimezone import org.isoron.platform.time.LocalDate.Companion.getLocalTime import org.isoron.platform.time.LocalDate.Companion.getStartOfToday import org.isoron.platform.time.LocalDate.Companion.getStartOfTodayWithOffset -import org.isoron.platform.time.LocalDate.Companion.getTimeZone +import org.isoron.platform.time.LocalDate.Companion.getUpcomingTimeInMillis import org.isoron.platform.time.LocalDate.Companion.startDayHourOffset import org.isoron.platform.time.LocalDate.Companion.startDayMinuteOffset import org.isoron.uhabits.core.models.Timestamp @@ -208,24 +206,6 @@ abstract class DateUtils { } } } - - @JvmStatic - fun getUpcomingTimeInMillis( - hour: Int, - minute: Int - ): Long { - val calendar = getStartOfTodayCalendar() - calendar.set(Calendar.HOUR_OF_DAY, hour) - calendar.set(Calendar.MINUTE, minute) - calendar.set(Calendar.SECOND, 0) - var time = calendar.timeInMillis - - if (getLocalTime() > time) { - time += DAY_LENGTH - } - - return applyTimezone(time) - } } enum class TruncateField { diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/DateUtilsTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/DateUtilsTest.kt index 8e79b607b..877d58d95 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/DateUtilsTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/DateUtilsTest.kt @@ -233,15 +233,6 @@ class DateUtilsTest : BaseUnitTest() { assertThat(expected, equalTo(truncate(field, Timestamp(nonTruncatedDate), firstWeekday))) } - @Test - fun testGetUpcomingTimeInMillis() { - LocalDate.fixedLocalTime = FIXED_LOCAL_TIME - LocalDate.fixedTimeZone = kotlinx.datetime.TimeZone.UTC - val expected = unixTime(2015, Calendar.JANUARY, 25, 10, 1) - val upcomingTimeMillis = DateUtils.getUpcomingTimeInMillis(10, 1) - assertThat(expected, equalTo(upcomingTimeMillis)) - } - @Test @Throws(Exception::class) fun testMillisecondsUntilTomorrow() { @@ -266,17 +257,6 @@ class DateUtilsTest : BaseUnitTest() { ) } - @Test - fun testGetStartOfTodayCalendar() { - LocalDate.fixedLocalTime = FIXED_LOCAL_TIME - LocalDate.fixedLocale = Locale.forLanguageTag("de-de") - val expectedStartOfDay = unixTime(2015, Calendar.JANUARY, 25, 0, 0) - val expectedCalendar = GregorianCalendar(TimeZone.getTimeZone("GMT"), java.util.Locale.GERMANY) - expectedCalendar.timeInMillis = expectedStartOfDay - val startOfTodayCalendar = DateUtils.getStartOfTodayCalendar() - assertThat(expectedCalendar, equalTo(startOfTodayCalendar)) - } - @Test fun testGetStartOfTodayCalendarWithOffset_priorToOffset() { val hourOffset = 3