From bf4167651beb61799cd2f2bb5bd2ad5aff946eed Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Thu, 20 Jun 2024 00:21:31 -0500 Subject: [PATCH] Use kotlinx.datetime --- .../com/todoroo/astrid/adapter/TaskAdapter.kt | 14 +- .../todoroo/astrid/alarms/AlarmCalculator.kt | 2 +- .../astrid/repeats/RepeatControlSet.kt | 3 +- .../com/todoroo/astrid/service/TaskCreator.kt | 6 +- .../todoroo/astrid/ui/StartDateViewModel.kt | 8 +- .../main/java/org/tasks/caldav/iCalendar.kt | 4 +- .../java/org/tasks/caldav/iCalendarMerge.kt | 4 +- .../java/org/tasks/compose/StartDateChip.kt | 4 +- .../java/org/tasks/data/TaskExtensions.kt | 6 +- .../main/java/org/tasks/date/DateTimeUtils.kt | 2 +- .../java/org/tasks/dialogs/DateTimePicker.kt | 12 +- .../org/tasks/dialogs/MyDatePickerDialog.kt | 2 +- .../org/tasks/dialogs/MyTimePickerDialog.kt | 2 +- .../repeats/CustomRecurrenceViewModel.kt | 4 +- .../org/tasks/tasklist/SectionedDataSource.kt | 2 +- .../java/org/tasks/tasklist/TaskViewHolder.kt | 8 +- .../main/java/org/tasks/time/DateTime.java | 440 ------------------ app/src/main/java/org/tasks/time/DateTime.kt | 323 +++++++++++++ .../main/java/org/tasks/time/DateTimeUtils.kt | 14 +- .../java/org/tasks/ui/TaskEditViewModel.kt | 6 +- .../tasks/widget/TasksWidgetViewFactory.kt | 2 +- .../org/tasks/widget/WidgetChipProvider.kt | 8 +- .../tasks/caldav/extensions/VAlarmTests.kt | 2 +- data/build.gradle.kts | 1 + .../kotlin/org/tasks/Platform.android.kt | 5 +- .../tasks/{Platform.common.kt => Platform.kt} | 4 +- .../kotlin/org/tasks/data/dao/CaldavDao.kt | 2 +- .../kotlin/org/tasks/time/DateTimeUtils.kt | 4 +- .../org/tasks/time/SystemMillisProvider.kt | 4 +- .../jvmMain/kotlin/org/tasks/Platform.jvm.kt | 2 + deps_fdroid.txt | 4 + deps_googleplay.txt | 4 + gradle/libs.versions.toml | 1 + kmp/build.gradle.kts | 1 + .../kotlin/org/tasks/time/LongExtensions.kt | 102 ++++ 35 files changed, 503 insertions(+), 509 deletions(-) delete mode 100644 app/src/main/java/org/tasks/time/DateTime.java create mode 100644 app/src/main/java/org/tasks/time/DateTime.kt rename data/src/commonMain/kotlin/org/tasks/{Platform.common.kt => Platform.kt} (82%) create mode 100644 kmp/src/commonMain/kotlin/org/tasks/time/LongExtensions.kt diff --git a/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapter.kt b/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapter.kt index 60ad044ac..93d48552b 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapter.kt +++ b/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapter.kt @@ -14,17 +14,17 @@ import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.service.TaskMover import org.tasks.BuildConfig import org.tasks.LocalBroadcastManager +import org.tasks.data.TaskContainer +import org.tasks.data.createDueDate +import org.tasks.data.createHideUntil import org.tasks.data.dao.CaldavDao import org.tasks.data.dao.CaldavDao.Companion.toAppleEpoch -import org.tasks.data.entity.CaldavTask import org.tasks.data.dao.GoogleTaskDao +import org.tasks.data.entity.CaldavTask import org.tasks.data.entity.Task import org.tasks.data.entity.Task.Companion.HIDE_UNTIL_SPECIFIC_DAY -import org.tasks.data.TaskContainer -import org.tasks.data.createDueDate -import org.tasks.data.createHideUntil import org.tasks.date.DateTimeUtils.toDateTime -import org.tasks.time.DateTimeUtils.millisOfDay +import org.tasks.time.millisOfDay open class TaskAdapter( private val newTasksOnTop: Boolean, @@ -206,7 +206,7 @@ open class TaskAdapter( val original = task.dueDate task.setDueDateAdjustingHideUntil(when { date == 0L -> 0L - task.hasDueTime() -> date.toDateTime().withMillisOfDay(original.millisOfDay()).millis + task.hasDueTime() -> date.toDateTime().withMillisOfDay(original.millisOfDay).millis else -> createDueDate(Task.URGENCY_SPECIFIC_DAY, date) }) if (original != task.dueDate) { @@ -218,7 +218,7 @@ open class TaskAdapter( val original = task.hideUntil task.hideUntil = when { date == 0L -> 0L - task.hasStartDate() -> date.toDateTime().withMillisOfDay(original.millisOfDay()).millis + task.hasStartDate() -> date.toDateTime().withMillisOfDay(original.millisOfDay).millis else -> task.createHideUntil(HIDE_UNTIL_SPECIFIC_DAY, date) } if (original != task.hideUntil) { diff --git a/app/src/main/java/com/todoroo/astrid/alarms/AlarmCalculator.kt b/app/src/main/java/com/todoroo/astrid/alarms/AlarmCalculator.kt index 1dc869f42..57c0eb8f0 100644 --- a/app/src/main/java/com/todoroo/astrid/alarms/AlarmCalculator.kt +++ b/app/src/main/java/com/todoroo/astrid/alarms/AlarmCalculator.kt @@ -5,7 +5,7 @@ import org.tasks.data.entity.Notification import org.tasks.data.entity.Task import org.tasks.preferences.Preferences import org.tasks.reminders.Random -import org.tasks.time.DateTimeUtils.withMillisOfDay +import org.tasks.time.withMillisOfDay import javax.inject.Inject class AlarmCalculator( diff --git a/app/src/main/java/com/todoroo/astrid/repeats/RepeatControlSet.kt b/app/src/main/java/com/todoroo/astrid/repeats/RepeatControlSet.kt index 8ca1d1cd8..41fd58c8b 100644 --- a/app/src/main/java/com/todoroo/astrid/repeats/RepeatControlSet.kt +++ b/app/src/main/java/com/todoroo/astrid/repeats/RepeatControlSet.kt @@ -29,6 +29,7 @@ import org.tasks.repeats.RepeatRuleToString import org.tasks.themes.TasksTheme import org.tasks.time.DateTime import org.tasks.time.DateTimeUtils2.currentTimeMillis +import org.tasks.time.startOfDay import org.tasks.ui.TaskEditControlFragment import javax.inject.Inject @@ -43,7 +44,7 @@ class RepeatControlSet : TaskEditControlFragment() { val result = data?.getStringExtra(BasicRecurrenceDialog.EXTRA_RRULE) viewModel.recurrence.value = result if (result?.isNotBlank() == true && viewModel.dueDate.value == 0L) { - viewModel.setDueDate(DateTime().startOfDay().millis) + viewModel.setDueDate(currentTimeMillis().startOfDay()) } } } else { diff --git a/app/src/main/java/com/todoroo/astrid/service/TaskCreator.kt b/app/src/main/java/com/todoroo/astrid/service/TaskCreator.kt index ec83b9e3d..cb71786ba 100644 --- a/app/src/main/java/com/todoroo/astrid/service/TaskCreator.kt +++ b/app/src/main/java/com/todoroo/astrid/service/TaskCreator.kt @@ -1,8 +1,6 @@ package com.todoroo.astrid.service import com.todoroo.andlib.utility.DateUtilities -import org.tasks.filters.CaldavFilter -import org.tasks.filters.GtasksFilter import com.todoroo.astrid.api.PermaSql import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.gcal.GCalHelper @@ -34,12 +32,14 @@ import org.tasks.data.entity.Task.Companion.DUE_DATE import org.tasks.data.entity.Task.Companion.HIDE_UNTIL import org.tasks.data.entity.Task.Companion.HIDE_UNTIL_NONE import org.tasks.data.entity.Task.Companion.IMPORTANCE +import org.tasks.filters.CaldavFilter import org.tasks.filters.Filter +import org.tasks.filters.GtasksFilter import org.tasks.filters.mapFromSerializedString import org.tasks.preferences.DefaultFilterProvider import org.tasks.preferences.Preferences -import org.tasks.time.DateTimeUtils.startOfDay import org.tasks.time.DateTimeUtils2.currentTimeMillis +import org.tasks.time.startOfDay import timber.log.Timber import javax.inject.Inject diff --git a/app/src/main/java/com/todoroo/astrid/ui/StartDateViewModel.kt b/app/src/main/java/com/todoroo/astrid/ui/StartDateViewModel.kt index 135948156..f1cee1425 100644 --- a/app/src/main/java/com/todoroo/astrid/ui/StartDateViewModel.kt +++ b/app/src/main/java/com/todoroo/astrid/ui/StartDateViewModel.kt @@ -1,12 +1,12 @@ package com.todoroo.astrid.ui import androidx.lifecycle.ViewModel -import org.tasks.data.entity.Task import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import org.tasks.R +import org.tasks.data.entity.Task import org.tasks.date.DateTimeUtils.toDateTime import org.tasks.dialogs.StartDatePicker import org.tasks.dialogs.StartDatePicker.Companion.DAY_BEFORE_DUE @@ -14,8 +14,8 @@ import org.tasks.dialogs.StartDatePicker.Companion.DUE_DATE import org.tasks.dialogs.StartDatePicker.Companion.DUE_TIME import org.tasks.dialogs.StartDatePicker.Companion.WEEK_BEFORE_DUE import org.tasks.preferences.Preferences -import org.tasks.time.DateTimeUtils.millisOfDay -import org.tasks.time.DateTimeUtils.startOfDay +import org.tasks.time.millisOfDay +import org.tasks.time.startOfDay import javax.inject.Inject @HiltViewModel @@ -32,7 +32,7 @@ class StartDateViewModel @Inject constructor( fun init(dueDate: Long, startDate: Long, isNew: Boolean) { val dueDay = dueDate.startOfDay() - val dueTime = dueDate.millisOfDay() + val dueTime = dueDate.millisOfDay val hideUntil = startDate.takeIf { it > 0 }?.toDateTime() if (hideUntil == null) { if (isNew) { diff --git a/app/src/main/java/org/tasks/caldav/iCalendar.kt b/app/src/main/java/org/tasks/caldav/iCalendar.kt index 52d191fe6..2521a402f 100644 --- a/app/src/main/java/org/tasks/caldav/iCalendar.kt +++ b/app/src/main/java/org/tasks/caldav/iCalendar.kt @@ -57,10 +57,10 @@ import org.tasks.location.GeofenceApi import org.tasks.notifications.NotificationManager import org.tasks.preferences.Preferences import org.tasks.repeats.RecurrenceUtils.newRRule -import org.tasks.time.DateTimeUtils.startOfDay -import org.tasks.time.DateTimeUtils.startOfMinute import org.tasks.time.DateTimeUtils.toDate import org.tasks.time.DateTimeUtils2.currentTimeMillis +import org.tasks.time.startOfDay +import org.tasks.time.startOfMinute import timber.log.Timber import java.io.ByteArrayOutputStream import java.io.StringReader diff --git a/app/src/main/java/org/tasks/caldav/iCalendarMerge.kt b/app/src/main/java/org/tasks/caldav/iCalendarMerge.kt index c8c37ab8a..a9070be91 100644 --- a/app/src/main/java/org/tasks/caldav/iCalendarMerge.kt +++ b/app/src/main/java/org/tasks/caldav/iCalendarMerge.kt @@ -14,9 +14,9 @@ import org.tasks.data.entity.Task.Priority.Companion.MEDIUM import org.tasks.data.entity.Task.Priority.Companion.NONE import org.tasks.data.setRecurrence import org.tasks.date.DateTimeUtils.newDateTime -import org.tasks.time.DateTime.UTC -import org.tasks.time.DateTimeUtils.startOfSecond +import org.tasks.time.DateTime.Companion.UTC import org.tasks.time.DateTimeUtils2.currentTimeMillis +import org.tasks.time.startOfSecond fun org.tasks.data.entity.Task.applyRemote( remote: Task, diff --git a/app/src/main/java/org/tasks/compose/StartDateChip.kt b/app/src/main/java/org/tasks/compose/StartDateChip.kt index 7b55c709f..d2e6e1847 100644 --- a/app/src/main/java/org/tasks/compose/StartDateChip.kt +++ b/app/src/main/java/org/tasks/compose/StartDateChip.kt @@ -6,10 +6,10 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import com.todoroo.andlib.utility.DateUtilities -import org.tasks.data.entity.Task import org.tasks.R +import org.tasks.data.entity.Task import org.tasks.date.DateTimeUtils.toDateTime -import org.tasks.time.DateTimeUtils.startOfDay +import org.tasks.time.startOfDay import java.time.format.FormatStyle @Composable diff --git a/app/src/main/java/org/tasks/data/TaskExtensions.kt b/app/src/main/java/org/tasks/data/TaskExtensions.kt index 621820e96..8324b05eb 100644 --- a/app/src/main/java/org/tasks/data/TaskExtensions.kt +++ b/app/src/main/java/org/tasks/data/TaskExtensions.kt @@ -1,12 +1,12 @@ package org.tasks.data import com.todoroo.andlib.utility.DateUtilities -import org.tasks.data.entity.Task import net.fortuna.ical4j.model.Recur +import org.tasks.data.entity.Task import org.tasks.date.DateTimeUtils import org.tasks.date.DateTimeUtils.toDateTime -import org.tasks.time.DateTimeUtils.startOfDay import org.tasks.time.DateTimeUtils2.currentTimeMillis +import org.tasks.time.startOfDay /** Checks whether task is hidden. Requires HIDDEN_UNTIL */ val Task.isHidden @@ -44,7 +44,7 @@ val Task.isOverdue: Boolean if (isCompleted || !hasDueDate()) { return false } - val compareTo = if (hasDueTime()) currentTimeMillis() else DateTimeUtils.newDateTime().startOfDay().millis + val compareTo = if (hasDueTime()) currentTimeMillis() else currentTimeMillis().startOfDay() return dueDate < compareTo } diff --git a/app/src/main/java/org/tasks/date/DateTimeUtils.kt b/app/src/main/java/org/tasks/date/DateTimeUtils.kt index 38e603feb..c43dcef8f 100644 --- a/app/src/main/java/org/tasks/date/DateTimeUtils.kt +++ b/app/src/main/java/org/tasks/date/DateTimeUtils.kt @@ -1,7 +1,7 @@ package org.tasks.date import org.tasks.time.DateTime -import org.tasks.time.DateTime.UTC +import org.tasks.time.DateTime.Companion.UTC import java.util.TimeZone object DateTimeUtils { diff --git a/app/src/main/java/org/tasks/dialogs/DateTimePicker.kt b/app/src/main/java/org/tasks/dialogs/DateTimePicker.kt index e2e248f66..1bebbc4b3 100644 --- a/app/src/main/java/org/tasks/dialogs/DateTimePicker.kt +++ b/app/src/main/java/org/tasks/dialogs/DateTimePicker.kt @@ -16,16 +16,16 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.launch import org.tasks.R -import org.tasks.data.entity.Task import org.tasks.data.createDueDate +import org.tasks.data.entity.Task import org.tasks.databinding.DialogDateTimePickerBinding import org.tasks.date.DateTimeUtils.newDateTime import org.tasks.date.DateTimeUtils.toDateTime import org.tasks.dialogs.MyTimePickerDialog.Companion.newTimePicker import org.tasks.notifications.NotificationManager import org.tasks.time.DateTime -import org.tasks.time.DateTimeUtils.millisOfDay -import org.tasks.time.DateTimeUtils.startOfDay +import org.tasks.time.millisOfDay +import org.tasks.time.startOfDay import java.time.format.FormatStyle import java.util.Calendar.FRIDAY import java.util.Calendar.MONDAY @@ -78,7 +78,7 @@ class DateTimePicker : BaseDateTimePicker() { ): DateTimePicker { val fragment = DateTimePicker() val dueDates = tasks.map { it.dueDate.startOfDay() }.toSet() - val dueTimes = tasks.map { it.dueDate.millisOfDay() }.toSet() + val dueTimes = tasks.map { it.dueDate.millisOfDay }.toSet() fragment.arguments = Bundle().apply { putLongArray(EXTRA_TASKS, tasks.map { it.id }.toLongArray()) putLong(EXTRA_DAY, if (dueDates.size == 1) dueDates.first() else MULTIPLE_DAYS) @@ -99,7 +99,7 @@ class DateTimePicker : BaseDateTimePicker() { val fragment = DateTimePicker() fragment.arguments = Bundle().apply { putLong(EXTRA_DAY, current.startOfDay()) - putInt(EXTRA_TIME, current.millisOfDay()) + putInt(EXTRA_TIME, current.millisOfDay) putBoolean(EXTRA_AUTO_CLOSE, autoClose) putBoolean(EXTRA_HIDE_NO_DATE, hideNoDate) } @@ -267,7 +267,7 @@ class DateTimePicker : BaseDateTimePicker() { selectedDay } val time = if (selectedTime == MULTIPLE_TIMES) { - if (it.hasDueTime()) it.dueDate.millisOfDay() else NO_TIME + if (it.hasDueTime()) it.dueDate.millisOfDay else NO_TIME } else { selectedTime } diff --git a/app/src/main/java/org/tasks/dialogs/MyDatePickerDialog.kt b/app/src/main/java/org/tasks/dialogs/MyDatePickerDialog.kt index 3ba0025d2..c7312b011 100644 --- a/app/src/main/java/org/tasks/dialogs/MyDatePickerDialog.kt +++ b/app/src/main/java/org/tasks/dialogs/MyDatePickerDialog.kt @@ -12,8 +12,8 @@ import dagger.hilt.android.AndroidEntryPoint import org.tasks.R import org.tasks.preferences.Preferences import org.tasks.time.DateTime -import org.tasks.time.DateTimeUtils.startOfDay import org.tasks.time.DateTimeUtils2.currentTimeMillis +import org.tasks.time.startOfDay import javax.inject.Inject @AndroidEntryPoint diff --git a/app/src/main/java/org/tasks/dialogs/MyTimePickerDialog.kt b/app/src/main/java/org/tasks/dialogs/MyTimePickerDialog.kt index efb05177a..388234ca3 100644 --- a/app/src/main/java/org/tasks/dialogs/MyTimePickerDialog.kt +++ b/app/src/main/java/org/tasks/dialogs/MyTimePickerDialog.kt @@ -18,8 +18,8 @@ import org.tasks.R import org.tasks.date.DateTimeUtils.toDateTime import org.tasks.preferences.Preferences import org.tasks.time.DateTime -import org.tasks.time.DateTimeUtils.startOfDay import org.tasks.time.DateTimeUtils2.currentTimeMillis +import org.tasks.time.startOfDay import javax.inject.Inject @AndroidEntryPoint diff --git a/app/src/main/java/org/tasks/repeats/CustomRecurrenceViewModel.kt b/app/src/main/java/org/tasks/repeats/CustomRecurrenceViewModel.kt index a2baabeb3..7b232cf04 100644 --- a/app/src/main/java/org/tasks/repeats/CustomRecurrenceViewModel.kt +++ b/app/src/main/java/org/tasks/repeats/CustomRecurrenceViewModel.kt @@ -23,8 +23,8 @@ import org.tasks.repeats.CustomRecurrenceActivity.Companion.EXTRA_ACCOUNT_TYPE import org.tasks.repeats.CustomRecurrenceActivity.Companion.EXTRA_DATE import org.tasks.repeats.CustomRecurrenceActivity.Companion.EXTRA_RRULE import org.tasks.time.DateTime -import org.tasks.time.DateTimeUtils.startOfDay import org.tasks.time.DateTimeUtils2.currentTimeMillis +import org.tasks.time.startOfDay import java.time.DayOfWeek import java.time.Instant import java.time.ZoneId @@ -42,7 +42,7 @@ class CustomRecurrenceViewModel @Inject constructor( data class ViewState( val interval: Int = 1, val frequency: Recur.Frequency = WEEKLY, - val dueDate: Long = DateTime().startOfDay().millis, + val dueDate: Long = currentTimeMillis().startOfDay(), val endSelection: Int = 0, val endDate: Long = dueDate.toDateTime().plusMonths(1).startOfDay().millis, val endCount: Int = 1, diff --git a/app/src/main/java/org/tasks/tasklist/SectionedDataSource.kt b/app/src/main/java/org/tasks/tasklist/SectionedDataSource.kt index 900ee2903..09dcceed9 100644 --- a/app/src/main/java/org/tasks/tasklist/SectionedDataSource.kt +++ b/app/src/main/java/org/tasks/tasklist/SectionedDataSource.kt @@ -4,8 +4,8 @@ import android.util.SparseArray import androidx.core.util.forEach import com.todoroo.astrid.core.SortHelper import org.tasks.data.TaskContainer -import org.tasks.time.DateTimeUtils.startOfDay import org.tasks.time.DateTimeUtils2.currentTimeMillis +import org.tasks.time.startOfDay import org.tasks.ui.TaskListViewModel.UiItem class SectionedDataSource( diff --git a/app/src/main/java/org/tasks/tasklist/TaskViewHolder.kt b/app/src/main/java/org/tasks/tasklist/TaskViewHolder.kt index 335b57a1f..496d7ca03 100644 --- a/app/src/main/java/org/tasks/tasklist/TaskViewHolder.kt +++ b/app/src/main/java/org/tasks/tasklist/TaskViewHolder.kt @@ -14,9 +14,6 @@ import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.unit.dp import androidx.recyclerview.widget.RecyclerView import com.todoroo.andlib.utility.DateUtilities -import org.tasks.filters.CaldavFilter -import org.tasks.filters.GtasksFilter -import org.tasks.filters.TagFilter import com.todoroo.astrid.core.SortHelper.SORT_DUE import com.todoroo.astrid.core.SortHelper.SORT_LIST import com.todoroo.astrid.core.SortHelper.SORT_START @@ -33,13 +30,16 @@ import org.tasks.data.isOverdue import org.tasks.databinding.TaskAdapterRowBinding import org.tasks.date.DateTimeUtils.newDateTime import org.tasks.dialogs.Linkify +import org.tasks.filters.CaldavFilter import org.tasks.filters.Filter +import org.tasks.filters.GtasksFilter import org.tasks.filters.PlaceFilter +import org.tasks.filters.TagFilter import org.tasks.markdown.Markdown import org.tasks.preferences.Preferences import org.tasks.themes.TasksTheme -import org.tasks.time.DateTimeUtils.startOfDay import org.tasks.time.DateTimeUtils2.currentTimeMillis +import org.tasks.time.startOfDay import org.tasks.ui.CheckBoxProvider import org.tasks.ui.ChipProvider import java.time.format.FormatStyle diff --git a/app/src/main/java/org/tasks/time/DateTime.java b/app/src/main/java/org/tasks/time/DateTime.java deleted file mode 100644 index aa95c198a..000000000 --- a/app/src/main/java/org/tasks/time/DateTime.java +++ /dev/null @@ -1,440 +0,0 @@ -package org.tasks.time; - -import static org.tasks.time.DateTimeUtils2.currentTimeMillis; -import static java.util.Calendar.FRIDAY; -import static java.util.Calendar.MONDAY; -import static java.util.Calendar.SATURDAY; -import static java.util.Calendar.SUNDAY; -import static java.util.Calendar.THURSDAY; -import static java.util.Calendar.TUESDAY; -import static java.util.Calendar.WEDNESDAY; - -import net.fortuna.ical4j.model.WeekDay; - -import org.tasks.data.dao.CaldavDao; - -import java.text.SimpleDateFormat; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.Locale; -import java.util.Objects; -import java.util.TimeZone; -import java.util.concurrent.TimeUnit; - -public class DateTime { - - public static final int MAX_MILLIS_PER_DAY = (int) TimeUnit.DAYS.toMillis(1) - 1; - public static final TimeZone UTC = TimeZone.getTimeZone("GMT"); - private static final int MILLIS_PER_HOUR = (int) TimeUnit.HOURS.toMillis(1); - private static final int MILLIS_PER_MINUTE = (int) TimeUnit.MINUTES.toMillis(1); - private static final int MILLIS_PER_SECOND = (int) TimeUnit.SECONDS.toMillis(1); - - private final TimeZone timeZone; - private final long timestamp; - - public DateTime(int year, int month, int day) { - this(year, month, day, 0, 0, 0, 0); - } - - public DateTime(int year, int month, int day, int hour, int minute) { - this(year, month, day, hour, minute, 0, 0); - } - - public DateTime(int year, int month, int day, int hour, int minute, int second) { - this(year, month, day, hour, minute, second, 0); - } - - public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond) { - this(year, month, day, hour, minute, second, millisecond, TimeZone.getDefault()); - } - - public DateTime( - int year, - int month, - int day, - int hour, - int minute, - int second, - int millisecond, - TimeZone timeZone) { - GregorianCalendar gregorianCalendar = new GregorianCalendar(timeZone); - gregorianCalendar.set(year, month - 1, day, hour, minute, second); - gregorianCalendar.set(Calendar.MILLISECOND, millisecond); - timestamp = gregorianCalendar.getTimeInMillis(); - this.timeZone = timeZone; - } - - public DateTime() { - this(currentTimeMillis()); - } - - public DateTime(long timestamp) { - this(timestamp, TimeZone.getDefault()); - } - - public DateTime(long timestamp, TimeZone timeZone) { - this.timestamp = timestamp; - this.timeZone = timeZone; - } - - private DateTime(Calendar calendar) { - this(calendar.getTimeInMillis(), calendar.getTimeZone()); - } - - public static DateTime from(Date date) { - if (date == null) { - return new DateTime(0); - } - DateTime dateTime = new DateTime(date.getTime()); - return dateTime.minusMillis(dateTime.getOffset()); - } - - public static DateTime from(net.fortuna.ical4j.model.Date date) { - if (date instanceof net.fortuna.ical4j.model.DateTime) { - net.fortuna.ical4j.model.DateTime dt = (net.fortuna.ical4j.model.DateTime) date; - TimeZone tz = dt.getTimeZone(); - return new DateTime( - dt.getTime(), - tz != null ? tz : dt.isUtc() ? UTC : TimeZone.getDefault() - ); - } else { - return from((java.util.Date) date); - } - } - - private DateTime setTime(int hours, int minutes, int seconds, int milliseconds) { - Calendar calendar = getCalendar(); - calendar.set(Calendar.HOUR_OF_DAY, hours); - calendar.set(Calendar.MINUTE, minutes); - calendar.set(Calendar.SECOND, seconds); - calendar.set(Calendar.MILLISECOND, milliseconds); - return new DateTime(calendar); - } - - public DateTime startOfDay() { - Calendar calendar = getCalendar(); - calendar.set(Calendar.HOUR_OF_DAY, 0); - calendar.set(Calendar.MINUTE, 0); - calendar.set(Calendar.SECOND, 0); - calendar.set(Calendar.MILLISECOND, 0); - return new DateTime(calendar); - } - - public DateTime startOfMinute() { - Calendar calendar = getCalendar(); - calendar.set(Calendar.SECOND, 0); - calendar.set(Calendar.MILLISECOND, 0); - return new DateTime(calendar); - } - - public DateTime startOfSecond() { - Calendar calendar = getCalendar(); - calendar.set(Calendar.MILLISECOND, 0); - return new DateTime(calendar); - } - - public DateTime endOfMinute() { - Calendar calendar = getCalendar(); - calendar.set(Calendar.SECOND, 59); - calendar.set(Calendar.MILLISECOND, 999); - return new DateTime(calendar); - } - - public DateTime noon() { - Calendar calendar = getCalendar(); - calendar.set(Calendar.HOUR_OF_DAY, 12); - calendar.set(Calendar.MINUTE, 0); - calendar.set(Calendar.SECOND, 0); - calendar.set(Calendar.MILLISECOND, 0); - return new DateTime(calendar); - } - - public DateTime endOfDay() { - Calendar calendar = getCalendar(); - calendar.set(Calendar.HOUR_OF_DAY, 23); - calendar.set(Calendar.MINUTE, 59); - calendar.set(Calendar.SECOND, 59); - calendar.set(Calendar.MILLISECOND, 0); - return new DateTime(calendar); - } - - public DateTime withMillisOfDay(int millisOfDay) { - if (millisOfDay > MAX_MILLIS_PER_DAY || millisOfDay < 0) { - throw new RuntimeException("Illegal millis of day: " + millisOfDay); - } - int hours = millisOfDay / MILLIS_PER_HOUR; - millisOfDay %= MILLIS_PER_HOUR; - int minutes = millisOfDay / MILLIS_PER_MINUTE; - millisOfDay %= MILLIS_PER_MINUTE; - int seconds = millisOfDay / MILLIS_PER_SECOND; - millisOfDay %= MILLIS_PER_SECOND; - return startOfDay().setTime(hours, minutes, seconds, millisOfDay); - } - - public long getOffset() { - return timeZone.getOffset(timestamp); - } - - public long getMillis() { - return timestamp; - } - - public int getMillisOfDay() { - Calendar calendar = getCalendar(); - long millisOfDay = - calendar.get(Calendar.MILLISECOND) - + TimeUnit.SECONDS.toMillis(calendar.get(Calendar.SECOND)) - + TimeUnit.MINUTES.toMillis(calendar.get(Calendar.MINUTE)) - + TimeUnit.HOURS.toMillis(calendar.get(Calendar.HOUR_OF_DAY)); - return (int) millisOfDay; - } - - public int getYear() { - return getCalendar().get(Calendar.YEAR); - } - - public int getMonthOfYear() { - return getCalendar().get(Calendar.MONTH) + 1; - } - - public int getDayOfMonth() { - return getCalendar().get(Calendar.DATE); - } - - public int getDayOfWeek() { - return getCalendar().get(Calendar.DAY_OF_WEEK); - } - - public int getHourOfDay() { - return getCalendar().get(Calendar.HOUR_OF_DAY); - } - - public int getMinuteOfHour() { - return getCalendar().get(Calendar.MINUTE); - } - - public int getSecondOfMinute() { - return getCalendar().get(Calendar.SECOND); - } - - public DateTime withYear(int year) { - return with(Calendar.YEAR, year); - } - - public DateTime withMonthOfYear(int monthOfYear) { - return with(Calendar.MONTH, monthOfYear - 1); - } - - public DateTime withDayOfMonth(int dayOfMonth) { - return with(Calendar.DAY_OF_MONTH, dayOfMonth); - } - - public DateTime withHourOfDay(int hourOfDay) { - return with(Calendar.HOUR_OF_DAY, hourOfDay); - } - - public DateTime withMinuteOfHour(int minuteOfHour) { - return with(Calendar.MINUTE, minuteOfHour); - } - - public DateTime withSecondOfMinute(int secondOfMinute) { - return with(Calendar.SECOND, secondOfMinute); - } - - public DateTime withMillisOfSecond(int millisOfSecond) { - return with(Calendar.MILLISECOND, millisOfSecond); - } - - public DateTime plusMonths(int interval) { - return add(Calendar.MONTH, interval); - } - - public DateTime plusWeeks(int weeks) { - return add(Calendar.WEEK_OF_MONTH, weeks); - } - - public DateTime plusDays(int interval) { - return add(Calendar.DATE, interval); - } - - public DateTime plusHours(int hours) { - return add(Calendar.HOUR_OF_DAY, hours); - } - - public DateTime plusMinutes(int minutes) { - return add(Calendar.MINUTE, minutes); - } - - public DateTime plusSeconds(int seconds) { - return add(Calendar.SECOND, seconds); - } - - public DateTime plusMillis(int millis) { - return add(Calendar.MILLISECOND, millis); - } - - public DateTime minusSeconds(int seconds) { - return subtract(Calendar.SECOND, seconds); - } - - public DateTime minusDays(int days) { - return subtract(Calendar.DATE, days); - } - - public DateTime minusMinutes(int minutes) { - return subtract(Calendar.MINUTE, minutes); - } - - public DateTime minusMillis(long millis) { - return new DateTime(timestamp - millis, timeZone); - } - - public boolean isAfterNow() { - return isAfter(currentTimeMillis()); - } - - public boolean isAfter(DateTime dateTime) { - return isAfter(dateTime.getMillis()); - } - - private boolean isAfter(long timestamp) { - return this.timestamp > timestamp; - } - - public boolean isBeforeNow() { - return timestamp < currentTimeMillis(); - } - - public boolean isBefore(DateTime dateTime) { - return timestamp < dateTime.getMillis(); - } - - public DateTime toUTC() { - return toTimeZone(UTC); - } - - public DateTime toLocal() { - return toTimeZone(TimeZone.getDefault()); - } - - public boolean isLastDayOfMonth() { - return getDayOfMonth() == getNumberOfDaysInMonth(); - } - - public int getNumberOfDaysInMonth() { - return getCalendar().getActualMaximum(Calendar.DAY_OF_MONTH); - } - - private DateTime toTimeZone(TimeZone timeZone) { - if (timeZone == this.timeZone) { - return this; - } - Calendar current = getCalendar(); - Calendar target = new GregorianCalendar(timeZone); - target.setTimeInMillis(current.getTimeInMillis()); - return new DateTime(target); - } - - private DateTime with(int field, int value) { - Calendar calendar = getCalendar(); - calendar.set(field, value); - return new DateTime(calendar); - } - - private DateTime subtract(int field, int value) { - return add(field, -value); - } - - private DateTime add(int field, int value) { - Calendar calendar = getCalendar(); - calendar.add(field, value); - return new DateTime(calendar); - } - - private Calendar getCalendar() { - Calendar calendar = new GregorianCalendar(timeZone); - calendar.setTimeInMillis(timestamp); - return calendar; - } - - public net.fortuna.ical4j.model.DateTime toDateTime() { - return timestamp == 0 ? null : new net.fortuna.ical4j.model.DateTime(timestamp); - } - - public net.fortuna.ical4j.model.Date toDate() { - return timestamp == 0 ? null : new net.fortuna.ical4j.model.Date(timestamp + getOffset()); - } - - public LocalDate toLocalDate() { - return timestamp == 0 ? null : LocalDate.of(getYear(), getMonthOfYear(), getDayOfMonth()); - } - - public LocalDateTime toLocalDateTime() { - return timestamp == 0 ? null : LocalDateTime.of(getYear(), getMonthOfYear(), getDayOfMonth(), getHourOfDay(), getMinuteOfHour()); - } - - public long toAppleEpoch() { - return CaldavDao.toAppleEpoch(timestamp); - } - - public int getDayOfWeekInMonth() { - return getCalendar().get(Calendar.DAY_OF_WEEK_IN_MONTH); - } - - public int getMaxDayOfWeekInMonth() { - return getCalendar().getActualMaximum(Calendar.DAY_OF_WEEK_IN_MONTH); - } - - public WeekDay getWeekDay() { - switch (getCalendar().get(Calendar.DAY_OF_WEEK)) { - case SUNDAY: - return WeekDay.SU; - case MONDAY: - return WeekDay.MO; - case TUESDAY: - return WeekDay.TU; - case WEDNESDAY: - return WeekDay.WE; - case THURSDAY: - return WeekDay.TH; - case FRIDAY: - return WeekDay.FR; - case SATURDAY: - return WeekDay.SA; - } - throw new RuntimeException(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof DateTime)) { - return false; - } - DateTime dateTime = (DateTime) o; - return timestamp == dateTime.timestamp && Objects.equals(timeZone, dateTime.timeZone); - } - - @Override - public int hashCode() { - return Objects.hash(timeZone, timestamp); - } - - public String toString(String format) { - Calendar calendar = getCalendar(); - SimpleDateFormat simpleDateFormat = - new SimpleDateFormat(format, Locale.getDefault()); - simpleDateFormat.setCalendar(calendar); - return simpleDateFormat.format(calendar.getTime()); - } - - @Override - public String toString() { - return toString("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); - } -} diff --git a/app/src/main/java/org/tasks/time/DateTime.kt b/app/src/main/java/org/tasks/time/DateTime.kt new file mode 100644 index 000000000..2a0727f86 --- /dev/null +++ b/app/src/main/java/org/tasks/time/DateTime.kt @@ -0,0 +1,323 @@ +package org.tasks.time + +import net.fortuna.ical4j.model.Date +import net.fortuna.ical4j.model.WeekDay +import org.tasks.data.dao.CaldavDao.Companion.toAppleEpoch +import org.tasks.time.DateTimeUtils2.currentTimeMillis +import java.text.SimpleDateFormat +import java.time.LocalDate +import java.time.LocalDateTime +import java.util.Calendar +import java.util.GregorianCalendar +import java.util.Locale +import java.util.Objects +import java.util.TimeZone +import java.util.concurrent.TimeUnit + +class DateTime { + private val timeZone: TimeZone + val millis: Long + + @JvmOverloads + constructor( + year: Int, + month: Int, + day: Int, + hour: Int = 0, + minute: Int = 0, + second: Int = 0, + millisecond: Int = 0, + timeZone: TimeZone = TimeZone.getDefault() + ) { + val gregorianCalendar = GregorianCalendar(timeZone) + gregorianCalendar[year, month - 1, day, hour, minute] = second + gregorianCalendar[Calendar.MILLISECOND] = millisecond + millis = gregorianCalendar.timeInMillis + this.timeZone = timeZone + } + + @JvmOverloads + constructor(timestamp: Long = currentTimeMillis(), timeZone: TimeZone = TimeZone.getDefault()) { + this.millis = timestamp + this.timeZone = timeZone + } + + private constructor(calendar: Calendar) : this(calendar.timeInMillis, calendar.timeZone) + + fun startOfDay(): DateTime = DateTime(millis.startOfDay()) + + fun startOfMinute(): DateTime = DateTime(millis.startOfMinute()) + + fun startOfSecond(): DateTime = DateTime(millis.startOfSecond()) + + fun endOfMinute(): DateTime = DateTime(millis.endOfMinute()) + + fun noon(): DateTime = DateTime(millis.noon()) + + fun endOfDay(): DateTime = DateTime(millis.endOfDay()) + + fun withMillisOfDay(millisOfDay: Int): DateTime = DateTime(millis.withMillisOfDay(millisOfDay)) + + val offset: Long + get() = timeZone.getOffset(millis).toLong() + + val millisOfDay: Int + get() = millis.millisOfDay + + val year: Int + get() = calendar[Calendar.YEAR] + + val monthOfYear: Int + get() = calendar[Calendar.MONTH] + 1 + + val dayOfMonth: Int + get() = calendar[Calendar.DATE] + + val dayOfWeek: Int + get() = calendar[Calendar.DAY_OF_WEEK] + + val hourOfDay: Int + get() = calendar[Calendar.HOUR_OF_DAY] + + val minuteOfHour: Int + get() = calendar[Calendar.MINUTE] + + val secondOfMinute: Int + get() = calendar[Calendar.SECOND] + + fun withYear(year: Int): DateTime { + return with(Calendar.YEAR, year) + } + + fun withMonthOfYear(monthOfYear: Int): DateTime { + return with(Calendar.MONTH, monthOfYear - 1) + } + + fun withDayOfMonth(dayOfMonth: Int): DateTime { + return with(Calendar.DAY_OF_MONTH, dayOfMonth) + } + + fun withHourOfDay(hourOfDay: Int): DateTime { + return with(Calendar.HOUR_OF_DAY, hourOfDay) + } + + fun withMinuteOfHour(minuteOfHour: Int): DateTime { + return with(Calendar.MINUTE, minuteOfHour) + } + + fun withSecondOfMinute(secondOfMinute: Int): DateTime { + return with(Calendar.SECOND, secondOfMinute) + } + + fun withMillisOfSecond(millisOfSecond: Int): DateTime { + return with(Calendar.MILLISECOND, millisOfSecond) + } + + fun plusMonths(interval: Int): DateTime { + return add(Calendar.MONTH, interval) + } + + fun plusWeeks(weeks: Int): DateTime { + return add(Calendar.WEEK_OF_MONTH, weeks) + } + + fun plusDays(interval: Int): DateTime { + return add(Calendar.DATE, interval) + } + + fun plusHours(hours: Int): DateTime { + return add(Calendar.HOUR_OF_DAY, hours) + } + + fun plusMinutes(minutes: Int): DateTime { + return add(Calendar.MINUTE, minutes) + } + + fun plusSeconds(seconds: Int): DateTime { + return add(Calendar.SECOND, seconds) + } + + fun plusMillis(millis: Int): DateTime { + return add(Calendar.MILLISECOND, millis) + } + + fun minusSeconds(seconds: Int): DateTime { + return subtract(Calendar.SECOND, seconds) + } + + fun minusDays(days: Int): DateTime { + return subtract(Calendar.DATE, days) + } + + fun minusMinutes(minutes: Int): DateTime { + return subtract(Calendar.MINUTE, minutes) + } + + fun minusMillis(millis: Long): DateTime { + return DateTime(this.millis - millis, timeZone) + } + + val isAfterNow: Boolean + get() = isAfter(currentTimeMillis()) + + fun isAfter(dateTime: DateTime): Boolean { + return isAfter(dateTime.millis) + } + + private fun isAfter(timestamp: Long): Boolean { + return this.millis > timestamp + } + + val isBeforeNow: Boolean + get() = millis < currentTimeMillis() + + fun isBefore(dateTime: DateTime): Boolean { + return millis < dateTime.millis + } + + fun toUTC(): DateTime { + return toTimeZone(UTC) + } + + fun toLocal(): DateTime { + return toTimeZone(TimeZone.getDefault()) + } + + val isLastDayOfMonth: Boolean + get() = dayOfMonth == numberOfDaysInMonth + + val numberOfDaysInMonth: Int + get() = calendar.getActualMaximum(Calendar.DAY_OF_MONTH) + + private fun toTimeZone(timeZone: TimeZone): DateTime { + if (timeZone === this.timeZone) { + return this + } + val current = calendar + val target: Calendar = GregorianCalendar(timeZone) + target.timeInMillis = current.timeInMillis + return DateTime(target) + } + + private fun with(field: Int, value: Int): DateTime { + val calendar = calendar + calendar[field] = value + return DateTime(calendar) + } + + private fun subtract(field: Int, value: Int): DateTime { + return add(field, -value) + } + + private fun add(field: Int, value: Int): DateTime { + val calendar = calendar + calendar.add(field, value) + return DateTime(calendar) + } + + private val calendar: Calendar + get() { + val calendar: Calendar = GregorianCalendar(timeZone) + calendar.timeInMillis = millis + return calendar + } + + fun toDateTime(): net.fortuna.ical4j.model.DateTime { + return if (millis == 0L) throw IllegalStateException() else net.fortuna.ical4j.model.DateTime(millis) + } + + fun toDate(): Date { + return if (millis == 0L) throw IllegalStateException() else Date(millis + offset) + } + + fun toLocalDate(): LocalDate? { + return if (millis == 0L) null else LocalDate.of(year, monthOfYear, dayOfMonth) + } + + fun toLocalDateTime(): LocalDateTime? { + return if (millis == 0L) null else LocalDateTime.of( + year, + monthOfYear, + dayOfMonth, + hourOfDay, + minuteOfHour + ) + } + + fun toAppleEpoch(): Long { + return millis.toAppleEpoch() + } + + val dayOfWeekInMonth: Int + get() = calendar[Calendar.DAY_OF_WEEK_IN_MONTH] + + val maxDayOfWeekInMonth: Int + get() = calendar.getActualMaximum(Calendar.DAY_OF_WEEK_IN_MONTH) + + val weekDay: WeekDay + get() { + when (calendar[Calendar.DAY_OF_WEEK]) { + Calendar.SUNDAY -> return WeekDay.SU + Calendar.MONDAY -> return WeekDay.MO + Calendar.TUESDAY -> return WeekDay.TU + Calendar.WEDNESDAY -> return WeekDay.WE + Calendar.THURSDAY -> return WeekDay.TH + Calendar.FRIDAY -> return WeekDay.FR + Calendar.SATURDAY -> return WeekDay.SA + } + throw RuntimeException() + } + + override fun equals(o: Any?): Boolean { + if (this === o) { + return true + } + if (o !is DateTime) { + return false + } + val dateTime = o + return millis == dateTime.millis && timeZone == dateTime.timeZone + } + + override fun hashCode(): Int { + return Objects.hash(timeZone, millis) + } + + fun toString(format: String?): String { + val calendar = calendar + val simpleDateFormat = + SimpleDateFormat(format, Locale.getDefault()) + simpleDateFormat.calendar = calendar + return simpleDateFormat.format(calendar.time) + } + + override fun toString(): String { + return toString("yyyy-MM-dd'T'HH:mm:ss.SSSZ") + } + + companion object { + val MAX_MILLIS_PER_DAY: Int = TimeUnit.DAYS.toMillis(1).toInt() - 1 + val UTC: TimeZone = TimeZone.getTimeZone("GMT") + + fun from(date: java.util.Date?): DateTime { + if (date == null) { + return DateTime(0) + } + val dateTime = DateTime(date.time) + return dateTime.minusMillis(dateTime.offset) + } + + fun from(date: Date?): DateTime { + if (date is net.fortuna.ical4j.model.DateTime) { + val dt = date + val tz: TimeZone? = dt.timeZone + return DateTime( + dt.time, + tz ?: if (dt.isUtc) UTC else TimeZone.getDefault() + ) + } else { + return from(date as java.util.Date?) + } + } + } +} diff --git a/app/src/main/java/org/tasks/time/DateTimeUtils.kt b/app/src/main/java/org/tasks/time/DateTimeUtils.kt index 634242041..8d50ade28 100644 --- a/app/src/main/java/org/tasks/time/DateTimeUtils.kt +++ b/app/src/main/java/org/tasks/time/DateTimeUtils.kt @@ -12,17 +12,5 @@ object DateTimeUtils { "%dh %dm %ds", seconds / 3600L, (seconds % 3600L / 60L).toInt(), (seconds % 60L).toInt()) } else millis.toString() - @JvmStatic - fun Long.startOfDay(): Long = if (this > 0) toDateTime().startOfDay().millis else 0 - - fun Long.startOfMinute(): Long = if (this > 0) toDateTime().startOfMinute().millis else 0 - - fun Long.startOfSecond(): Long = if (this > 0) toDateTime().startOfSecond().millis else 0 - - fun Long.millisOfDay(): Int = if (this > 0) toDateTime().millisOfDay else 0 - fun Long.toDate(): net.fortuna.ical4j.model.Date? = this.toDateTime().toDate() - - fun Long.withMillisOfDay(millisOfDay: Int): Long = - if (this > 0) toDateTime().withMillisOfDay(millisOfDay).millis else 0 -} \ No newline at end of file +} diff --git a/app/src/main/java/org/tasks/ui/TaskEditViewModel.kt b/app/src/main/java/org/tasks/ui/TaskEditViewModel.kt index 3da4fd0d9..83212274f 100644 --- a/app/src/main/java/org/tasks/ui/TaskEditViewModel.kt +++ b/app/src/main/java/org/tasks/ui/TaskEditViewModel.kt @@ -9,8 +9,6 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.todoroo.astrid.activity.TaskEditFragment import com.todoroo.astrid.alarms.AlarmService -import org.tasks.filters.CaldavFilter -import org.tasks.filters.GtasksFilter import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.gcal.GCalHelper import com.todoroo.astrid.service.TaskCompleter @@ -59,12 +57,14 @@ import org.tasks.data.entity.UserActivity import org.tasks.data.setPicture import org.tasks.date.DateTimeUtils.toDateTime import org.tasks.files.FileHelper +import org.tasks.filters.CaldavFilter import org.tasks.filters.Filter +import org.tasks.filters.GtasksFilter import org.tasks.location.GeofenceApi import org.tasks.preferences.PermissionChecker import org.tasks.preferences.Preferences -import org.tasks.time.DateTimeUtils.startOfDay import org.tasks.time.DateTimeUtils2.currentTimeMillis +import org.tasks.time.startOfDay import timber.log.Timber import javax.inject.Inject diff --git a/app/src/main/java/org/tasks/widget/TasksWidgetViewFactory.kt b/app/src/main/java/org/tasks/widget/TasksWidgetViewFactory.kt index b7c3f518f..050fa7cb7 100644 --- a/app/src/main/java/org/tasks/widget/TasksWidgetViewFactory.kt +++ b/app/src/main/java/org/tasks/widget/TasksWidgetViewFactory.kt @@ -29,8 +29,8 @@ import org.tasks.markdown.Markdown import org.tasks.tasklist.HeaderFormatter import org.tasks.tasklist.SectionedDataSource import org.tasks.themes.ColorProvider.Companion.priorityColor -import org.tasks.time.DateTimeUtils.startOfDay import org.tasks.time.DateTimeUtils2.currentTimeMillis +import org.tasks.time.startOfDay import org.tasks.ui.CheckBoxProvider.Companion.getCheckboxRes import timber.log.Timber import java.time.format.FormatStyle diff --git a/app/src/main/java/org/tasks/widget/WidgetChipProvider.kt b/app/src/main/java/org/tasks/widget/WidgetChipProvider.kt index 73c6ec0af..155d548b2 100644 --- a/app/src/main/java/org/tasks/widget/WidgetChipProvider.kt +++ b/app/src/main/java/org/tasks/widget/WidgetChipProvider.kt @@ -4,9 +4,6 @@ import android.content.Context import android.widget.RemoteViews import androidx.annotation.ColorInt import com.todoroo.andlib.utility.DateUtilities -import org.tasks.filters.CaldavFilter -import org.tasks.filters.GtasksFilter -import org.tasks.filters.TagFilter import dagger.hilt.android.qualifiers.ApplicationContext import org.tasks.BuildConfig import org.tasks.R @@ -15,10 +12,13 @@ import org.tasks.data.entity.Task import org.tasks.data.isHidden import org.tasks.date.DateTimeUtils.toDateTime import org.tasks.extensions.setColorFilter +import org.tasks.filters.CaldavFilter import org.tasks.filters.Filter +import org.tasks.filters.GtasksFilter import org.tasks.filters.PlaceFilter +import org.tasks.filters.TagFilter import org.tasks.themes.CustomIcons -import org.tasks.time.DateTimeUtils.startOfDay +import org.tasks.time.startOfDay import org.tasks.ui.ChipListCache import java.time.format.FormatStyle import java.util.Locale diff --git a/app/src/test/java/org/tasks/caldav/extensions/VAlarmTests.kt b/app/src/test/java/org/tasks/caldav/extensions/VAlarmTests.kt index 097db47d7..38d1f54f5 100644 --- a/app/src/test/java/org/tasks/caldav/extensions/VAlarmTests.kt +++ b/app/src/test/java/org/tasks/caldav/extensions/VAlarmTests.kt @@ -7,7 +7,7 @@ import org.tasks.data.entity.Alarm.Companion.TYPE_DATE_TIME import org.tasks.data.entity.Alarm.Companion.TYPE_REL_END import org.tasks.data.entity.Alarm.Companion.TYPE_REL_START import org.tasks.time.DateTime -import org.tasks.time.DateTime.UTC +import org.tasks.time.DateTime.Companion.UTC import java.util.concurrent.TimeUnit.HOURS import java.util.concurrent.TimeUnit.MINUTES diff --git a/data/build.gradle.kts b/data/build.gradle.kts index 62840805f..ee3318e62 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -26,6 +26,7 @@ kotlin { sourceSets { commonMain.dependencies { implementation(libs.androidx.room) + implementation(libs.kotlinx.datetime) implementation(libs.kotlinx.serialization) implementation(libs.kermit) } diff --git a/data/src/androidMain/kotlin/org/tasks/Platform.android.kt b/data/src/androidMain/kotlin/org/tasks/Platform.android.kt index e0fd3f3c7..a689aab6a 100644 --- a/data/src/androidMain/kotlin/org/tasks/Platform.android.kt +++ b/data/src/androidMain/kotlin/org/tasks/Platform.android.kt @@ -5,9 +5,12 @@ package org.tasks import android.os.Parcelable import kotlinx.parcelize.RawValue import org.tasks.data.BuildConfig +import java.util.Date actual typealias CommonParcelable = Parcelable actual typealias CommonRawValue = RawValue -actual val IS_DEBUG = BuildConfig.DEBUG \ No newline at end of file +actual val IS_DEBUG = BuildConfig.DEBUG + +actual fun Long.printTimestamp(): String = Date(this).toString() \ No newline at end of file diff --git a/data/src/commonMain/kotlin/org/tasks/Platform.common.kt b/data/src/commonMain/kotlin/org/tasks/Platform.kt similarity index 82% rename from data/src/commonMain/kotlin/org/tasks/Platform.common.kt rename to data/src/commonMain/kotlin/org/tasks/Platform.kt index 1be411992..07daef4de 100644 --- a/data/src/commonMain/kotlin/org/tasks/Platform.common.kt +++ b/data/src/commonMain/kotlin/org/tasks/Platform.kt @@ -12,4 +12,6 @@ expect annotation class CommonRawValue() expect interface CommonParcelable -expect val IS_DEBUG: Boolean \ No newline at end of file +expect val IS_DEBUG: Boolean + +expect fun Long.printTimestamp(): String \ No newline at end of file diff --git a/data/src/commonMain/kotlin/org/tasks/data/dao/CaldavDao.kt b/data/src/commonMain/kotlin/org/tasks/data/dao/CaldavDao.kt index e0a1e43e7..775db07b8 100644 --- a/data/src/commonMain/kotlin/org/tasks/data/dao/CaldavDao.kt +++ b/data/src/commonMain/kotlin/org/tasks/data/dao/CaldavDao.kt @@ -389,6 +389,6 @@ ORDER BY primary_sort companion object { const val LOCAL = "local" - @JvmStatic fun Long.toAppleEpoch(): Long = (this - APPLE_EPOCH) / 1000 + fun Long.toAppleEpoch(): Long = (this - APPLE_EPOCH) / 1000 } } \ No newline at end of file diff --git a/data/src/commonMain/kotlin/org/tasks/time/DateTimeUtils.kt b/data/src/commonMain/kotlin/org/tasks/time/DateTimeUtils.kt index fa84245fb..4ff7b5b5b 100644 --- a/data/src/commonMain/kotlin/org/tasks/time/DateTimeUtils.kt +++ b/data/src/commonMain/kotlin/org/tasks/time/DateTimeUtils.kt @@ -1,7 +1,7 @@ package org.tasks.time import org.tasks.IS_DEBUG -import java.util.Date +import org.tasks.printTimestamp object DateTimeUtils2 { @JvmStatic @@ -24,4 +24,4 @@ object DateTimeUtils2 { } fun printTimestamp(timestamp: Long): String = - if (IS_DEBUG) Date(timestamp).toString() else timestamp.toString() \ No newline at end of file + if (IS_DEBUG) timestamp.printTimestamp() else timestamp.toString() diff --git a/data/src/commonMain/kotlin/org/tasks/time/SystemMillisProvider.kt b/data/src/commonMain/kotlin/org/tasks/time/SystemMillisProvider.kt index f7a8707f2..ec40bb2ec 100644 --- a/data/src/commonMain/kotlin/org/tasks/time/SystemMillisProvider.kt +++ b/data/src/commonMain/kotlin/org/tasks/time/SystemMillisProvider.kt @@ -1,6 +1,8 @@ package org.tasks.time +import kotlinx.datetime.Clock + class SystemMillisProvider : MillisProvider { override val millis: Long - get() = System.currentTimeMillis() + get() = Clock.System.now().toEpochMilliseconds() } diff --git a/data/src/jvmMain/kotlin/org/tasks/Platform.jvm.kt b/data/src/jvmMain/kotlin/org/tasks/Platform.jvm.kt index 1439942f5..0db807cf4 100644 --- a/data/src/jvmMain/kotlin/org/tasks/Platform.jvm.kt +++ b/data/src/jvmMain/kotlin/org/tasks/Platform.jvm.kt @@ -11,3 +11,5 @@ annotation class RawValue actual typealias CommonRawValue = RawValue actual val IS_DEBUG = false + +actual fun Long.printTimestamp(): String = this.toString() diff --git a/deps_fdroid.txt b/deps_fdroid.txt index 48fd2f937..ae811c9bc 100644 --- a/deps_fdroid.txt +++ b/deps_fdroid.txt @@ -257,6 +257,9 @@ +| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3 -> 1.8.0 (*) +| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -> 1.8.0 (*) +| | \--- androidx.room:room-common:2.7.0-alpha04 (c) ++| +--- org.jetbrains.kotlinx:kotlinx-datetime:0.6.0 ++| | \--- org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.6.0 ++| | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.21 -> 2.0.0 (*) +| +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.0 +| | \--- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.7.0 +| | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*) @@ -611,6 +614,7 @@ +| | \--- androidx.compose.material:material-ripple:1.6.8 (c) +| +--- org.jetbrains.compose.runtime:runtime:1.6.11 (*) +| +--- co.touchlab:kermit:2.0.4 (*) ++| +--- org.jetbrains.kotlinx:kotlinx-datetime:0.6.0 (*) +| \--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.0 (*) ++--- com.github.bitfireAT:dav4jvm:2.2.1 +| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.20 -> 2.0.0 (*) diff --git a/deps_googleplay.txt b/deps_googleplay.txt index bd3c2f743..7f8fb9a21 100644 --- a/deps_googleplay.txt +++ b/deps_googleplay.txt @@ -626,6 +626,9 @@ +| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3 -> 1.8.0 (*) +| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -> 1.8.0 (*) +| | \--- androidx.room:room-common:2.7.0-alpha04 (c) ++| +--- org.jetbrains.kotlinx:kotlinx-datetime:0.6.0 ++| | \--- org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.6.0 ++| | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.21 -> 2.0.0 (*) +| +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.0 +| | \--- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.7.0 +| | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*) @@ -920,6 +923,7 @@ +| | \--- androidx.compose.material:material-ripple:1.6.8 (c) +| +--- org.jetbrains.compose.runtime:runtime:1.6.11 (*) +| +--- co.touchlab:kermit:2.0.4 (*) ++| +--- org.jetbrains.kotlinx:kotlinx-datetime:0.6.0 (*) +| \--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.0 (*) ++--- com.github.bitfireAT:dav4jvm:2.2.1 +| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.20 -> 2.0.0 (*) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0f4eefc91..779391166 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -127,6 +127,7 @@ kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version. kotlin-jdk8 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" } kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" } +kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version = "0.6.0" } kotlinx-immutable = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version = "0.3.7" } kotlinx-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.7.0" } leakcanary = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanary" } diff --git a/kmp/build.gradle.kts b/kmp/build.gradle.kts index 01bd095fa..169840d27 100644 --- a/kmp/build.gradle.kts +++ b/kmp/build.gradle.kts @@ -32,6 +32,7 @@ kotlin { implementation(compose.materialIconsExtended) implementation(compose.runtime) implementation(libs.kermit) + implementation(libs.kotlinx.datetime) } } task("testClasses") diff --git a/kmp/src/commonMain/kotlin/org/tasks/time/LongExtensions.kt b/kmp/src/commonMain/kotlin/org/tasks/time/LongExtensions.kt new file mode 100644 index 000000000..4c2f25a7c --- /dev/null +++ b/kmp/src/commonMain/kotlin/org/tasks/time/LongExtensions.kt @@ -0,0 +1,102 @@ +package org.tasks.time + +import kotlinx.datetime.Instant +import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.LocalTime +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toInstant +import kotlinx.datetime.toLocalDateTime + +fun Long.noon(): Long = + if (this > 0) { + toLocalDateTime() + .let { LocalDateTime(it.year, it.month, it.dayOfMonth, 12, 0, 0, 0) } + .toEpochMilliseconds() + } else { + 0 + } + +fun Long.startOfDay(): Long = + if (this > 0) { + toLocalDateTime() + .let { LocalDateTime(it.year, it.month, it.dayOfMonth, 0, 0, 0, 0) } + .toEpochMilliseconds() + } else { + 0 + } + +fun Long.startOfMinute(): Long = + if (this > 0) { + toLocalDateTime() + .let { LocalDateTime(it.year, it.month, it.dayOfMonth, it.hour, it.minute, 0, 0) } + .toEpochMilliseconds() + } else { + 0 + } + +fun Long.startOfSecond(): Long = + if (this > 0) { + toLocalDateTime() + .let { + LocalDateTime( + it.year, + it.month, + it.dayOfMonth, + it.hour, + it.minute, + it.second, + 0 + ) + } + .toEpochMilliseconds() + } else { + 0 + } + +fun Long.endOfMinute(): Long = + if (this > 0) { + toLocalDateTime() + .let { + LocalDateTime( + it.year, + it.month, + it.dayOfMonth, + it.hour, + it.minute, + 59, + 999_000_000 + ) + } + .toEpochMilliseconds() + } else { + 0 + } + +fun Long.endOfDay(): Long = + if (this > 0) { + toLocalDateTime() + .let { LocalDateTime(it.year, it.month, it.dayOfMonth, 23, 59, 59, 0) } + .toEpochMilliseconds() + } else { + 0 + } + +fun Long.withMillisOfDay(millisOfDay: Int): Long = + if (this > 0) { + LocalDateTime( + date = toLocalDateTime().date, + time = LocalTime.fromMillisecondOfDay(millisOfDay) + ) + .toEpochMilliseconds() + } else { + 0 + } + +val Long.millisOfDay: Int + get() = if (this > 0) toLocalDateTime().time.toMillisecondOfDay() else 0 + +private fun Long.toLocalDateTime(): LocalDateTime = + Instant.fromEpochMilliseconds(this).toLocalDateTime(TimeZone.currentSystemDefault()) + +private fun LocalDateTime.toEpochMilliseconds(): Long = + toInstant(TimeZone.currentSystemDefault()).toEpochMilliseconds()