Layer 8Ehrlicher Tech-Support

Der Termin verschiebt sich um eine Stunde

Nach der Zeitumstellung steht die Besprechung plötzlich um zehn statt um neun. Schuld ist nicht der Kalender, sondern eine Uhrzeit ohne Zeitzone.

Aufwand:
ca. 25 Min.
Voraussetzung:
Python 3 (Modul zoneinfo), Die ICS-Datei der Einladung als Text, Zugriff auf die Kalendereinstellungen
Gilt für:
iCalendar nach RFC 5545 (ICS-Dateien, Einladungen per Mail), Serientermine über eine Zeitumstellung hinweg, Python 3.9 und neuer

Das Symptom

Der wöchentliche Jour fixe stand seit Monaten auf neun Uhr. Am ersten Mittwoch nach der Zeitumstellung steht er auf zehn – bei manchen Teilnehmern, bei anderen weiterhin auf neun. Zwei Leute erscheinen, drei nicht, und alle drei haben den Kalender aufgeschlagen, bevor sie losgegangen sind.

Andere Ausprägungen derselben Sache: Eine Einladung aus dem Ausland liegt beim Empfänger um mehrere Stunden daneben. Ein Serientermin fällt an genau einem Sonntag im März ersatzlos aus. Ein nächtlicher Sicherungslauf, der um 2:30 Uhr starten sollte, ist an einem Tag im Jahr einfach nicht gelaufen, ohne Fehlermeldung.

Die „Layer 8“-Wahrheit

Gespeichert wurde nicht, was gemeint war. Gemeint war „neun Uhr in Berlin“ – gespeichert wurde entweder eine nackte Uhrzeit ohne Ortsangabe oder ein fester Zeitpunkt in Weltzeit. Beides ist etwas anderes, und beides fällt erst auf, wenn die Sommerzeit dazwischengeht.

RFC 5545, der Standard hinter jeder ICS-Datei und jeder Einladung per Mail, kennt genau drei Formen. Eine Ortszeit ohne Zeitzonenbezug heißt dort „floating“ und ist ausdrücklich an keine Zeitzone gebunden: Zwei Eingeladene in verschiedenen Ländern nehmen dann zu verschiedenen realen Zeitpunkten am selben Termin teil. Weltzeit ist eindeutig, aber sie beschreibt einen Zeitpunkt, keine Regel – „jeden Mittwoch um neun Uhr in Berlin“ entspricht im Winter einem anderen UTC-Wert als im Sommer. Nur die dritte Form, Ortszeit mit Zeitzonenbezug, hält beides zusammen. Der Standard sagt das selbst und schreibt für den festen Zeitpunkt ein MUST vor.

Der Layer-8-Anteil ist der Reflex, danach am Kalenderprogramm zu drehen. Das Programm hat nichts falsch gemacht: Es hat den gespeicherten Zeitpunkt korrekt wiederholt und im Frühjahr sogar korrekt einen Termin übersprungen, weil es die Stunde von 2 bis 3 Uhr an diesem Tag nicht gibt – auch das steht so im Standard. Reparieren lässt sich das nicht an der Anzeige, sondern nur an der Speicherung: Zonenname statt Stundenversatz, TZID statt nackter Uhrzeit, und in der Einladung drei zusätzliche Wörter für die Menschen, die den Kalender ohnehin nicht aufmachen.

Die echte, verifizierte Lösung

