PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

PostgreSQL konvertiert falsch von Zeitstempel ohne Zeitzone zu Zeitstempel mit Zeitzone

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';