Zeitzonen korrekt in SQL verarbeiten: UTC, Offsets und Sommerzeit
AI generated
SELECT
JOIN
SQL / Temporal Data
Zeitzonen korrekt in SQL verarbeiten
UTC speichern, Zeitzone erst bei der Anzeige anwenden

Kaum ein Datenmodellierungsfehler rächt sich so verlässlich erst Monate später wie ein falscher Umgang mit Zeitzonen: Ein Server in Europe/Berlin, ein Kunde in New York, eine Sommerzeit-Umstellung mitten in einem wiederkehrenden Termin, und plötzlich stimmen Zeitstempel nicht mehr. Dieser Artikel erklärt den technischen Unterschied zwischen TIMESTAMP WITH TIME ZONE und TIMESTAMP WITHOUT TIME ZONE, zeigt die häufigsten Fallstricke im Datenbankalltag und liefert eine klare, praktisch erprobte Empfehlung für sauberen Umgang mit Zeitzonen.

11 Min. Lesezeit UTC · Zeitzonen · Sommerzeit PostgreSQL · MySQL · SQL Server im Vergleich

1. TIMESTAMP WITH TIME ZONE vs. WITHOUT TIME ZONE: der technische Unterschied

TIMESTAMP WITHOUT TIME ZONE speichert nur einen nackten Kalenderwert, etwa 2026-06-15 14:30:00, ohne jede Information darüber, in welcher Zeitzone dieser Wert gemeint ist. Zwei Anwendungen können denselben Wert vollkommen unterschiedlich interpretieren, wenn sie unterschiedliche Annahmen über die gemeinte Zeitzone treffen, und die Datenbank selbst hat keine Möglichkeit, diese Mehrdeutigkeit aufzulösen.

TIMESTAMP WITH TIME ZONE, in PostgreSQL als timestamptz bekannt, löst das Problem, indem die Datenbank den eingehenden Wert bei einem Insert sofort in UTC umrechnet und intern ausschließlich UTC speichert. Bei einer Abfrage rechnet die Datenbank den Wert dann wieder in die Zeitzone der aktuellen Session um. Der Name ist dabei leicht irreführend, denn gespeichert wird keine Zeitzoneninformation selbst, sondern ausschließlich ein UTC-Zeitpunkt, dessen Anzeige von der Session-Zeitzone abhängt.


-- PostgreSQL: Eingabe in Session-Zeitzone, Speicherung intern als UTC
SET timezone = 'Europe/Berlin';
INSERT INTO termin (id, beginn) VALUES (1, '2026-06-15 14:30:00');

-- Dieselbe Zeile aus einer Session mit anderer Zeitzone gelesen
SET timezone = 'America/New_York';
SELECT beginn FROM termin WHERE id = 1;
-- liefert 2026-06-15 08:30:00-04, denselben UTC-Moment, andere Darstellung

2. Fallstrick 1: Speichern in Serverzeit statt in UTC

Wird ein naiver Zeitstempel-Typ ohne Zeitzoneninformation verwendet und die Anwendung schreibt dort direkt die lokale Serverzeit hinein, funktioniert das zunächst scheinbar problemlos, solange Server und alle Nutzerinnen in derselben Zeitzone sitzen. Sobald aber ein zweiter Anwendungsserver in einer anderen Zeitzone hinzukommt, eine Datenbankreplikation über Kontinente hinweg eingerichtet wird oder der Hosting-Anbieter wechselt, driften die gespeicherten Werte auseinander, ohne dass ein einziges SQL-Statement geändert wurde.

Besonders tückisch ist dieser Fehler bei Datenexporten und -importen zwischen Systemen: Ein CSV-Export aus einer Datenbank mit Serverzeit Europe/Berlin, importiert in ein System mit UTC-Annahme, verschiebt jeden einzelnen Zeitstempel um ein bis zwei Stunden, meist unbemerkt, bis jemand eine Bestellung findet, die scheinbar vor ihrer eigenen Erstellung bearbeitet wurde.

3. Fallstrick 2: Sommerzeit-Umstellung bei wiederkehrenden Terminen

Bei der Umstellung von Winter- auf Sommerzeit existiert eine lokale Uhrzeit, etwa 2:30 Uhr in Europe/Berlin, an diesem Tag gar nicht, weil die Uhr direkt von 2:00 auf 3:00 Uhr springt. Bei der Rückstellung von Sommer- auf Winterzeit existiert dieselbe lokale Uhrzeit dagegen zweimal, einmal noch in Sommerzeit und einmal bereits in Winterzeit. Ein naiv gespeicherter, wiederkehrender Termin um 2:30 Uhr kann an diesen beiden Tagen im Jahr also entweder gar nicht eindeutig oder doppeldeutig interpretiert werden.

