diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/data/reminders/ReminderNotifier.kt b/app/src/main/java/de/jeanlucmakiola/calendula/data/reminders/ReminderNotifier.kt index 17d7213..7ca24b7 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/data/reminders/ReminderNotifier.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/data/reminders/ReminderNotifier.kt @@ -88,6 +88,7 @@ class ReminderNotifier @Inject constructor( is24Hour = is24Hour, today = Instant.now().atZone(zone).toLocalDate(), firstDayOfWeek = firstDayOfWeek, + todayLabel = context.getString(R.string.reminder_day_today), tomorrowLabel = context.getString(R.string.reminder_day_tomorrow), yesterdayLabel = context.getString(R.string.reminder_day_yesterday), ) diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/data/reminders/ReminderTimeText.kt b/app/src/main/java/de/jeanlucmakiola/calendula/data/reminders/ReminderTimeText.kt index cdc7462..c169d70 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/data/reminders/ReminderTimeText.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/data/reminders/ReminderTimeText.kt @@ -27,12 +27,12 @@ import java.util.Locale * alone would be ambiguous) * - timed, crossing days: "11 Jun, 23:30 – 12 Jun, 00:30" (medium date + short time, * already unambiguous) - * - all-day, one day: "11 Jun 2026" - * - all-day, multi-day: "11 Jun 2026 – 12 Jun 2026" + * - all-day, one day: "Tomorrow" / "Thu" / "11 Jun 2026" (same relative rules, + * plus [todayLabel] — no time range to lean on) + * - all-day, multi-day: "Tomorrow – 12 Jun 2026" (relative start, explicit end) * - * All-day instances already carry an explicit date, so they never gain a - * relative prefix. They store UTC midnights with an exclusive end, so they are - * read in UTC and the end day is the last *covered* day. + * All-day instances store UTC midnights with an exclusive end, so they are read + * in UTC and the end day is the last *covered* day (issue #308). */ fun reminderTimeText( beginMillis: Long, @@ -43,6 +43,7 @@ fun reminderTimeText( is24Hour: Boolean, today: LocalDate, firstDayOfWeek: DayOfWeek, + todayLabel: String, tomorrowLabel: String, yesterdayLabel: String, ): String { @@ -53,11 +54,16 @@ fun reminderTimeText( val lastDay = Instant.ofEpochMilli(endMillis).atZone(ZoneOffset.UTC).toLocalDate() .minusDays(1) .coerceAtLeast(firstDay) - return if (lastDay == firstDay) { - dateFormat.format(firstDay) - } else { - dateFormat.format(firstDay) + RANGE + dateFormat.format(lastDay) - } + val start = relativeDayPrefix( + day = firstDay, + today = today, + firstDayOfWeek = firstDayOfWeek, + locale = locale, + dateFormat = dateFormat, + tomorrowLabel = tomorrowLabel, + yesterdayLabel = yesterdayLabel, + ) ?: todayLabel + return if (lastDay == firstDay) start else start + RANGE + dateFormat.format(lastDay) } val timeFormat = timeOfDayFormatter(is24Hour, locale) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f1dcca0..323904d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -353,6 +353,7 @@ Snooze Dismiss + Today Tomorrow Yesterday diff --git a/app/src/test/java/de/jeanlucmakiola/calendula/data/reminders/ReminderTimeTextTest.kt b/app/src/test/java/de/jeanlucmakiola/calendula/data/reminders/ReminderTimeTextTest.kt index c8014a1..1b7791e 100644 --- a/app/src/test/java/de/jeanlucmakiola/calendula/data/reminders/ReminderTimeTextTest.kt +++ b/app/src/test/java/de/jeanlucmakiola/calendula/data/reminders/ReminderTimeTextTest.kt @@ -38,6 +38,7 @@ class ReminderTimeTextTest { is24Hour = is24Hour, today = today ?: java.time.Instant.ofEpochMilli(beginMillis).atZone(zone).toLocalDate(), firstDayOfWeek = firstDayOfWeek, + todayLabel = "Today", tomorrowLabel = "Tomorrow", yesterdayLabel = "Yesterday", ) @@ -130,38 +131,74 @@ class ReminderTimeTextTest { } @Test - fun `all-day single day shows one date, read in UTC`() { + fun `all-day single day today reads as today, read in UTC`() { val text = text( beginMillis = utcMidnight(LocalDate.of(2026, 6, 11)), endMillis = utcMidnight(LocalDate.of(2026, 6, 12)), isAllDay = true, // Zone must not matter for all-day events: UTC midnight is // 02:00 in Berlin — naive local reading would shift the day. + zone = ZoneId.of("America/Los_Angeles"), today = LocalDate.of(2026, 6, 11), ) - assertThat(text).isEqualTo("11.06.2026") + assertThat(text).isEqualTo("Today") } @Test - fun `all-day event tomorrow still shows the exact date, no relative prefix`() { + fun `all-day event tomorrow reads as tomorrow`() { val text = text( beginMillis = utcMidnight(LocalDate.of(2026, 6, 12)), endMillis = utcMidnight(LocalDate.of(2026, 6, 13)), isAllDay = true, today = LocalDate.of(2026, 6, 11), ) - assertThat(text).isEqualTo("12.06.2026") + assertThat(text).isEqualTo("Tomorrow") } @Test - fun `all-day multi-day shows the last covered day, not the exclusive end`() { + fun `all-day event later this week reads as the short weekday`() { + // 2026-06-11 is a Thursday; Saturday the 13th shares its week. val text = text( - beginMillis = utcMidnight(LocalDate.of(2026, 6, 11)), - endMillis = utcMidnight(LocalDate.of(2026, 6, 13)), + beginMillis = utcMidnight(LocalDate.of(2026, 6, 13)), + endMillis = utcMidnight(LocalDate.of(2026, 6, 14)), + isAllDay = true, + locale = Locale.US, + today = LocalDate.of(2026, 6, 11), + ) + assertThat(text).isEqualTo("Sat") + } + + @Test + fun `all-day event next week falls back to the exact date`() { + val text = text( + beginMillis = utcMidnight(LocalDate.of(2026, 6, 16)), + endMillis = utcMidnight(LocalDate.of(2026, 6, 17)), isAllDay = true, today = LocalDate.of(2026, 6, 11), ) - assertThat(text).isEqualTo("11.06.2026 – 12.06.2026") + assertThat(text).isEqualTo("16.06.2026") + } + + @Test + fun `all-day multi-day reads relative start and the last covered day`() { + val text = text( + beginMillis = utcMidnight(LocalDate.of(2026, 6, 12)), + endMillis = utcMidnight(LocalDate.of(2026, 6, 14)), + isAllDay = true, + today = LocalDate.of(2026, 6, 11), + ) + assertThat(text).isEqualTo("Tomorrow – 13.06.2026") + } + + @Test + fun `all-day multi-day further out keeps both dates`() { + val text = text( + beginMillis = utcMidnight(LocalDate.of(2026, 7, 1)), + endMillis = utcMidnight(LocalDate.of(2026, 7, 3)), + isAllDay = true, + today = LocalDate.of(2026, 6, 11), + ) + assertThat(text).isEqualTo("01.07.2026 – 02.07.2026") } @Test @@ -173,6 +210,6 @@ class ReminderTimeTextTest { isAllDay = true, today = LocalDate.of(2026, 6, 11), ) - assertThat(text).isEqualTo("11.06.2026") + assertThat(text).isEqualTo("Today") } }