From ba86092cc845a11893e5d599c33c6536c25fcf77 Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Sun, 10 May 2026 11:01:13 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20Wochenansicht=20=E2=80=93=20ganzt=C3=A4g?= =?UTF-8?q?ige=20Termine=20spannen=20sich=20nicht=20mehr=20=C3=BCber=20zwe?= =?UTF-8?q?i=20Tage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bei der Layout-Berechnung für ganztägige Termine wurden Start/Ende als UTC-Zeitstempel mit der Lokal-Zeit der Tagesgrenze verglichen — das führte in Zeitzonen mit positivem UTC-Offset (z.B. CET) dazu, dass das exklusive DTEND zwei Stunden in den nächsten Tag hineinragte und die UI den Termin auf zwei Tagen darstellte. Fix: Für ganztägige Events normalisieren wir auf reine Datumswerte (setHours(0,0,0,0)) und ziehen einen Tag vom End-Datum ab, sodass die Vergleiche dieselbe inklusive Semantik wie die Monatsansicht nutzen. Timed Events behalten die ursprüngliche strict-overlap Logik. Auch die continues-left/right Marker arbeiten jetzt mit den normalisierten Daten. Co-Authored-By: Claude Opus 4.6 --- frontend/js/views/week.js | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/frontend/js/views/week.js b/frontend/js/views/week.js index bc440bc..e84c0cb 100644 --- a/frontend/js/views/week.js +++ b/frontend/js/views/week.js @@ -63,8 +63,19 @@ export function renderWeek(container, currentDate, events, onSlotClick, onEventC const color = ev.color || ev.calendarColor || '#4285f4'; const pastCls = isPast(ev) ? 'past' : ''; const multiCls = isMultiTimed ? 'multiday-timed' : ''; - const cL = new Date(ev.start) < new Date(days[0]) ? 'continues-left' : ''; - const cR = new Date(ev.end) > (() => { const d = new Date(days[n-1]); d.setHours(24,0,0,0); return d; })() ? 'continues-right' : ''; + // continues-left/right: compute on date-only basis for all-day events + let evStart = new Date(ev.start); + let evEnd = new Date(ev.end); + if (ev.allDay) { + evStart.setHours(0, 0, 0, 0); + evEnd.setHours(0, 0, 0, 0); + if (evEnd > evStart) evEnd.setDate(evEnd.getDate() - 1); + } + const firstDay = new Date(days[0]); firstDay.setHours(0, 0, 0, 0); + const lastDayMidnight = new Date(days[n-1]); lastDayMidnight.setHours(24, 0, 0, 0); + const lastDay = new Date(days[n-1]); lastDay.setHours(0, 0, 0, 0); + const cL = evStart < firstDay ? 'continues-left' : ''; + const cR = (ev.allDay ? evEnd > lastDay : evEnd > lastDayMidnight) ? 'continues-right' : ''; const label = isMultiTimed && isSameDay(new Date(ev.start), days[colStart]) ? `${fmtTime(new Date(ev.start))} ${ev.title}` : ev.title; @@ -236,11 +247,28 @@ function renderNowLine(container, days, hourH = 60) { function layoutWeekAllDay(evs, days) { const items = []; evs.forEach(ev => { + // For all-day events, normalize to date-only with inclusive end-day + // (iCal stores exclusive end → subtract 1). For timed events, keep + // the original strict-overlap logic so events ending exactly at + // midnight don't bleed into the next day. + let ns, ne; + if (ev.allDay) { + ns = new Date(ev.start); ns.setHours(0, 0, 0, 0); + ne = new Date(ev.end); ne.setHours(0, 0, 0, 0); + if (ne > ns) ne.setDate(ne.getDate() - 1); + } + let colStart = -1, colEnd = -1; days.forEach((day, i) => { const ds = new Date(day); ds.setHours(0, 0, 0, 0); - const de = new Date(day); de.setHours(24, 0, 0, 0); - if (new Date(ev.start) < de && new Date(ev.end) > ds) { + let matches; + if (ev.allDay) { + matches = ds >= ns && ds <= ne; + } else { + const de = new Date(day); de.setHours(24, 0, 0, 0); + matches = new Date(ev.start) < de && new Date(ev.end) > ds; + } + if (matches) { if (colStart === -1) colStart = i; colEnd = i; }