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 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) {

@ -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(

@ -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 {

@ -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

@ -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) {

@ -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

@ -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,

@ -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

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

@ -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 {

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

@ -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

@ -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

@ -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,

@ -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(

@ -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

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

@ -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

@ -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

@ -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

@ -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

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

@ -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
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 val IS_DEBUG: Boolean
expect val IS_DEBUG: Boolean
expect fun Long.printTimestamp(): String

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

@ -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()
if (IS_DEBUG) timestamp.printTimestamp() else timestamp.toString()

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

@ -11,3 +11,5 @@ annotation class RawValue
actual typealias CommonRawValue = RawValue
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-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 (*)

@ -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 (*)

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

@ -32,6 +32,7 @@ kotlin {
implementation(compose.materialIconsExtended)
implementation(compose.runtime)
implementation(libs.kermit)
implementation(libs.kotlinx.datetime)
}
}
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