Unterschied zwischen Zeitstempeln mit / ohne Zeitzone in PostgreSQL

186

Werden Zeitstempelwerte in PostgreSQL unterschiedlich gespeichert, wenn der Datentyp WITH TIME ZONEversus ist WITHOUT TIME ZONE? Können die Unterschiede mit einfachen Testfällen veranschaulicht werden?

Larsenal
quelle
3
Diese verwandte Antwort kann hilfreich sein.
Erwin Brandstetter

Antworten:

155

Die Unterschiede werden in der PostgreSQL-Dokumentation für Datums- / Zeittypen behandelt . Ja, die Behandlung von TIMEoder TIMESTAMPunterscheidet sich zwischen einem WITH TIME ZONEoder WITHOUT TIME ZONE. Es hat keinen Einfluss darauf, wie die Werte gespeichert werden. es beeinflusst, wie sie interpretiert werden.

Die Auswirkungen von Zeitzonen auf diese Datentypen werden in den Dokumenten speziell behandelt . Der Unterschied ergibt sich aus dem, was das System vernünftigerweise über den Wert wissen kann:

  • Mit einer Zeitzone als Teil des Werts kann der Wert als lokale Zeit im Client gerendert werden.

  • Ohne eine Zeitzone als Teil des Werts ist die offensichtliche Standardzeitzone UTC, sodass sie für diese Zeitzone gerendert wird.

Das Verhalten unterscheidet sich in Abhängigkeit von mindestens drei Faktoren:

  • Die Zeitzoneneinstellung im Client.
  • Der Datentyp (dh WITH TIME ZONEoder WITHOUT TIME ZONE) des Werts.
  • Gibt an, ob der Wert mit einer bestimmten Zeitzone angegeben ist.

Hier sind Beispiele für die Kombinationen dieser Faktoren:

foo=> SET TIMEZONE TO 'Japan';
SET
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP;
      timestamp      
---------------------
 2011-01-01 00:00:00
(1 row)

foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP WITH TIME ZONE;
      timestamptz       
------------------------
 2011-01-01 00:00:00+09
(1 row)

foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP;
      timestamp      
---------------------
 2011-01-01 00:00:00
(1 row)

foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP WITH TIME ZONE;
      timestamptz       
------------------------
 2011-01-01 06:00:00+09
(1 row)

foo=> SET TIMEZONE TO 'Australia/Melbourne';
SET
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP;
      timestamp      
---------------------
 2011-01-01 00:00:00
(1 row)

foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP WITH TIME ZONE;
      timestamptz       
------------------------
 2011-01-01 00:00:00+11
(1 row)

foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP;
      timestamp      
---------------------
 2011-01-01 00:00:00
(1 row)

foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP WITH TIME ZONE;
      timestamptz       
------------------------
 2011-01-01 08:00:00+11
(1 row)
große Nase
quelle
88
Korrigieren Sie nur, wenn Sie sich auf das Einfügen / Abrufen von Werten beziehen. Die Leser sollten jedoch verstehen, dass beide Datentypen timestamp with time zoneund timestamp without time zonein Postgres * keine Zeitzoneninformationen speichern. Sie können dies mit einem Blick auf die Datentyp-Dokumentseite bestätigen: Beide Typen belegen die gleiche Anzahl von Oktetten und verfügen über den gespeicherten Wertebereich, sodass kein Platz zum Speichern von Zeitzoneninformationen vorhanden ist. Der Text der Seite bestätigt dies. Etwas falsch: "ohne tz" bedeutet "Versatz beim Einfügen von Daten ignorieren" und "mit tz" bedeutet "Versatz zum Anpassen an UTC verwenden".
Basil Bourque
41
Die Datentypen sind in zweiter Linie eine Fehlbezeichnung: Sie sagen "Zeitzone", aber tatsächlich sprechen wir über einen Versatz von UTC / GMT. Eine Zeitzone ist eigentlich ein Versatz plus Regeln / Verlauf über Sommerzeit (DST) und andere Anomalien.
Basil Bourque
4
Ich würde eher sagen, dass ein Offset eine Zeitzone plus Regeln für die Sommerzeit ist. Sie können die Zeitzone nicht anhand eines Versatzes ermitteln, aber Sie können den Versatz anhand der Zeitzone und der Sommerzeitregeln ermitteln.
igorsantos07
3
Zitieren des offiziellen Dokuments : Alle zeitzonenbezogenen Daten und Zeiten werden intern in UTC gespeichert. Sie werden in der durch den TimeZone-Konfigurationsparameter angegebenen Zone in die Ortszeit konvertiert, bevor sie dem Client angezeigt werden.
Guillaume Husta
2
@ igorsantos07 Eine Zeitzone ist der Satz von Regeln / Verlauf über DST-Änderungen und andere Änderungen. Ihr Wortlaut erscheint überflüssig. Und Ihre Aussage, dass "ein Versatz eine Zeitzone plus Regeln für die Sommerzeit ist", ist einfach falsch: Ein Versatz besteht lediglich aus einer Anzahl von Stunden, Minuten und Sekunden - nicht mehr und nicht weniger.
Basil Bourque
33

