Wichtige Dinge, die Sie verstehen sollten
timestamp without time zone AT TIME ZONE
neuinterpretiert ein timestamp
als in dieser Zeitzone liegend zum Zweck der Umrechnung in UTC .
timestamp with time zone AT TIME ZONE
konvertiert ein timestamptz
in einen timestamp
in der angegebenen Zeitzone.
PostgreSQL verwendet ISO-8601-Zeitzonen, die angeben, dass östlich von Greenwich positiv ist ... es sei denn, Sie verwenden einen POSIX-Zeitzonenbezeichner, in diesem Fall folgt er auf POSIX. Wahnsinn folgt.
Warum der erste zu einem unerwarteten Ergebnis führt
Zeitstempel und Zeitzonen in SQL sind schrecklich. Dies:
select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'EST5EDT';
interpretiert das unbekannte Literal '2011-12-30 00:30:00'
als timestamp without time zone
, von dem Pg annimmt, dass es sich in der lokalen Zeitzone befindet, sofern nicht anders angegeben. Wenn Sie AT TIME ZONE
verwenden , es wird (gemäß der Spezifikation) neu interpretiert als timestamp with time zone
in der Zeitzone EST5EDT
dann als absolute Zeit in UTC gespeichert - also von umgerechnet EST5EDT
an UTC, d.h. der Zeitzonenoffset wird abgezogen . x - (-5)
ist x + 5
.
Dieser an die UTC-Speicherung angepasste Zeitstempel wird dann für Ihre Server-TimeZone
angepasst Einstellung für die Anzeige, damit sie in Ortszeit angezeigt wird.
Wenn Sie stattdessen sagen möchten "Ich habe diesen Zeitstempel in UTC-Zeit und möchte sehen, was die entsprechende Ortszeit in EST5EDT ist", wenn Sie unabhängig von der Zeitzoneneinstellung des Servers sein möchten, müssen Sie etwas schreiben wie:P>
select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
AT TIME ZONE 'EST5EDT';
Hier steht:„Gegebener Zeitstempel 2011-12-30 00:30:00, behandle ihn als Zeitstempel in UTC bei der Konvertierung in timestamptz, dann konvertiere diesen Zeitstempel in eine Ortszeit in EST5EDT.“
Schrecklich, oder? Ich möchte fest reden wer auch immer sich für die verrückte Semantik von AT TIME ZONE
entschieden hat - es sollte wirklich so etwas wie timestamp CONVERT FROM TIME ZONE '-5'
sein und timestamptz CONVERT TO TIME ZONE '+5'
. Außerdem timestamp with time zone
sollte eigentlich seine Zeitzone mit sich führen, nicht in UTC gespeichert und automatisch in Ortszeit konvertiert werden.
Warum die zweite funktioniert (solange TimeZone =UTC)
Ihre ursprüngliche "Werks"-Version:
select '2011-12-30 00:30:00' AT TIME ZONE 'EST5EDT';
ist nur korrekt, wenn TimeZone auf UTC eingestellt ist, da die Text-zu-Timestamptz-Umwandlung TimeZone annimmt, wenn keine angegeben ist.
Warum der dritte funktioniert
Zwei Probleme heben sich gegenseitig auf.
Die andere Version, die zu funktionieren scheint, ist TimeZone-unabhängig, funktioniert aber nur, weil sich zwei Probleme gegenseitig aufheben. Zuerst, wie oben erklärt, timestamp without time zone AT TIME ZONE
neuinterpretiert der Zeitstempel als in dieser Zeitzone liegend für die Umwandlung in einen UTC-Zeitstempeltz; das subtrahiert effektiv der Zeitzonen-Offset.
Aus Gründen, die ich nicht kenne, verwendet PostgreSQL jedoch Zeitstempel mit dem umgekehrten Vorzeichen zu dem, was ich an die meisten Orte gewöhnt bin. Siehe Dokumentation:
Ein weiteres zu beachtendes Problem ist, dass in POSIX-Zeitzonennamen positive Offsets für Standorte westlich von Greenwich verwendet werden. Überall sonst folgt PostgreSQL der ISO-8601-Konvention, dass positive Zeitzonen-Offsets östlich von Greenwich sind.
Das bedeutet, dass EST5EDT
ist dasselbe wie +5
, nicht -5
. Aus diesem Grund funktioniert es:weil Sie den tz-Offset subtrahieren und nicht addieren, sondern einen negierten Offset subtrahieren!
Was Sie brauchen, um es richtig zu machen, ist stattdessen:
select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
AT TIME ZONE '+5';