From e7174770f3e7f7454c245ca1f9ce49c6f40934e8 Mon Sep 17 00:00:00 2001 From: Guido Schmit Date: Tue, 5 May 2026 18:16:56 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20Ganzt=C3=A4gig-Termine=20zeigen=20End-Da?= =?UTF-8?q?tum=20jetzt=20korrekt=20inklusive=20iCal=20speichert=20DTEND=20?= =?UTF-8?q?exklusiv=20(Tag=20NACH=20dem=20letzten=20Tag).=20Bisher=20f?= =?UTF-8?q?=C3=BChrte=20das=20dazu,=20dass=20ein=20Termin=20mit=20Ende=3D1?= =?UTF-8?q?8.08=20nur=20bis=20zum=2017.08=20angezeigt=20wurde,=20obwohl=20?= =?UTF-8?q?der=20User=2018.08=20als=20letzten=20Tag=20erwartete.=20Fix:=20?= =?UTF-8?q?Im=20Date-Picker=20arbeiten=20wir=20jetzt=20mit=20inklusiven=20?= =?UTF-8?q?End-Daten=20('endet=20am=2018.08'=20=3D=2018.08=20ist=20letzter?= =?UTF-8?q?=20Tag)=20und=20konvertieren=20beim=20Speichern=20auf=20exklusi?= =?UTF-8?q?v=20(DTEND=3D19.08).=20Beim=20Laden=20umgekehrt:=20-1=20Tag=20f?= =?UTF-8?q?=C3=BCrs=20Anzeigen=20im=20Picker.=20Betrifft:=20openEditEventM?= =?UTF-8?q?odal,=20openCopyEditModal,=20Save-Handler.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/js/calendar.js | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/frontend/js/calendar.js b/frontend/js/calendar.js index a7faea5..7b3b41b 100644 --- a/frontend/js/calendar.js +++ b/frontend/js/calendar.js @@ -1127,6 +1127,21 @@ function populateCalendarSelect(selectedId) { } // ── Date field helpers ──────────────────────────────────── + +// All-day events use exclusive end-dates (iCal RFC 5545 convention): +// DTEND points to the day AFTER the last visible day. The user picker +// uses inclusive end-dates ("ends on 18.08" means 18.08 is the last +// day). These helpers convert between the two. +function shiftDate(isoDate, deltaDays) { + if (!isoDate) return isoDate; + const [y, m, d] = isoDate.slice(0, 10).split('-').map(Number); + const dt = new Date(Date.UTC(y, m - 1, d)); + dt.setUTCDate(dt.getUTCDate() + deltaDays); + return dt.toISOString().slice(0, 10); +} +const allDayEndToInclusive = iso => shiftDate(iso, -1); // storage → picker +const allDayEndToExclusive = iso => shiftDate(iso, +1); // picker → storage + function setDtValue(id, isoStr, mode) { const input = document.getElementById(id); if (input) input.value = isoStr || ''; @@ -1177,7 +1192,7 @@ function openCopyEditModal(ev, targetCal) { if (ev.allDay) { setDtValue('ev-start-date', (ev.start || '').slice(0, 10), 'date'); - setDtValue('ev-end-date', (ev.end || '').slice(0, 10), 'date'); + setDtValue('ev-end-date', allDayEndToInclusive((ev.end || '').slice(0, 10)), 'date'); } else { const s = new Date(ev.start); const e = new Date(ev.end); @@ -1213,7 +1228,7 @@ function openEditEventModal(ev) { if (ev.allDay) { setDtValue('ev-start-date', ev.start.slice(0, 10), 'date'); - setDtValue('ev-end-date', ev.end.slice(0, 10), 'date'); + setDtValue('ev-end-date', allDayEndToInclusive(ev.end.slice(0, 10)), 'date'); toggleAlldayFields(true); } else { const s = new Date(ev.start); @@ -1473,6 +1488,8 @@ function bindEventModal() { end = document.getElementById('ev-end-date').value; if (!start) { showToast(t('error_enter_date'), true); return; } if (!end || end < start) end = start; + // User picker uses inclusive end; storage uses exclusive (iCal convention) + end = allDayEndToExclusive(end); } else { const sv = document.getElementById('ev-start').value; const ev2 = document.getElementById('ev-end').value;