Dieses Problem betrifft nicht nur seltene Randfälle: Jede Anwendung mit wiederkehrenden Terminen, etwa nächtlichen Batch-Jobs oder automatisierten Berichten, die exakt zu einer festen lokalen Uhrzeit laufen sollen, muss sich zweimal jährlich mit genau dieser Mehrdeutigkeit auseinandersetzen, wenn der Termin ausgerechnet in das betroffene Zeitfenster fällt.

4. Fallstrick 3: feste Offsets statt benannter Zeitzonen verwenden

Ein fester Offset wie +01:00 beschreibt nur den Versatz zu UTC an einem bestimmten Tag, nicht aber die Regel, wann sich dieser Versatz ändert. Europe/Berlin liegt je nach Jahreszeit bei +01:00 oder +02:00, und nur der benannte Zeitzonen-Bezeichner aus der IANA-Zeitzonendatenbank kennt die zugrunde liegende Umstellungsregel. Wer stattdessen nur einen festen Offset speichert, verliert genau diese Information und kann spätere Umstellungstermine nicht mehr korrekt nachvollziehen.

Für einen bereits vergangenen, punktuellen Zeitpunkt, etwa den Zeitstempel eines Log-Eintrags, ist ein fester UTC-Moment völlig ausreichend, weil er sich nicht mehr ändert. Für einen zukünftigen, wiederkehrenden Termin dagegen, etwa jeden Montag 9 Uhr Ortszeit, reicht ein fester Offset nicht, weil sich die Sommerzeitregel für dieses Datum in der Zukunft theoretisch noch ändern kann und die lokale Bedeutung erhalten bleiben soll.

5. Datenbankspezifika: PostgreSQL, MySQL und SQL Server im Vergleich

PostgreSQL bietet mit timestamptz eine konsistente, immer UTC-basierte Speicherung mit automatischer Umrechnung anhand der Session-Zeitzone. MySQL und MariaDB unterscheiden zwischen TIMESTAMP, das intern ebenfalls in UTC gespeichert und bei Lese- und Schreibzugriffen anhand der Session-Zeitzone konvertiert wird, aber auf den Bereich von 1970 bis 2038 begrenzt ist, und DATETIME, das einen reinen Kalenderwert ohne jede Zeitzonenumrechnung speichert.

SQL Server bietet mit datetimeoffset einen Typ, der explizit sowohl den UTC-Zeitpunkt als auch den ursprünglichen Offset speichert, jedoch ohne den Namen einer Zeitzone, und mit datetime2 einen reinen, zeitzonenlosen Kalenderwert. Für alle drei Systeme gilt: Der zeitzonenbewusste Typ übernimmt die UTC-Umrechnung automatisch, der einfache Typ überlässt sie vollständig der Anwendung.


-- MySQL: TIMESTAMP konvertiert automatisch anhand der Session-Zeitzone
SET time_zone = '+02:00';
INSERT INTO log_eintrag (id, erstellt_um) VALUES (1, NOW());

SET time_zone = '+00:00';
SELECT erstellt_um FROM log_eintrag WHERE id = 1;
-- liefert denselben Moment, zwei Stunden früher dargestellt

6. Praktische Empfehlung: immer UTC speichern, Zeitzone erst bei der Anzeige anwenden

Die robusteste Grundregel lautet: In der Datenbank wird ausschließlich UTC gespeichert, entweder über einen zeitzonenbewussten Typ wie timestamptz, der das automatisch übernimmt, oder über einen zeitzonenlosen Typ, dessen Werte aber diszipliniert immer als UTC interpretiert werden. Die Umrechnung in die Zeitzone einer Nutzerin geschieht ausschließlich in der Präsentationsschicht, niemals in der Datenbank oder in einer mittleren Verarbeitungsschicht.

Für diese Umrechnung sollte immer die IANA-Zeitzonendatenbank mit ihren benannten Bezeichnern wie Europe/Berlin oder America/New_York verwendet werden, nicht feste Offsets, damit Sommerzeitregeln automatisch korrekt angewendet werden und sich zukünftige gesetzliche Änderungen an diesen Regeln, wie sie in der Vergangenheit bereits mehrfach vorkamen, zentral über ein Datenbank-Update der Zeitzonen-Bibliothek statt über Anwendungscode nachziehen lassen.

