Das Symptom

Du importierst via gromox-eml2mt --ical einen all-day Event für Mittwoch und Donnerstag:

DTSTART;VALUE=DATE:20260422
DTEND;VALUE=DATE:20260424
SUMMARY:Kind bei Papa

Laut RFC 5545 ist DTEND bei DATE-Werten exclusive, also sollte der Event am Mittwoch beginnen und Donnerstag zu Ende sein — Mi+Do, zwei Tage. Stattdessen zeigt grommunio-Web (und auch Outlook nach Sync) drei Tage: Mi + Do + Fr-Vormittag.

Ursache

gromox-eml2mt behandelt DATE-Werte als DATETIME in UTC. Aus

DTSTART;VALUE=DATE:20260422   →  2026-04-22T00:00:00Z
DTEND;VALUE=DATE:20260424     →  2026-04-24T00:00:00Z

wird in der Zielzeitzone Europe/Berlin (CEST = UTC+2):

Start: 2026-04-22T02:00:00 CEST   (Mi 02:00)
Ende:  2026-04-24T02:00:00 CEST   (Fr 02:00)

Das deckt Mi (komplett), Do (komplett) und zieht sich bis Fr 02:00 morgens — in der Wochenansicht erscheint deshalb auch Freitag als belegter Tag. Im Winter mit CET (+1h) wäre der Effekt kleiner, aber immer noch sichtbar.

Überprüfen per gromox-mbop:

gromox-mbop -u user@example.de get-freebusy \
  -a 2026-04-20T00:00:00 -b 2026-04-30T00:00:00 | grep "Kind bei Papa"

Output:

{start=2026-04-22T02:00:00, end=2026-04-24T02:00:00, subject="Kind bei Papa"}

Die T02:00:00 beweisen den UTC→Lokalzeit-Shift.

Workaround

Verwende statt DATE-only Werten DATETIME mit expliziter Zeitzone und bette eine VTIMEZONE-Komponente ein. Dann rechnet gromox die Zeiten korrekt in Lokalzeit ohne Offset-Drift.

BEGIN:VCALENDAR
PRODID:-//example//DE
VERSION:2.0
BEGIN:VTIMEZONE
TZID:Europe/Berlin
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
UID:12345@example.de
DTSTAMP:20260419T140000Z
DTSTART;TZID=Europe/Berlin:20260422T000000
DTEND;TZID=Europe/Berlin:20260424T000000
SUMMARY:Kind bei Papa
X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR

Das X-MICROSOFT-CDO-ALLDAYEVENT:TRUE signalisiert Outlook/grommunio-Web zusätzlich, dass es sich um einen ganztägigen Termin handelt — kosmetisch, aber hilfreich für die korrekte Darstellung.

Verifikation nach Re-Import

{start=2026-04-22T00:00:00, end=2026-04-24T00:00:00, subject="Kind bei Papa"}

T00:00:00 ohne Offset — genau das, was wir wollten.

Warum das kein reiner gromox-Bug ist

Microsoft/Outlook und auch einige andere MAPI-Stores haben historisch DATE-Werte intern als UTC-DATETIME repräsentiert. Die Übersetzung DATE ↔ MAPI ist in der Spezifikation nicht eindeutig, und unterschiedliche Implementierungen bewältigen es unterschiedlich. gromox ist hier Exchange-kompatibel — was leider heißt: genauso suboptimal.

Der saubere Weg in iCalendar ist nun mal VALUE=DATE für all-day Events. Aber wenn dein Ziel-Store ein MAPI-basierter ist, kommst du mit expliziter VTIMEZONE besser durch.

Fazit

Wenn du iCal-Dateien programmatisch erzeugst und nach grommunio/gromox importierst:

  • All-day Events: immer mit TZID und DATETIME, nie mit VALUE=DATE.
  • Nach dem Import: per gromox-mbop get-freebusy die tatsächlichen Start/End-Zeiten prüfen. Wenn sie T02:00:00 statt T00:00:00 sind, weißt du Bescheid.
  • Normale (timed) Events sind nicht betroffen — die haben von Haus aus TZID und rechnen korrekt.

Kostet fünf Minuten mehr beim iCal-Template-Bau, erspart dir aber, dass dein Chef seinen Freitag Vormittag blockiert sieht obwohl er frei hat.

Referenzen