Use kotlinx.datetime

pull/2910/head
Alex Baker 1 week ago
parent b990716502
commit bf4167651b

@ -14,17 +14,17 @@ import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.service.TaskMover import com.todoroo.astrid.service.TaskMover
import org.tasks.BuildConfig import org.tasks.BuildConfig
import org.tasks.LocalBroadcastManager 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
import org.tasks.data.dao.CaldavDao.Companion.toAppleEpoch import org.tasks.data.dao.CaldavDao.Companion.toAppleEpoch
import org.tasks.data.entity.CaldavTask
import org.tasks.data.dao.GoogleTaskDao import org.tasks.data.dao.GoogleTaskDao
import org.tasks.data.entity.CaldavTask
import org.tasks.data.entity.Task import org.tasks.data.entity.Task
import org.tasks.data.entity.Task.Companion.HIDE_UNTIL_SPECIFIC_DAY 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.date.DateTimeUtils.toDateTime
import org.tasks.time.DateTimeUtils.millisOfDay import org.tasks.time.millisOfDay
open class TaskAdapter( open class TaskAdapter(
private val newTasksOnTop: Boolean, private val newTasksOnTop: Boolean,
@ -206,7 +206,7 @@ open class TaskAdapter(
val original = task.dueDate val original = task.dueDate
task.setDueDateAdjustingHideUntil(when { task.setDueDateAdjustingHideUntil(when {
date == 0L -> 0L 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) else -> createDueDate(Task.URGENCY_SPECIFIC_DAY, date)
}) })
if (original != task.dueDate) { if (original != task.dueDate) {
@ -218,7 +218,7 @@ open class TaskAdapter(
val original = task.hideUntil val original = task.hideUntil
task.hideUntil = when { task.hideUntil = when {
date == 0L -> 0L 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) else -> task.createHideUntil(HIDE_UNTIL_SPECIFIC_DAY, date)
} }
if (original != task.hideUntil) { if (original != task.hideUntil) {

@ -5,7 +5,7 @@ import org.tasks.data.entity.Notification
import org.tasks.data.entity.Task import org.tasks.data.entity.Task
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.reminders.Random import org.tasks.reminders.Random
import org.tasks.time.DateTimeUtils.withMillisOfDay import org.tasks.time.withMillisOfDay
import javax.inject.Inject import javax.inject.Inject
class AlarmCalculator( class AlarmCalculator(

@ -29,6 +29,7 @@ import org.tasks.repeats.RepeatRuleToString
import org.tasks.themes.TasksTheme import org.tasks.themes.TasksTheme
import org.tasks.time.DateTime import org.tasks.time.DateTime
import org.tasks.time.DateTimeUtils2.currentTimeMillis import org.tasks.time.DateTimeUtils2.currentTimeMillis
import org.tasks.time.startOfDay
import org.tasks.ui.TaskEditControlFragment import org.tasks.ui.TaskEditControlFragment
import javax.inject.Inject import javax.inject.Inject
@ -43,7 +44,7 @@ class RepeatControlSet : TaskEditControlFragment() {
val result = data?.getStringExtra(BasicRecurrenceDialog.EXTRA_RRULE) val result = data?.getStringExtra(BasicRecurrenceDialog.EXTRA_RRULE)
viewModel.recurrence.value = result viewModel.recurrence.value = result
if (result?.isNotBlank() == true && viewModel.dueDate.value == 0L) { if (result?.isNotBlank() == true && viewModel.dueDate.value == 0L) {
viewModel.setDueDate(DateTime().startOfDay().millis) viewModel.setDueDate(currentTimeMillis().startOfDay())
} }
} }
} else { } else {

@ -1,8 +1,6 @@
package com.todoroo.astrid.service package com.todoroo.astrid.service
import com.todoroo.andlib.utility.DateUtilities 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.api.PermaSql
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.gcal.GCalHelper 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
import org.tasks.data.entity.Task.Companion.HIDE_UNTIL_NONE import org.tasks.data.entity.Task.Companion.HIDE_UNTIL_NONE
import org.tasks.data.entity.Task.Companion.IMPORTANCE import org.tasks.data.entity.Task.Companion.IMPORTANCE
import org.tasks.filters.CaldavFilter
import org.tasks.filters.Filter import org.tasks.filters.Filter
import org.tasks.filters.GtasksFilter
import org.tasks.filters.mapFromSerializedString import org.tasks.filters.mapFromSerializedString
import org.tasks.preferences.DefaultFilterProvider import org.tasks.preferences.DefaultFilterProvider
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.time.DateTimeUtils.startOfDay
import org.tasks.time.DateTimeUtils2.currentTimeMillis import org.tasks.time.DateTimeUtils2.currentTimeMillis
import org.tasks.time.startOfDay
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject

@ -1,12 +1,12 @@
package com.todoroo.astrid.ui package com.todoroo.astrid.ui
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import org.tasks.data.entity.Task
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import org.tasks.R import org.tasks.R
import org.tasks.data.entity.Task
import org.tasks.date.DateTimeUtils.toDateTime import org.tasks.date.DateTimeUtils.toDateTime
import org.tasks.dialogs.StartDatePicker import org.tasks.dialogs.StartDatePicker
import org.tasks.dialogs.StartDatePicker.Companion.DAY_BEFORE_DUE 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.DUE_TIME
import org.tasks.dialogs.StartDatePicker.Companion.WEEK_BEFORE_DUE import org.tasks.dialogs.StartDatePicker.Companion.WEEK_BEFORE_DUE
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.time.DateTimeUtils.millisOfDay import org.tasks.time.millisOfDay
import org.tasks.time.DateTimeUtils.startOfDay import org.tasks.time.startOfDay
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
@ -32,7 +32,7 @@ class StartDateViewModel @Inject constructor(
fun init(dueDate: Long, startDate: Long, isNew: Boolean) { fun init(dueDate: Long, startDate: Long, isNew: Boolean) {
val dueDay = dueDate.startOfDay() val dueDay = dueDate.startOfDay()
val dueTime = dueDate.millisOfDay() val dueTime = dueDate.millisOfDay
val hideUntil = startDate.takeIf { it > 0 }?.toDateTime() val hideUntil = startDate.takeIf { it > 0 }?.toDateTime()
if (hideUntil == null) { if (hideUntil == null) {
if (isNew) { if (isNew) {

@ -57,10 +57,10 @@ import org.tasks.location.GeofenceApi
import org.tasks.notifications.NotificationManager import org.tasks.notifications.NotificationManager
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.repeats.RecurrenceUtils.newRRule 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.DateTimeUtils.toDate
import org.tasks.time.DateTimeUtils2.currentTimeMillis import org.tasks.time.DateTimeUtils2.currentTimeMillis
import org.tasks.time.startOfDay
import org.tasks.time.startOfMinute
import timber.log.Timber import timber.log.Timber
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.StringReader import java.io.StringReader

@ -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.entity.Task.Priority.Companion.NONE
import org.tasks.data.setRecurrence import org.tasks.data.setRecurrence
import org.tasks.date.DateTimeUtils.newDateTime import org.tasks.date.DateTimeUtils.newDateTime
import org.tasks.time.DateTime.UTC import org.tasks.time.DateTime.Companion.UTC
import org.tasks.time.DateTimeUtils.startOfSecond
import org.tasks.time.DateTimeUtils2.currentTimeMillis import org.tasks.time.DateTimeUtils2.currentTimeMillis
import org.tasks.time.startOfSecond
fun org.tasks.data.entity.Task.applyRemote( fun org.tasks.data.entity.Task.applyRemote(
remote: Task, remote: Task,

@ -6,10 +6,10 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import com.todoroo.andlib.utility.DateUtilities import com.todoroo.andlib.utility.DateUtilities
import org.tasks.data.entity.Task
import org.tasks.R import org.tasks.R
import org.tasks.data.entity.Task
import org.tasks.date.DateTimeUtils.toDateTime import org.tasks.date.DateTimeUtils.toDateTime
import org.tasks.time.DateTimeUtils.startOfDay import org.tasks.time.startOfDay
import java.time.format.FormatStyle import java.time.format.FormatStyle
@Composable @Composable

@ -1,12 +1,12 @@
package org.tasks.data package org.tasks.data
import com.todoroo.andlib.utility.DateUtilities import com.todoroo.andlib.utility.DateUtilities
import org.tasks.data.entity.Task
import net.fortuna.ical4j.model.Recur import net.fortuna.ical4j.model.Recur
import org.tasks.data.entity.Task
import org.tasks.date.DateTimeUtils import org.tasks.date.DateTimeUtils
import org.tasks.date.DateTimeUtils.toDateTime import org.tasks.date.DateTimeUtils.toDateTime
import org.tasks.time.DateTimeUtils.startOfDay
import org.tasks.time.DateTimeUtils2.currentTimeMillis import org.tasks.time.DateTimeUtils2.currentTimeMillis
import org.tasks.time.startOfDay
/** Checks whether task is hidden. Requires HIDDEN_UNTIL */ /** Checks whether task is hidden. Requires HIDDEN_UNTIL */
val Task.isHidden val Task.isHidden
@ -44,7 +44,7 @@ val Task.isOverdue: Boolean
if (isCompleted || !hasDueDate()) { if (isCompleted || !hasDueDate()) {
return false return false
} }
val compareTo = if (hasDueTime()) currentTimeMillis() else DateTimeUtils.newDateTime().startOfDay().millis val compareTo = if (hasDueTime()) currentTimeMillis() else currentTimeMillis().startOfDay()
return dueDate < compareTo return dueDate < compareTo
} }

@ -1,7 +1,7 @@
package org.tasks.date package org.tasks.date
import org.tasks.time.DateTime import org.tasks.time.DateTime
import org.tasks.time.DateTime.UTC import org.tasks.time.DateTime.Companion.UTC
import java.util.TimeZone import java.util.TimeZone
object DateTimeUtils { object DateTimeUtils {

@ -16,16 +16,16 @@ import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.tasks.R import org.tasks.R
import org.tasks.data.entity.Task
import org.tasks.data.createDueDate import org.tasks.data.createDueDate
import org.tasks.data.entity.Task
import org.tasks.databinding.DialogDateTimePickerBinding import org.tasks.databinding.DialogDateTimePickerBinding
import org.tasks.date.DateTimeUtils.newDateTime import org.tasks.date.DateTimeUtils.newDateTime
import org.tasks.date.DateTimeUtils.toDateTime import org.tasks.date.DateTimeUtils.toDateTime
import org.tasks.dialogs.MyTimePickerDialog.Companion.newTimePicker import org.tasks.dialogs.MyTimePickerDialog.Companion.newTimePicker
import org.tasks.notifications.NotificationManager import org.tasks.notifications.NotificationManager
import org.tasks.time.DateTime import org.tasks.time.DateTime
import org.tasks.time.DateTimeUtils.millisOfDay import org.tasks.time.millisOfDay
import org.tasks.time.DateTimeUtils.startOfDay import org.tasks.time.startOfDay
import java.time.format.FormatStyle import java.time.format.FormatStyle
import java.util.Calendar.FRIDAY import java.util.Calendar.FRIDAY
import java.util.Calendar.MONDAY import java.util.Calendar.MONDAY
@ -78,7 +78,7 @@ class DateTimePicker : BaseDateTimePicker() {
): DateTimePicker { ): DateTimePicker {
val fragment = DateTimePicker() val fragment = DateTimePicker()
val dueDates = tasks.map { it.dueDate.startOfDay() }.toSet() 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 { fragment.arguments = Bundle().apply {
putLongArray(EXTRA_TASKS, tasks.map { it.id }.toLongArray()) putLongArray(EXTRA_TASKS, tasks.map { it.id }.toLongArray())
putLong(EXTRA_DAY, if (dueDates.size == 1) dueDates.first() else MULTIPLE_DAYS) putLong(EXTRA_DAY, if (dueDates.size == 1) dueDates.first() else MULTIPLE_DAYS)
@ -99,7 +99,7 @@ class DateTimePicker : BaseDateTimePicker() {
val fragment = DateTimePicker() val fragment = DateTimePicker()
fragment.arguments = Bundle().apply { fragment.arguments = Bundle().apply {
putLong(EXTRA_DAY, current.startOfDay()) putLong(EXTRA_DAY, current.startOfDay())
putInt(EXTRA_TIME, current.millisOfDay()) putInt(EXTRA_TIME, current.millisOfDay)
putBoolean(EXTRA_AUTO_CLOSE, autoClose) putBoolean(EXTRA_AUTO_CLOSE, autoClose)
putBoolean(EXTRA_HIDE_NO_DATE, hideNoDate) putBoolean(EXTRA_HIDE_NO_DATE, hideNoDate)
} }
@ -267,7 +267,7 @@ class DateTimePicker : BaseDateTimePicker() {
selectedDay selectedDay
} }
val time = if (selectedTime == MULTIPLE_TIMES) { 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 { } else {
selectedTime selectedTime
} }

@ -12,8 +12,8 @@ import dagger.hilt.android.AndroidEntryPoint
import org.tasks.R import org.tasks.R
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.time.DateTime import org.tasks.time.DateTime
import org.tasks.time.DateTimeUtils.startOfDay
import org.tasks.time.DateTimeUtils2.currentTimeMillis import org.tasks.time.DateTimeUtils2.currentTimeMillis
import org.tasks.time.startOfDay
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint

@ -18,8 +18,8 @@ import org.tasks.R
import org.tasks.date.DateTimeUtils.toDateTime import org.tasks.date.DateTimeUtils.toDateTime
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.time.DateTime import org.tasks.time.DateTime
import org.tasks.time.DateTimeUtils.startOfDay
import org.tasks.time.DateTimeUtils2.currentTimeMillis import org.tasks.time.DateTimeUtils2.currentTimeMillis
import org.tasks.time.startOfDay
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint

@ -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_DATE
import org.tasks.repeats.CustomRecurrenceActivity.Companion.EXTRA_RRULE import org.tasks.repeats.CustomRecurrenceActivity.Companion.EXTRA_RRULE
import org.tasks.time.DateTime import org.tasks.time.DateTime
import org.tasks.time.DateTimeUtils.startOfDay
import org.tasks.time.DateTimeUtils2.currentTimeMillis import org.tasks.time.DateTimeUtils2.currentTimeMillis
import org.tasks.time.startOfDay
import java.time.DayOfWeek import java.time.DayOfWeek
import java.time.Instant import java.time.Instant
import java.time.ZoneId import java.time.ZoneId
@ -42,7 +42,7 @@ class CustomRecurrenceViewModel @Inject constructor(
data class ViewState( data class ViewState(
val interval: Int = 1, val interval: Int = 1,
val frequency: Recur.Frequency = WEEKLY, val frequency: Recur.Frequency = WEEKLY,
val dueDate: Long = DateTime().startOfDay().millis, val dueDate: Long = currentTimeMillis().startOfDay(),
val endSelection: Int = 0, val endSelection: Int = 0,
val endDate: Long = dueDate.toDateTime().plusMonths(1).startOfDay().millis, val endDate: Long = dueDate.toDateTime().plusMonths(1).startOfDay().millis,
val endCount: Int = 1, val endCount: Int = 1,

@ -4,8 +4,8 @@ import android.util.SparseArray
import androidx.core.util.forEach import androidx.core.util.forEach
import com.todoroo.astrid.core.SortHelper import com.todoroo.astrid.core.SortHelper
import org.tasks.data.TaskContainer import org.tasks.data.TaskContainer
import org.tasks.time.DateTimeUtils.startOfDay
import org.tasks.time.DateTimeUtils2.currentTimeMillis import org.tasks.time.DateTimeUtils2.currentTimeMillis
import org.tasks.time.startOfDay
import org.tasks.ui.TaskListViewModel.UiItem import org.tasks.ui.TaskListViewModel.UiItem
class SectionedDataSource( class SectionedDataSource(

@ -14,9 +14,6 @@ import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.todoroo.andlib.utility.DateUtilities 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_DUE
import com.todoroo.astrid.core.SortHelper.SORT_LIST import com.todoroo.astrid.core.SortHelper.SORT_LIST
import com.todoroo.astrid.core.SortHelper.SORT_START 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.databinding.TaskAdapterRowBinding
import org.tasks.date.DateTimeUtils.newDateTime import org.tasks.date.DateTimeUtils.newDateTime
import org.tasks.dialogs.Linkify import org.tasks.dialogs.Linkify
import org.tasks.filters.CaldavFilter
import org.tasks.filters.Filter import org.tasks.filters.Filter
import org.tasks.filters.GtasksFilter
import org.tasks.filters.PlaceFilter import org.tasks.filters.PlaceFilter
import org.tasks.filters.TagFilter
import org.tasks.markdown.Markdown import org.tasks.markdown.Markdown
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.themes.TasksTheme import org.tasks.themes.TasksTheme
import org.tasks.time.DateTimeUtils.startOfDay
import org.tasks.time.DateTimeUtils2.currentTimeMillis import org.tasks.time.DateTimeUtils2.currentTimeMillis
import org.tasks.time.startOfDay
import org.tasks.ui.CheckBoxProvider import org.tasks.ui.CheckBoxProvider
import org.tasks.ui.ChipProvider import org.tasks.ui.ChipProvider
import java.time.format.FormatStyle import java.time.format.FormatStyle

@ -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");
}
}

@ -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?)
}
}
}
}

@ -12,17 +12,5 @@ object DateTimeUtils {
"%dh %dm %ds", seconds / 3600L, (seconds % 3600L / 60L).toInt(), (seconds % 60L).toInt()) "%dh %dm %ds", seconds / 3600L, (seconds % 3600L / 60L).toInt(), (seconds % 60L).toInt())
} else millis.toString() } 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.toDate(): net.fortuna.ical4j.model.Date? = this.toDateTime().toDate()
}
fun Long.withMillisOfDay(millisOfDay: Int): Long =
if (this > 0) toDateTime().withMillisOfDay(millisOfDay).millis else 0
}

@ -9,8 +9,6 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.todoroo.astrid.activity.TaskEditFragment import com.todoroo.astrid.activity.TaskEditFragment
import com.todoroo.astrid.alarms.AlarmService 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.dao.TaskDao
import com.todoroo.astrid.gcal.GCalHelper import com.todoroo.astrid.gcal.GCalHelper
import com.todoroo.astrid.service.TaskCompleter import com.todoroo.astrid.service.TaskCompleter
@ -59,12 +57,14 @@ import org.tasks.data.entity.UserActivity
import org.tasks.data.setPicture import org.tasks.data.setPicture
import org.tasks.date.DateTimeUtils.toDateTime import org.tasks.date.DateTimeUtils.toDateTime
import org.tasks.files.FileHelper import org.tasks.files.FileHelper
import org.tasks.filters.CaldavFilter
import org.tasks.filters.Filter import org.tasks.filters.Filter
import org.tasks.filters.GtasksFilter
import org.tasks.location.GeofenceApi import org.tasks.location.GeofenceApi
import org.tasks.preferences.PermissionChecker import org.tasks.preferences.PermissionChecker
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.time.DateTimeUtils.startOfDay
import org.tasks.time.DateTimeUtils2.currentTimeMillis import org.tasks.time.DateTimeUtils2.currentTimeMillis
import org.tasks.time.startOfDay
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject

@ -29,8 +29,8 @@ import org.tasks.markdown.Markdown
import org.tasks.tasklist.HeaderFormatter import org.tasks.tasklist.HeaderFormatter
import org.tasks.tasklist.SectionedDataSource import org.tasks.tasklist.SectionedDataSource
import org.tasks.themes.ColorProvider.Companion.priorityColor import org.tasks.themes.ColorProvider.Companion.priorityColor
import org.tasks.time.DateTimeUtils.startOfDay
import org.tasks.time.DateTimeUtils2.currentTimeMillis import org.tasks.time.DateTimeUtils2.currentTimeMillis
import org.tasks.time.startOfDay
import org.tasks.ui.CheckBoxProvider.Companion.getCheckboxRes import org.tasks.ui.CheckBoxProvider.Companion.getCheckboxRes
import timber.log.Timber import timber.log.Timber
import java.time.format.FormatStyle import java.time.format.FormatStyle

@ -4,9 +4,6 @@ import android.content.Context
import android.widget.RemoteViews import android.widget.RemoteViews
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import com.todoroo.andlib.utility.DateUtilities 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 dagger.hilt.android.qualifiers.ApplicationContext
import org.tasks.BuildConfig import org.tasks.BuildConfig
import org.tasks.R import org.tasks.R
@ -15,10 +12,13 @@ import org.tasks.data.entity.Task
import org.tasks.data.isHidden import org.tasks.data.isHidden
import org.tasks.date.DateTimeUtils.toDateTime import org.tasks.date.DateTimeUtils.toDateTime
import org.tasks.extensions.setColorFilter import org.tasks.extensions.setColorFilter
import org.tasks.filters.CaldavFilter
import org.tasks.filters.Filter import org.tasks.filters.Filter
import org.tasks.filters.GtasksFilter
import org.tasks.filters.PlaceFilter import org.tasks.filters.PlaceFilter
import org.tasks.filters.TagFilter
import org.tasks.themes.CustomIcons import org.tasks.themes.CustomIcons
import org.tasks.time.DateTimeUtils.startOfDay import org.tasks.time.startOfDay
import org.tasks.ui.ChipListCache import org.tasks.ui.ChipListCache
import java.time.format.FormatStyle import java.time.format.FormatStyle
import java.util.Locale import java.util.Locale

@ -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_END
import org.tasks.data.entity.Alarm.Companion.TYPE_REL_START import org.tasks.data.entity.Alarm.Companion.TYPE_REL_START
import org.tasks.time.DateTime 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.HOURS
import java.util.concurrent.TimeUnit.MINUTES import java.util.concurrent.TimeUnit.MINUTES

@ -26,6 +26,7 @@ kotlin {
sourceSets { sourceSets {
commonMain.dependencies { commonMain.dependencies {
implementation(libs.androidx.room) implementation(libs.androidx.room)
implementation(libs.kotlinx.datetime)
implementation(libs.kotlinx.serialization) implementation(libs.kotlinx.serialization)
implementation(libs.kermit) implementation(libs.kermit)
} }

@ -5,9 +5,12 @@ package org.tasks
import android.os.Parcelable import android.os.Parcelable
import kotlinx.parcelize.RawValue import kotlinx.parcelize.RawValue
import org.tasks.data.BuildConfig import org.tasks.data.BuildConfig
import java.util.Date
actual typealias CommonParcelable = Parcelable actual typealias CommonParcelable = Parcelable
actual typealias CommonRawValue = RawValue actual typealias CommonRawValue = RawValue
actual val IS_DEBUG = BuildConfig.DEBUG actual val IS_DEBUG = BuildConfig.DEBUG
actual fun Long.printTimestamp(): String = Date(this).toString()

@ -12,4 +12,6 @@ expect annotation class CommonRawValue()
expect interface CommonParcelable expect interface CommonParcelable
expect val IS_DEBUG: Boolean expect val IS_DEBUG: Boolean
expect fun Long.printTimestamp(): String

@ -389,6 +389,6 @@ ORDER BY primary_sort
companion object { companion object {
const val LOCAL = "local" const val LOCAL = "local"
@JvmStatic fun Long.toAppleEpoch(): Long = (this - APPLE_EPOCH) / 1000 fun Long.toAppleEpoch(): Long = (this - APPLE_EPOCH) / 1000
} }
} }

@ -1,7 +1,7 @@
package org.tasks.time package org.tasks.time
import org.tasks.IS_DEBUG import org.tasks.IS_DEBUG
import java.util.Date import org.tasks.printTimestamp
object DateTimeUtils2 { object DateTimeUtils2 {
@JvmStatic @JvmStatic
@ -24,4 +24,4 @@ object DateTimeUtils2 {
} }
fun printTimestamp(timestamp: Long): String = fun printTimestamp(timestamp: Long): String =
if (IS_DEBUG) Date(timestamp).toString() else timestamp.toString() if (IS_DEBUG) timestamp.printTimestamp() else timestamp.toString()

@ -1,6 +1,8 @@
package org.tasks.time package org.tasks.time
import kotlinx.datetime.Clock
class SystemMillisProvider : MillisProvider { class SystemMillisProvider : MillisProvider {
override val millis: Long override val millis: Long
get() = System.currentTimeMillis() get() = Clock.System.now().toEpochMilliseconds()
} }

@ -11,3 +11,5 @@ annotation class RawValue
actual typealias CommonRawValue = RawValue actual typealias CommonRawValue = RawValue
actual val IS_DEBUG = false actual val IS_DEBUG = false
actual fun Long.printTimestamp(): String = this.toString()

@ -257,6 +257,9 @@
+| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3 -> 1.8.0 (*) +| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3 -> 1.8.0 (*)
+| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core: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) +| | \--- 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:1.7.0
+| | \--- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.7.0 +| | \--- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.7.0
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*) +| | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*)
@ -611,6 +614,7 @@
+| | \--- androidx.compose.material:material-ripple:1.6.8 (c) +| | \--- androidx.compose.material:material-ripple:1.6.8 (c)
+| +--- org.jetbrains.compose.runtime:runtime:1.6.11 (*) +| +--- org.jetbrains.compose.runtime:runtime:1.6.11 (*)
+| +--- co.touchlab:kermit:2.0.4 (*) +| +--- co.touchlab:kermit:2.0.4 (*)
+| +--- org.jetbrains.kotlinx:kotlinx-datetime:0.6.0 (*)
+| \--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.0 (*) +| \--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.0 (*)
++--- com.github.bitfireAT:dav4jvm:2.2.1 ++--- com.github.bitfireAT:dav4jvm:2.2.1
+| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.20 -> 2.0.0 (*) +| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.20 -> 2.0.0 (*)

@ -626,6 +626,9 @@
+| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3 -> 1.8.0 (*) +| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3 -> 1.8.0 (*)
+| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core: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) +| | \--- 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:1.7.0
+| | \--- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.7.0 +| | \--- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.7.0
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*) +| | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*)
@ -920,6 +923,7 @@
+| | \--- androidx.compose.material:material-ripple:1.6.8 (c) +| | \--- androidx.compose.material:material-ripple:1.6.8 (c)
+| +--- org.jetbrains.compose.runtime:runtime:1.6.11 (*) +| +--- org.jetbrains.compose.runtime:runtime:1.6.11 (*)
+| +--- co.touchlab:kermit:2.0.4 (*) +| +--- co.touchlab:kermit:2.0.4 (*)
+| +--- org.jetbrains.kotlinx:kotlinx-datetime:0.6.0 (*)
+| \--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.0 (*) +| \--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.0 (*)
++--- com.github.bitfireAT:dav4jvm:2.2.1 ++--- com.github.bitfireAT:dav4jvm:2.2.1
+| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.20 -> 2.0.0 (*) +| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.20 -> 2.0.0 (*)

@ -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-jdk8 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", 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-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-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" } kotlinx-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.7.0" }
leakcanary = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanary" } leakcanary = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanary" }

@ -32,6 +32,7 @@ kotlin {
implementation(compose.materialIconsExtended) implementation(compose.materialIconsExtended)
implementation(compose.runtime) implementation(compose.runtime)
implementation(libs.kermit) implementation(libs.kermit)
implementation(libs.kotlinx.datetime)
} }
} }
task("testClasses") task("testClasses")

@ -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()
Loading…
Cancel
Save