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

Konvertieren zwischen Zeitzonen in Postgres

Lassen Sie mich die beiden Beispiele erläutern:

In beiden gehen wir von einer Zeitzone UTC aus (also SET timezone TO UTC ).

db=# SELECT timezone('US/Pacific', '2016-01-01 00:00');
      timezone
---------------------
 2015-12-31 16:00:00
(1 row)

Dies entspricht SELECT timezone('US/Pacific', '2016-01-01 00:00'::timestamptz) , d. h. Postgres hat den String implizit in einen timestamptz konvertiert .

Wir wissen, dass die timezone Funktion konvertiert hin und her zwischen timestamp und timestamptz :

Da wir ihm einen timestamptz geben Als Eingabe wird ein timestamp ausgegeben . Mit anderen Worten, es wird der absolute Zeitpunkt 2016-01-01 00:00Z konvertiert zu einer Wandzeit in US/Pacific , also was die Uhr in Los Angeles zu diesem absoluten Zeitpunkt anzeigte.

In Beispiel 2 machen wir das Gegenteil, nämlich einen timestamp und in einen timestamptz umzuwandeln . Mit anderen Worten, wir fragen:Was war der absolute Zeitpunkt, an dem die Uhr in Los Angeles 2016-01-01 00:00 zeigte ?

Sie erwähnen:

'2016-01-01 00:00'::timestamp ist ein timestamp , also eine Wandzeit. Es hat keine Zeitzone.

Ich denke, Sie haben den Unterschied zwischen timestamp möglicherweise nicht vollständig verstanden und timestamptz , was hier entscheidend ist. Betrachten Sie sie einfach als Wandzeit , also die Zeit, die irgendwo auf der Welt auf einer an der Wand hängenden Uhr angezeigt wird, und absolute Zeit , also die absolute Zeit in unserem Universum.

Die Beispiele, die Sie in Ihrer eigenen Antwort geben, sind nicht ganz genau.

SELECT ts FROM  (VALUES
(timestamptz '2012-03-05 17:00:00+0') -- outputs 2012-03-05 17:00:00+00 --1
,(timestamptz '2012-03-05 18:00:00+1') -- outputs 2012-03-05 17:00:00+00 --2
,(timestamp   '2012-03-05 18:00:00+1') -- outputs 2012-03-05 18:00:00+00 --3
,(timestamp   '2012-03-05 11:00:00'  AT TIME ZONE '+6') -- outputs 2012-03-05 17:00:00+00 --4
,(timestamp   '2012-03-05 17:00:00'  AT TIME ZONE 'UTC') -- outputs 2012-03-05 17:00:00+00 --5
,(timestamp   '2012-03-05 17:00:00'::timestamp) -- outputs 2012-03-05 17:00:00+00 --6
,(timestamp   '2012-03-05 17:00:00'::timestamptz) -- outputs 2012-03-05 17:00:00+00 --7
    ) t(ts);

Das Problem bei Ihrem Beispiel besteht darin, dass Sie einen Datensatz mit einer einzelnen Spalte erstellen. Da eine Spalte nur einen Typ haben kann, wird jede Zeile (oder in diesem Fall Einzelwert) in den gleichen Typ konvertiert, nämlich timestamptz , obwohl einige Werte als timestamp berechnet wurden (zB Wert 3). Somit haben Sie hier eine zusätzliche implizite Konvertierung.

Lassen Sie uns das Beispiel in separate Abfragen aufteilen und sehen, was vor sich geht:

Beispiel 1

db=# SELECT timestamptz '2012-03-05 17:00:00+0';
      timestamptz
------------------------
 2012-03-05 17:00:00+00

Wie Sie vielleicht bereits wissen, timestamptz '2012-03-05 17:00:00+0' und '2012-03-05 17:00:00+0'::timestamptz gleichwertig sind (ich bevorzuge letzteres). Um also dieselbe Syntax wie im Artikel zu verwenden, werde ich Folgendes umschreiben:

db=# SELECT '2012-03-05 17:00:00+0'::timestamptz;
      timestamptz
------------------------
 2012-03-05 17:00:00+00

