From e5b3e549e8111869e8bc961e530b07f4a4cee512 Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Sat, 12 Sep 2026 14:52:52 +0200 Subject: [PATCH] Catch multi-day events that do not end on a day boundary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Requiring both ends to sit exactly on midnight was too strict. A holiday stored as the 16th at 00:00 through the 21st at 01:00 slipped through and rendered as a one-hour slot. Starting on a day boundary and running into a later day is enough to call it all-day. The agenda row now names the weekday on both sides when an event crosses a day. Showing only "00:00 – 01:00" for something that runs from Wednesday to Monday is true and useless. The timezone handling above this is deliberately untouched: timed events show the right time, so the UTC assumption for timestamps without an offset is not what produced the odd hour. Co-Authored-By: Claude Opus 5 --- Calendarr iOS/Models/CalEvent.swift | 6 +++++- CalendarrWatch/Views/WatchEventRow.swift | 25 ++++++++++++++++++------ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/Calendarr iOS/Models/CalEvent.swift b/Calendarr iOS/Models/CalEvent.swift index 59c5cc0..db2298f 100644 --- a/Calendarr iOS/Models/CalEvent.swift +++ b/Calendarr iOS/Models/CalEvent.swift @@ -108,9 +108,13 @@ struct CalEvent: Identifiable, Hashable { // holiday sent without the flag renders as "00:00 – 00:00" — a time // range that is technically true and tells the reader nothing. let cal = Calendar.current + // Requiring the end to land exactly on midnight too was too strict: a + // holiday stored as 16th 00:00 to 21st 01:00 slipped through and showed + // up as a one-hour slot. Starting on a day boundary and running into a + // later day is enough. let spansWholeDays = e > s && cal.startOfDay(for: s) == s - && cal.startOfDay(for: e) == e + && cal.startOfDay(for: e) > cal.startOfDay(for: s) let isAllDay = declaredAllDay || spansWholeDays return CalEvent( diff --git a/CalendarrWatch/Views/WatchEventRow.swift b/CalendarrWatch/Views/WatchEventRow.swift index aff41da..6450f26 100644 --- a/CalendarrWatch/Views/WatchEventRow.swift +++ b/CalendarrWatch/Views/WatchEventRow.swift @@ -41,14 +41,27 @@ extension Color { } enum WatchEventFormat { - static func timeRange(_ event: SnapshotEvent, language: String) -> String { + static func timeRange(_ event: SnapshotEvent, language: String, + calendar: Calendar = Calendar(identifier: .gregorian)) -> String { if event.isAllDay { return WatchL10n.t("watch.allday", language) } - let formatter = DateFormatter() - formatter.locale = WatchL10n.locale(language) - formatter.dateFormat = "HH:mm" - let start = formatter.string(from: event.start) + + let time = DateFormatter() + time.locale = WatchL10n.locale(language) + time.dateFormat = "HH:mm" + + let start = time.string(from: event.start) guard event.end > event.start else { return start } - return "\(start) – \(formatter.string(from: event.end))" + + // Without the weekday, an event running from Wednesday to Monday reads + // as a one-hour slot: "00:00 – 01:00" is true and useless. + if calendar.startOfDay(for: event.end) > calendar.startOfDay(for: event.start) { + let day = DateFormatter() + day.locale = WatchL10n.locale(language) + day.setLocalizedDateFormatFromTemplate("EEE") + return "\(day.string(from: event.start)) \(start) – " + + "\(day.string(from: event.end)) \(time.string(from: event.end))" + } + return "\(start) – \(time.string(from: event.end))" } static func time(_ date: Date, language: String) -> String {