1000 % faktenbasiert. Jeder Befehl ist real, jeder Pfad existiert. Schritt für Schritt:

  1. 1.

    Die Einladung im Klartext ansehen

    Die ICS-Datei ist eine Textdatei. Öffne die Einladung aus der Mail mit einem Editor und sieh dir die Zeile DTSTART an. Steht dort eine nackte Uhrzeit, ist der Termin nach RFC 5545 eine schwebende Zeit und bedeutet für jeden Empfänger etwas anderes. Ein angehängtes Z bedeutet Weltzeit, ein vorangestelltes TZID bedeutet Ortszeit mit Zeitzonenbezug – nur die letzte Form überlebt eine Zeitumstellung unbeschadet.

    DTSTART:20260325T090000                      # schwebend – gilt überall anders
    DTSTART:20260325T080000Z                     # Weltzeit – fester Zeitpunkt
    DTSTART;TZID=Europe/Berlin:20260325T090000   # richtig für Serientermine
    
  2. 2.

    Den eigenen Fall nachrechnen, statt zu raten

    Bevor jemand den Kalender verdächtigt, lässt sich die Verschiebung in fünf Zeilen zeigen. Der Serientermin liegt fest in Weltzeit; die Berliner Ortszeit springt trotzdem, weil dazwischen die Sommerzeit beginnt. Genau diese Ausgabe ist der Beweis, dass nicht das Programm gerechnet hat, sondern die Zeitzone.

    python3 -c "
    from datetime import datetime, timedelta
    from zoneinfo import ZoneInfo
    B, U = ZoneInfo('Europe/Berlin'), ZoneInfo('UTC')
    start = datetime(2026, 3, 25, 9, 0, tzinfo=B).astimezone(U)
    for w in range(3):
        t = start + timedelta(weeks=w)
        print(t.date(), 'UTC', t.strftime('%H:%M'), '-> Berlin', t.astimezone(B).strftime('%H:%M %Z'))
    "
    # 2026-03-25 UTC 08:00 -> Berlin 09:00 CET
    # 2026-04-01 UTC 08:00 -> Berlin 10:00 CEST
    # 2026-04-08 UTC 08:00 -> Berlin 10:00 CEST
    
  3. 3.

    Zeitzonen als Namen speichern, nicht als Stundenversatz

    Ein Versatz wie +01:00 ist eine Momentaufnahme, kein Ort. Europe/Berlin dagegen ist eine Regel, die weiß, wann umgestellt wird – auch wenn sich die Regel ändert, was politisch regelmäßig vorkommt. In Datenbanken, Konfigurationen und Schnittstellen gehört deshalb der Zonenname hinein. Wer nur den Versatz speichert, hat die Sommerzeit fest eingebrannt und merkt es zweimal im Jahr.

  4. 4.

    Die doppelte Stunde im Herbst prüfen

    Am Umstellungswochenende im Oktober gibt es 2:30 Uhr zweimal. Python macht die Zweideutigkeit über das Merkmal `fold` sichtbar: derselbe Ortszeit-Wert, zwei verschiedene reale Zeitpunkte, eine Stunde auseinander. RFC 5545 entscheidet sich hier für das erste Vorkommen; jedes Programm, das etwas anderes tut, weicht bewusst oder unbewusst ab.

    python3 -c "
    from datetime import datetime
    from zoneinfo import ZoneInfo
    B, U = ZoneInfo('Europe/Berlin'), ZoneInfo('UTC')
    for f in (0, 1):
        a = datetime(2026, 10, 25, 2, 30, tzinfo=B, fold=f)
        print('fold', f, a, '| Offset', a.utcoffset(), '| UTC', a.astimezone(U))
    "
    # fold 0 2026-10-25 02:30:00+02:00 | Offset 2:00:00 | UTC 2026-10-25 00:30:00+00:00
    # fold 1 2026-10-25 02:30:00+01:00 | Offset 1:00:00 | UTC 2026-10-25 01:30:00+00:00
    
  5. 5.

    Nachts nichts zwischen 2 und 3 Uhr planen

    Die Stunde, die im Frühjahr ausfällt, nimmt alles mit, was in ihr liegt: Sicherungen, Abrechnungen, Erinnerungen, Schichtwechsel. Nach RFC 5545 müssen Serientermine mit nicht existierender Ortszeit übersprungen werden. Wer den nächtlichen Lauf auf 1:30 oder 3:30 Uhr legt, umgeht das Problem vollständig und ohne Sonderregel.

  6. 6.

    Bei geplanten Terminen die Zeitzone mitschreiben – auch im Text

    In der Einladung, im Betreff und in der Beschreibung gehört die Zone dazu: neun Uhr Berliner Zeit, nicht neun Uhr. Das kostet drei Wörter und ersetzt die halbe Stunde Rückfragen, wenn zwei Teilnehmer in verschiedenen Ländern sitzen. Für den Menschen ist der Satz die Absicherung gegen einen falsch konfigurierten Kalender – die Maschine hält sich an TZID, der Mensch an den Text.

  7. 7.

    Naive Zeitstempel aus dem eigenen Code verbannen

    In Protokollen und Datenbanken ist ein Zeitstempel ohne Zone die häufigste Ursache derselben Verwirrung. Die Python-Dokumentation formuliert es scharf: Ein naives Objekt „does not contain enough information to unambiguously locate itself relative to other date/time objects“ – ob es Weltzeit, Ortszeit oder etwas anderes meint, hänge allein vom Programm ab. Beim Speichern also Weltzeit plus Zonenname, beim Anzeigen erst umrechnen.

    python3 -c "
    from datetime import datetime, timezone
    print(datetime(2026, 3, 29, 2, 30).utcoffset())
    print(datetime.now(timezone.utc).isoformat(timespec='seconds'))
    "
    # None
    

Häufige Fragen (FAQ)

?Ich habe neun Uhr eingetragen. Wieso steht dort jetzt zehn Uhr?