Nun, was ist hier los? Nun, weniger als in Ihrer ursprünglichen Erklärung. Der String wird einfach als timestamptz geparst . Wenn das Ergebnis gedruckt wird, verwendet es die aktuell eingestellte timezone config, um sie zurück in eine menschenlesbare Darstellung der zugrunde liegenden Datenstruktur zu konvertieren, d. h. 2012-03-05 17:00:00+00 .

Lassen Sie uns die timezone ändern config und schau was passiert:

db=# SET timezone TO 'Europe/Berlin';
SET
db=# SELECT '2012-03-05 17:00:00+0'::timestamptz;
      timestamptz
------------------------
 2012-03-05 18:00:00+01

Das einzige, was sich geändert hat, ist wie der timestamptz auf den Bildschirm gedruckt wird, und zwar mit dem Europa/Berlin Zeitzone.

Beispiel 2

db=# SELECT timestamptz '2012-03-05 18:00:00+1';
      timestamptz
------------------------
 2012-03-05 17:00:00+00
(1 row)

Wieder nur das Datum parsen.

Beispiel 3

db=# SELECT timestamp '2012-03-05 18:00:00+1';
      timestamp
---------------------
 2012-03-05 18:00:00
(1 row)

Dies ist dasselbe wie '2012-03-05 18:00:00+1'::timestamp . Was hier passiert, ist, dass der Zeitzonenoffset einfach ignoriert wird, weil Sie nach einem timestamp fragen .

Beispiel 4

db=# SELECT timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6';
        timezone
------------------------
 2012-03-05 17:00:00+00
(1 row)

Lassen Sie uns umschreiben, um es einfacher zu machen:

db=# SELECT timezone('+6', '2012-03-05 11:00:00'::timestamp);
        timezone
------------------------
 2012-03-05 17:00:00+00
(1 row)

Dies ist die Frage:Was war die absolute Zeit, als die Uhr an der Wand in der Zeitzone mit einer Abweichung von +6 Stunden 2012-03-05 11:00:00 anzeigte ?

Beispiel 5

db=# SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC';
        timezone
------------------------
 2012-03-05 17:00:00+00
(1 row)

Schreiben wir um:

db=# SELECT timezone('UTC', '2012-03-05 17:00:00'::timestamp);
        timezone
------------------------
 2012-03-05 17:00:00+00
(1 row)

Dies fragt:Was war die absolute Zeit, als die Uhr an der Wand in der Zeitzone UTC 2012-03-05 17:00:00 anzeigte ?

Beispiel 6

db=# SELECT timestamp '2012-03-05 17:00:00'::timestamp;
      timestamp
---------------------
 2012-03-05 17:00:00
(1 row)

Hier wird zweimal auf timestamp gecastet , was keinen Unterschied macht. Vereinfachen wir:

db=# SELECT '2012-03-05 17:00:00'::timestamp;
      timestamp
---------------------
 2012-03-05 17:00:00
(1 row)

Das ist klar, denke ich.

Beispiel 7

db=# SELECT timestamp '2012-03-05 17:00:00'::timestamptz;
      timestamptz
------------------------
 2012-03-05 17:00:00+00
(1 row)

Schreiben wir um:

db=# SELECT ('2012-03-05 17:00:00'::timestamp)::timestamptz;
      timestamptz
------------------------
 2012-03-05 17:00:00+00
(1 row)

Zuerst parsen Sie den String als timestamp und dann in einen timestamptz umzuwandeln unter Verwendung der aktuell eingestellten timezone . Wenn wir die timezone ändern , erhalten wir etwas anderes, da Postgres diese Zeitzone beim Konvertieren eines timestamp annimmt (oder eine Zeichenfolge ohne Zeitzoneninformationen) in timestamptz :

db=# SET timezone TO 'Europe/Berlin';
SET
db=# SELECT ('2012-03-05 17:00:00'::timestamp)::timestamptz;
      timestamptz
------------------------
 2012-03-05 17:00:00+01
(1 row)

Diese absolute Zeit, ausgedrückt in UTC, ist 2012-03-05 16:00:00+00 , also anders als das ursprüngliche Beispiel.

Ich hoffe, das verdeutlicht die Dinge. Nochmals zum Verständnis des Unterschieds zwischen timestamp und timestamptz ist der Schlüssel. Stellen Sie sich die Wandzeit im Vergleich zur absoluten Zeit vor.