Ich versuche es verständlicher zu erklären als die verwiesene PostgreSQL-Dokumentation.

Keine der TIMESTAMPVarianten speichert eine Zeitzone (oder einen Versatz), trotz der Namen. Der Unterschied liegt in der Interpretation der gespeicherten Daten (und in der beabsichtigten Anwendung), nicht im Speicherformat selbst:

  • TIMESTAMP WITHOUT TIME ZONEspeichert die lokale Datums- und Uhrzeit (auch bekannt als Wandkalenderdatum und Wanduhrzeit). Die Zeitzone ist nicht angegeben, soweit PostgreSQL dies beurteilen kann (obwohl Ihre Anwendung möglicherweise weiß, was es ist). Daher führt PostgreSQL keine zeitzonenbezogene Konvertierung bei Eingabe oder Ausgabe durch. Wenn der Wert in die Datenbank eingegeben wurde als '2011-07-01 06:30:30', egal in welcher Zeitzone Sie ihn später anzeigen, wird immer noch Jahr 2011, Monat 07, Tag 01, 06 Stunden, 30 Minuten und 30 Sekunden (in einem bestimmten Format) angezeigt. Auch jede Offset- oder Zeitzone in der Eingabe angeben wird von PostgreSQL ignoriert, so '2011-07-01 06:30:30+00'und '2011-07-01 06:30:30+05'sind die gleichen wie gerade '2011-07-01 06:30:30'. Für Java-Entwickler: Es ist analog zu java.time.LocalDateTime.

  • TIMESTAMP WITH TIME ZONEspeichert einen Punkt auf der UTC-Zeitlinie. Wie es aussieht (wie viele Stunden, Minuten usw.), hängt von Ihrer Zeitzone ab, bezieht sich jedoch immer auf denselben "physischen" Moment (wie den Moment eines tatsächlichen physischen Ereignisses). Die Eingabe wird intern in UTC konvertiert und so gespeichert. Dazu muss der Versatz der Eingabe bekannt sein. Wenn die Eingabe keinen expliziten Versatz oder keine Zeitzone (wie '2011-07-01 06:30:30') enthält, wird angenommen, dass sie sich in der aktuellen Zeitzone der PostgreSQL-Sitzung befindet, andernfalls wird der explizit angegebene Versatz oder die explizit angegebene Zeitzone verwendet (wie in '2011-07-01 06:30:30+05'). Die Ausgabe wird in die aktuelle Zeitzone der PostgreSQL-Sitzung konvertiert angezeigt. Für Java-Entwickler: Es ist analog zu java.time.Instant(allerdings mit niedrigerer Auflösung), aber mit JDBC und JPA 2.2 sollten Sie es java.time.OffsetDateTime(oder zu java.util.Dateoder) zuordnenjava.sql.Timestamp natürlich).

Einige sagen, dass beide TIMESTAMPVarianten UTC-Datum und Uhrzeit speichern. Irgendwie, aber es ist meiner Meinung nach verwirrend, es so auszudrücken. TIMESTAMP WITHOUT TIME ZONEwird wie eine gespeichert TIMESTAMP WITH TIME ZONE, die mit der UTC-Zeitzone gerendert wird und zufällig das gleiche Jahr, den gleichen Monat, den gleichen Tag, die gleichen Stunden, Minuten, Minuten, Sekunden und Mikrosekunden wie in der lokalen Datums- und Uhrzeitangabe angibt. Es soll jedoch nicht den Punkt auf der Zeitlinie darstellen, den die UTC-Interpretation angibt, sondern nur die Art und Weise, wie die lokalen Datums- und Zeitfelder codiert werden. (Es handelt sich um eine Gruppe von Punkten auf der Zeitachse, da die Echtzeitzone nicht UTC ist. Wir wissen nicht, was es ist.)

