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.