7. Sonderfall: wiederkehrende zukünftige Termine korrekt speichern

Für einen wiederkehrenden zukünftigen Termin, etwa jeden Montag 9 Uhr Ortszeit in Berlin, ist eine UTC-Vorberechnung des nächsten Auftritts problematisch, weil sich die Sommerzeitregel zwischen heute und dem tatsächlichen Termin theoretisch ändern könnte und der Nutzer eigentlich weiterhin exakt 9 Uhr Ortszeit meint, nicht einen festen UTC-Moment. Hier ist es robuster, die lokale Uhrzeit zusammen mit dem Zeitzonen-Bezeichner zu speichern und den tatsächlichen UTC-Moment erst kurz vor Fälligkeit zu berechnen.

Für bereits vergangene, punktuelle Ereignisse gilt dagegen das Gegenteil: Hier ist die einmal berechnete UTC-Angabe endgültig und sollte nicht mehr aus einer möglicherweise inzwischen geänderten Zeitzonenregel neu berechnet werden, da sonst historische Datensätze rückwirkend ihre Bedeutung ändern würden.

8. Migrationsstrategie für bestehende naive Timestamp-Spalten

Bei einer bestehenden Spalte mit naiven, zeitzonenlosen Werten muss zunächst geklärt werden, welche Zeitzone tatsächlich gemeint war, meist die historische Serverzeitzone zum jeweiligen Erstellungszeitpunkt der Daten, einschließlich der damals geltenden Sommerzeitregel. Erst nach dieser Klärung lässt sich eine neue, zeitzonenbewusste Spalte korrekt befüllen, üblicherweise über ein einmaliges Backfill-Skript mit anschließender Umstellung aller schreibenden Codepfade auf den neuen Typ.

Während der Übergangsphase empfiehlt sich, beide Spalten parallel zu pflegen und über automatisierte Konsistenzprüfungen sicherzustellen, dass alte und neue Spalte tatsächlich denselben Moment beschreiben, bevor die alte, naive Spalte endgültig entfernt wird.


-- Bestehende naive Spalte auf eine zeitzonenbewusste Spalte migrieren
ALTER TABLE termin ADD COLUMN beginn_utc TIMESTAMPTZ;

UPDATE termin
SET beginn_utc = beginn AT TIME ZONE 'Europe/Berlin'
WHERE beginn_utc IS NULL;

9. Zeitzonen-Bugs durch gezieltes Testing und Monitoring aufdecken

Zeitzonen-Fehler zeigen sich selten in einer Standard-Testumgebung mit fest eingestellter UTC-Zeitzone auf allen Systemen. Wirksame Tests setzen die Session-Zeitzone bewusst auf unterschiedliche, auch stark abweichende Werte wie Pacific/Auckland und prüfen gezielt Randfälle rund um Mitternacht sowie die konkreten Umstellungstage von Sommer- auf Winterzeit und zurück.

Ergänzend hilft ein einfaches Monitoring, das Datenbank-, Anwendungsserver- und Betriebssystem-Zeitzone regelmäßig gegen einen erwarteten Wert abgleicht, denn ein unbemerkt geänderter Serverzeitzonen-Wert nach einem Betriebssystem-Update ist in der Praxis eine überraschend häufige Fehlerursache für plötzlich verschobene Zeitstempel.

Datenbank Zeitzonenbewusster Typ Zeitzonenloser Typ Interne Speicherung
PostgreSQL timestamptz timestamp Immer UTC, Session-Zeitzone bei Anzeige
MySQL / MariaDB TIMESTAMP DATETIME TIMESTAMP intern UTC, begrenzt bis 2038
SQL Server datetimeoffset datetime2 UTC plus gespeicherter Offset, kein Zonenname
Oracle TIMESTAMP WITH TIME ZONE TIMESTAMP Zeitzonenname zusätzlich gespeichert
Empfehlung Für alle neuen Anwendungen verwenden Nur mit expliziter UTC-Disziplin UTC in der Datenbank, Zeitzone bei Anzeige

Mironsoft

Datenbank-Optimierung, Query-Tuning und Migrationen

SQL-Abfragen, die bei Wachstum immer langsamer werden?

Wir analysieren und optimieren SQL-Datenbanken unabhängig vom eingesetzten System, planen sichere Migrationen und Schema-Änderungen und bringen Teams Query-Optimierung praxisnah bei.

Query-Optimierung

Langsame Abfragen analysieren und mit Indizes und Explain-Plänen gezielt beschleunigen.

Migrations-Planung

Schema-Änderungen und Datenmigrationen sicher und ohne Downtime umsetzen.