ddekany
quelle
Es ist nichts Falsches daran, a TIMESTAMP WITH TIME ZONEals Instant. Beide repräsentieren einen Punkt auf der Zeitachse in UTC. Instantwird meiner Meinung nach vorgezogen, OffsetDateTimeda es selbstdokumentierender ist: A TIMESTAMP WITH TIME ZONEwird immer als UTC aus der Datenbank abgerufen, und a Instantist immer in UTC, also eine natürliche Übereinstimmung, während a OffsetDateTimeandere Offsets tragen kann.
Basil Bourque
@BasilBourque Leider werden in der aktuellen JDBC-Spezifikation, der JPA 2.2-Spezifikation und auch in der PostgreSQL-JDBC-Dokumentation nur OffsetDateTimeder zugeordnete Java-Typ erwähnt. Ich bin mir nicht sicher, ob Instanceirgendwo noch inoffiziell unterstützt wird.
ddekany
Frage, Sie sagen, jeder Offset, den ich in der Eingabe angegeben habe, wie '2011-07-01 06:30:30+00'und '2011-07-01 06:30:30+05' wird ignoriert, aber ich bin in der Lage, insert into test_table (date) values ('2018-03-24T00:00:00-05:00'::timestamptz);und es wird es korrekt in utc konvertieren. Dabei ist Datum ein Zeitstempel ohne Zeitzone. Ich versuche zu verstehen, was der Hauptwert von Zeitstempel mit Zeitzone ist und habe Probleme.
pk1m
@ pk1m Du erschwerst die Sache mit dem ::timestamptz. Damit konvertieren Sie die Zeichenfolge in TIMESTAMP WITH TIME ZONEund wenn diese weiter konvertiert WITHOUT TIME ZONEwird, werden der "Wandkalender" -Tag und die Wanduhrzeit dieses Augenblicks aus Ihrer Sitzungszeitzone (möglicherweise UTC) gespeichert. Es wird immer noch nur ein lokaler Zeitstempel mit nicht angegebenem Versatz (keine Zone) sein.
Ddekany
Ich arbeite mit Python, und genau das wird eingefügt, wenn ein zeitstempelabhängiges Datettime-Objekt eingefügt wird. Es scheint mir, dass es sinnvoll ist, Zeitstempel mit Zeitzone zu verwenden, aber es ist nicht notwendig, Zeitzonen zu behandeln.
pk1m
12

Hier ist ein Beispiel, das helfen sollte. Wenn Sie einen Zeitstempel mit einer Zeitzone haben, können Sie diesen Zeitstempel in eine andere Zeitzone konvertieren. Wenn Sie keine Basiszeitzone haben, wird diese nicht korrekt konvertiert.

SELECT now(),
   now()::timestamp,
   now() AT TIME ZONE 'CST',
   now()::timestamp AT TIME ZONE 'CST'

Ausgabe:

-[ RECORD 1 ]---------------------------
now      | 2018-09-15 17:01:36.399357+03
now      | 2018-09-15 17:01:36.399357
timezone | 2018-09-15 08:01:36.399357
timezone | 2018-09-16 02:01:36.399357+03
serby
quelle
5
Die Aussage "wird nicht korrekt konvertiert" ist einfach nicht wahr. Sie müssen verstehen, was timestampund timestamptzmeinen. timestamptzbedeutet einen absoluten Zeitpunkt (UTC), während timestampbezeichnet wird, was die Uhr in einer bestimmten Zeitzone zeigte. Wenn Sie also timestamptzin eine Zeitzone konvertieren , fragen Sie sich, was die Uhr zu diesem absoluten Zeitpunkt in New York gezeigt hat? Während Sie beim "Konvertieren" von a timestampfragen, was der absolute Zeitpunkt war, an dem die Uhr in New York x zeigte?
Fphilipe
Das AT TIME ZONEKonstrukt ist ein eigener Denksport, auch wenn Sie die WITHvs.- WITHOUT TIME ZONETypen bereits verstehen . Es ist also eine merkwürdige Wahl, sie zu erklären. (: ( AT TIME ZONEkonvertiert einen WITH TIME ZONEZeitstempel in einen WITHOUT TIME ZONEZeitstempel und umgekehrt ... nicht genau offensichtlich.)
ddekany
now()::timestamp AT TIME ZONE 'CST'macht keinen Sinn, es sei denn, Sie würden zu welchem ​​Zeitpunkt eine Uhr für die Zone 'CST' die Zeit anzeigen, die Ihre lokale Uhr gerade anzeigt
Jasen