Weil im Termin nicht neun Uhr gespeichert wurde, sondern ein fester Zeitpunkt in Weltzeit. Solange die Zeitumstellung nicht dazwischenkommt, sind das dasselbe – danach nicht mehr. Selbst nachgerechnet: Ein Serientermin, der am 25. März 2026 um 09:00 Berliner Zeit beginnt, liegt bei 08:00 UTC. Am 1. April 2026 liegt derselbe UTC-Zeitpunkt in Berlin auf 10:00, weil dazwischen die Sommerzeit begonnen hat. Der Kalender hat nichts verschoben; er hat genau das wiederholt, was gespeichert war.

?Was genau muss denn in der Einladung stehen, damit es hält?

Ein Bezug auf eine Zeitzone, nicht nur eine Uhrzeit. RFC 5545 kennt drei Formen für Datum und Uhrzeit. Form 1 ist die Ortszeit ohne alles: Solche Werte heißen im Standard „floating“ und sind „not bound to any time zone in particular“ – zwei Eingeladene in verschiedenen Zeitzonen nehmen dann „at different actual times“ am selben Termin teil. Der Standard schreibt dazu unmissverständlich: „In most cases, a fixed time is desired. To properly communicate a fixed time in a property value, either UTC time or local time with time zone reference MUST be specified.“ Praktisch heißt das: `DTSTART;TZID=Europe/Berlin:20260325T090000` statt `DTSTART:20260325T090000`.

?Reicht es nicht, alles in UTC zu speichern? Das ist doch eindeutig.

Für einen einzelnen Termin ja, für einen Serientermin nein. Ein UTC-Zeitpunkt ist eindeutig, aber die Aussage „jeden Mittwoch um neun in Berlin“ ist keine Aussage über UTC – der zugehörige UTC-Zeitpunkt wechselt zweimal im Jahr. Wer die Serie einmal in UTC umrechnet und dann wöchentlich sieben Tage addiert, bekommt nach der Umstellung genau die Stunde Versatz aus der ersten Frage. Deshalb verlangt RFC 5545 für Werte, die weder UTC noch „floating“ sind, den Parameter TZID mit einer passenden VTIMEZONE-Definition und warnt: „Failure to include and follow VTIMEZONE definitions in iCalendar objects may lead to inconsistent understanding of the local time at any given location.“

?Ein wöchentlicher Termin ist an einem Sonntag im März einfach ausgefallen. Fehler?

Nein, Vorschrift. In der Nacht der Umstellung auf Sommerzeit gibt es die Stunde von 2 bis 3 Uhr Ortszeit nicht. RFC 5545 regelt diesen Fall bei den Wiederholungsregeln ausdrücklich: Eine Serie könne Einzeltermine mit ungültigem Datum oder mit „nonexistent local time (e.g., 1:30 AM on a day where the local time is moved forward by an hour at 1:00 AM)“ erzeugen, und solche Einzeltermine „MUST be ignored and MUST NOT be counted as part of the recurrence set“. Ein Termin, der auf 2:30 Uhr Ortszeit fällt, verschwindet an diesem einen Tag also völlig regelkonform. Wer nachts Sicherungen, Abrechnungsläufe oder Schichtübergaben plant, legt sie deshalb nicht zwischen 2 und 3 Uhr.

?Und im Herbst gibt es die Stunde zweimal – welche gilt dann?

Der Standard entscheidet sich: Kommt die beschriebene Ortszeit mehr als einmal vor, meint der Wert „the first occurrence of the referenced time“, also die noch geltende Sommerzeit. Programme, die das nicht abbilden, treffen die Wahl anders oder gar nicht. Selbst nachgerechnet für Berlin: 2026-10-25 02:30 Uhr gibt es zweimal, einmal mit Offset +02:00 (das ist 00:30 UTC) und einmal mit +01:00 (01:30 UTC). Zwischen den beiden liegt eine volle Stunde reale Zeit. Bei einem Meeting ist das ärgerlich, bei einem Protokoll mit Zeitstempeln ist es eine Beweislücke.

Belege

Diese Seite ist gegen die folgenden Primärquellen geprüft – Hersteller, Standard oder Gesetzestext. Wer es nicht glaubt, muss es nicht: Da steht, wo es nachzulesen ist.

Zuletzt geprüft am . Dokumentation altert – ein Datum daneben ist der einzige ehrliche Umgang damit.

Auch zu finden unter

Verwandte Fehler

SWLästigCERTIFICATE_VERIFY_FAILED

pip: SSL: CERTIFICATE_VERIFY_FAILED

pip lädt keine Pakete wegen SSL-Zertifikatsfehler? Meist schneidet ein Firmen-Proxy mit oder die Zertifikate sind veraltet — beides ist lösbar.