diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/domain/EventForm.kt b/app/src/main/java/de/jeanlucmakiola/calendula/domain/EventForm.kt index 4ffe8b5..bbd9646 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/domain/EventForm.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/domain/EventForm.kt @@ -162,10 +162,7 @@ fun EventDetail.toEditForm(beginMillis: Long, endMillis: Long, zone: TimeZone): val begin = Instant.fromEpochMilliseconds(beginMillis) val finish = Instant.fromEpochMilliseconds(endMillis) val (start, end) = if (instance.isAllDay) { - val startDate = begin.toLocalDateTime(TimeZone.UTC).date - val endExclusive = finish.toLocalDateTime(TimeZone.UTC).date - val endDate = maxOf(startDate, LocalDate.fromEpochDays(endExclusive.toEpochDays() - 1)) - FormTime(LocalDateTime(startDate, LocalTime(9, 0))) to FormTime(LocalDateTime(endDate, LocalTime(10, 0))) + allDayFormTimes(begin.toLocalDateTime(TimeZone.UTC).date, finish.toLocalDateTime(TimeZone.UTC).date) } else { begin.toFormTime(formZone) to finish.toFormTime(formZone) } @@ -181,7 +178,7 @@ fun EventDetail.toEditForm(beginMillis: Long, endMillis: Long, zone: TimeZone): reminders = reminders.map { it.minutes }.distinct().sorted(), availability = availability, accessLevel = accessLevel, - rrule = rrule?.removePrefix("RRULE:")?.takeIf { it.isNotBlank() }, + rrule = rrule.normalizedRrule(), // The provider fills EVENT_COLOR from the key, so [color] is the // swatch either way; a null colour means the event inherits its // calendar's colour. @@ -233,6 +230,19 @@ fun EventDetail.toEditSnapshot(beginMillis: Long, endMillis: Long, zone: TimeZon rowEnd = instance.end, ) +/** + * Form times for an all-day span stored as UTC midnights with an exclusive + * end: the last covered day (never before [start]), with placeholder + * wall-clock times kept in case the user switches the event to timed. + */ +internal fun allDayFormTimes(start: LocalDate, endExclusive: LocalDate?): Pair { + val end = endExclusive?.let { maxOf(start, LocalDate.fromEpochDays(it.toEpochDays() - 1)) } ?: start + return FormTime(LocalDateTime(start, LocalTime(9, 0))) to FormTime(LocalDateTime(end, LocalTime(10, 0))) +} + +/** The bare RRULE value the form edits; blank means none. */ +internal fun String?.normalizedRrule(): String? = this?.removePrefix("RRULE:")?.takeIf { it.isNotBlank() } + /** * Whether this form's times mean something else in [target] — a timed event * pinned to another zone. False when there's nothing to disambiguate: an diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/domain/InsertEventForm.kt b/app/src/main/java/de/jeanlucmakiola/calendula/domain/InsertEventForm.kt index a537275..0a6c0c1 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/domain/InsertEventForm.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/domain/InsertEventForm.kt @@ -1,8 +1,5 @@ package de.jeanlucmakiola.calendula.domain -import kotlinx.datetime.LocalDate -import kotlinx.datetime.LocalDateTime -import kotlinx.datetime.LocalTime import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime import kotlin.time.Duration.Companion.hours @@ -35,17 +32,11 @@ fun buildInsertEventForm( now: Instant, ): EventForm { val (start, end) = if (isAllDay) { - // All-day provider times are UTC midnights with an exclusive end; show - // the last covered day and keep placeholder wall-clock times in case the - // user switches the event to timed (mirrors EventDetail.toEditForm). val startDate = beginMillis ?.let { Instant.fromEpochMilliseconds(it).toLocalDateTime(TimeZone.UTC).date } ?: now.toLocalDateTime(zone).date - val endDate = endMillis - ?.let { Instant.fromEpochMilliseconds(it).toLocalDateTime(TimeZone.UTC).date } - ?.let { exclusive -> maxOf(startDate, LocalDate.fromEpochDays(exclusive.toEpochDays() - 1)) } - ?: startDate - FormTime(LocalDateTime(startDate, LocalTime(9, 0))) to FormTime(LocalDateTime(endDate, LocalTime(10, 0))) + val endExclusive = endMillis?.let { Instant.fromEpochMilliseconds(it).toLocalDateTime(TimeZone.UTC).date } + allDayFormTimes(startDate, endExclusive) } else { val startAt = beginMillis?.let(Instant::fromEpochMilliseconds) ?: nextFullHour(now) val endAt = endMillis?.let(Instant::fromEpochMilliseconds)?.takeIf { it >= startAt } @@ -62,7 +53,7 @@ fun buildInsertEventForm( description = description.orEmpty(), // Bare RRULE value (Events.RRULE convention); tolerate a leading "RRULE:" // some callers include. - rrule = rrule?.removePrefix("RRULE:")?.takeIf { it.isNotBlank() }, + rrule = rrule.normalizedRrule(), ) } diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/domain/ics/ParsedIcsForm.kt b/app/src/main/java/de/jeanlucmakiola/calendula/domain/ics/ParsedIcsForm.kt index e8ab23b..1467e04 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/domain/ics/ParsedIcsForm.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/domain/ics/ParsedIcsForm.kt @@ -1,11 +1,9 @@ package de.jeanlucmakiola.calendula.domain.ics import de.jeanlucmakiola.calendula.domain.EventForm -import de.jeanlucmakiola.calendula.domain.FormTime +import de.jeanlucmakiola.calendula.domain.allDayFormTimes +import de.jeanlucmakiola.calendula.domain.normalizedRrule import de.jeanlucmakiola.calendula.domain.toFormTime -import kotlinx.datetime.LocalDate -import kotlinx.datetime.LocalDateTime -import kotlinx.datetime.LocalTime import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime @@ -23,10 +21,7 @@ import kotlinx.datetime.toLocalDateTime */ fun ParsedIcsEvent.toEventForm(zone: TimeZone): EventForm { val (start, end) = if (isAllDay) { - val startDate = this.start.toLocalDateTime(TimeZone.UTC).date - val endExclusive = this.end.toLocalDateTime(TimeZone.UTC).date - val endDate = maxOf(startDate, LocalDate.fromEpochDays(endExclusive.toEpochDays() - 1)) - FormTime(LocalDateTime(startDate, LocalTime(9, 0))) to FormTime(LocalDateTime(endDate, LocalTime(10, 0))) + allDayFormTimes(this.start.toLocalDateTime(TimeZone.UTC).date, this.end.toLocalDateTime(TimeZone.UTC).date) } else { this.start.toFormTime(zone) to this.end.toFormTime(zone) } @@ -40,6 +35,6 @@ fun ParsedIcsEvent.toEventForm(zone: TimeZone): EventForm { description = description.orEmpty(), reminders = semanticReminderMinutes(), availability = availability, - rrule = recurrenceRule?.removePrefix("RRULE:")?.takeIf { it.isNotBlank() }, + rrule = recurrenceRule.normalizedRrule(), ) }