Team-Schulung

SQL-Grundlagen und Performance-Denken praxisnah im Entwicklerteam verankern.

10. Zusammenfassung

Zeitzonen in SQL: das Wichtigste auf einen Blick

Grundregel

Immer UTC in der Datenbank speichern, Zeitzone erst in der Präsentationsschicht anwenden.

Sommerzeit

Lokale Uhrzeiten sind an Umstellungstagen nicht eindeutig, wiederkehrende Termine brauchen Sonderbehandlung.

Namen statt Offsets

IANA-Zeitzonenbezeichner statt fester Offsets für zukünftige, wiederkehrende Termine verwenden.

Datenbankwahl

timestamptz, TIMESTAMP oder datetimeoffset übernehmen die UTC-Umrechnung automatisch.

11. FAQ: Zeitzonen in SQL: das Wichtigste auf einen Blick

1Was ist der Unterschied zwischen TIMESTAMP WITH TIME ZONE und WITHOUT TIME ZONE?
WITH TIME ZONE speichert intern immer einen UTC-Moment und rechnet ihn bei Anzeige in die Session-Zeitzone um. WITHOUT TIME ZONE speichert nur einen nackten Kalenderwert ohne jede Information über die gemeinte Zeitzone.
2Warum ist es riskant, Serverzeit statt UTC zu speichern?
Sobald ein zweiter Server in anderer Zeitzone, eine Replikation über Kontinente hinweg oder ein Hosting-Wechsel hinzukommt, driften gespeicherte Werte auseinander, ohne dass am Code etwas geändert wurde. Der Fehler bleibt oft lange unbemerkt.
3Warum ist eine lokale Uhrzeit bei der Sommerzeit-Umstellung problematisch?
Bei der Umstellung auf Sommerzeit existiert eine bestimmte lokale Uhrzeit gar nicht, bei der Rückstellung existiert dieselbe Uhrzeit zweimal. Ein naiv gespeicherter, wiederkehrender Termin kann an diesen Tagen mehrdeutig sein.
4Warum reicht ein fester Offset wie plus eins Stunde nicht aus?
Ein fester Offset kennt nicht die zugrunde liegende Umstellungsregel zwischen Sommer- und Winterzeit. Nur ein benannter Zeitzonenbezeichner aus der IANA-Datenbank weiß, wann sich der Offset im Jahresverlauf ändert.
5Wie speichert MySQL Zeitzonen intern?
TIMESTAMP wird intern in UTC gespeichert und bei Lese- und Schreibzugriffen anhand der Session-Zeitzone konvertiert, ist aber auf den Bereich 1970 bis 2038 begrenzt. DATETIME speichert dagegen einen reinen Kalenderwert ohne jede Umrechnung.
6Was ist die praktische Grundregel für den Umgang mit Zeitzonen?
In der Datenbank wird ausschließlich UTC gespeichert, entweder automatisch über einen zeitzonenbewussten Typ oder diszipliniert über einen zeitzonenlosen Typ. Die Umrechnung in die Nutzerzeitzone geschieht ausschließlich in der Präsentationsschicht.
7Wie behandle ich wiederkehrende zukünftige Termine korrekt?
Am robustesten wird die lokale Uhrzeit zusammen mit dem Zeitzonen-Bezeichner gespeichert, und der tatsächliche UTC-Moment erst kurz vor Fälligkeit berechnet, damit spätere Änderungen der Sommerzeitregel korrekt berücksichtigt werden.
8Wie migriere ich eine bestehende naive Timestamp-Spalte sicher?
Zuerst klären, welche Zeitzone historisch tatsächlich gemeint war, dann eine neue zeitzonenbewusste Spalte per Backfill befüllen, beide Spalten übergangsweise parallel pflegen und erst nach Konsistenzprüfung die alte Spalte entfernen.
9Wie finde ich Zeitzonen-Bugs zuverlässig durch Testing?
Durch bewusstes Testen mit unterschiedlichen, auch stark abweichenden Session-Zeitzonen sowie gezielten Tests rund um Mitternacht und die konkreten Umstellungstage von Sommer- auf Winterzeit und zurück.
10Was ist bei Oracle im Vergleich zu PostgreSQL und SQL Server anders?
Oracles TIMESTAMP WITH TIME ZONE speichert zusätzlich den Namen der ursprünglichen Zeitzone, während PostgreSQLs timestamptz nur den UTC-Moment kennt und SQL Servers datetimeoffset lediglich den numerischen Offset ohne Zonennamen speichert.