Der Datentyp timestamp
ist der Kurzname für timestamp without time zone
.
Die andere Option timestamptz
ist die Abkürzung für timestamp with time zone
.
timestamptz
ist buchstäblich der bevorzugte Typ in der Datums- / Zeitfamilie. Es hat typispreferred
eingesetzt pg_type
, was relevant sein kann:
Interner Speicher und Epoche
Intern belegen Zeitstempel 8 Byte Speicherplatz auf der Festplatte und im RAM. Dies ist ein ganzzahliger Wert, der die Anzahl der Mikrosekunden aus der Postgres-Epoche 2000-01-01 00:00:00 UTC darstellt.
Postgres verfügt außerdem über integrierte Kenntnisse über die häufig verwendete UNIX- Zeitzählung von Sekunden aus der UNIX-Epoche 1970-01-01 00:00:00 UTC und verwendet diese in Funktionen to_timestamp(double precision)
oder EXTRACT(EPOCH FROM timestamptz)
.
Der Quellcode:
* Zeitstempel sowie die Intervallfelder h / m / s werden als gespeichert
* int64-Werte mit Einheiten von Mikrosekunden. (Es war einmal
* doppelte Werte mit Einheiten von Sekunden.)
Und:
/ * Julianische Datumsäquivalente von Tag 0 in Unix- und Postgres-Abrechnung * /
#define UNIX_EPOCH_JDATE 2440588 / * == date2j (1970, 1, 1) * /
#define POSTGRES_EPOCH_JDATE 2451545 / * == date2j (2000, 1, 1) * /
Die Mikrosekundenauflösung entspricht maximal 6 Bruchstellen für Sekunden.
timestamp
Ein als eingegebener Wert teilt Postgres mit, dass keine Zeitzone explizit angegeben wird. Die aktuelle Zeitzone wird angenommen. Postgres ignoriert jeden versehentlich hinzugefügten Zeitzonenmodifikator!timestamp
[without time zone]
Für die Anzeige werden keine Stunden verschoben. Bei gleicher Zeitzoneneinstellung ist alles in Ordnung. Bei einer anderen Zeitzoneneinstellung ändert sich die Bedeutung, aber Wert und Anzeige bleiben gleich.
timestamptz
Der Umgang mit timestamp with time zone
ist subtil anders. Ich zitiere das Handbuch hier :
Denn timestamp with time zone
der intern gespeicherte Wert ist immer in UTC (Universal Coordinated Time ...)
Meine kühne Betonung. Die Zeitzone selbst wird niemals gespeichert . Es ist ein Eingabemodifikator, der zur Berechnung des entsprechenden UTC-Zeitstempels verwendet wird, der gespeichert wird - oder ein Ausgabemodifikator, der zur Berechnung der lokalen Anzeigezeit verwendet wird - mit angefügtem Zeitzonenversatz. Wenn Sie timestamptz
bei der Eingabe keinen Versatz anhängen , wird die aktuelle Zeitzoneneinstellung der Sitzung angenommen. Alle Berechnungen werden mit UTC-Zeitstempelwerten durchgeführt. Wenn Sie mit mehr als einer Zeitzone arbeiten müssen (oder müssen), verwenden Sie timestamptz
.
Kunden wie psql oder pgAdmin oder jede Anwendung kommuniziert über libpq (wie Ruby mit dem gem pg) mit dem Zeitstempel dargestellt und für die Offset aktuellen Zeitzone oder entsprechend eine angeforderten Zeitzone (siehe unten). Es ist immer der gleiche Zeitpunkt , nur das Anzeigeformat variiert. Oder, wie es im Handbuch heißt :
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.
Betrachten Sie dieses einfache Beispiel (in psql):
db = # SELECT timestamptz '2012-03-05 20:00 +03 ';
timestamptz
------------------------
2012-03-05 18:00:00 +01
Meine kühne Betonung. Was ist hier passiert?
Ich habe einen beliebigen Zeitzonenversatz +3
für das Eingabeliteral gewählt. Für Postgres ist dies nur eine von vielen Möglichkeiten, den UTC-Zeitstempel einzugeben 2012-03-05 17:00:00
. Das Ergebnis der Abfrage wird für die aktuelle Zeitzoneneinstellung Wien / Österreich in meinem Test angezeigt , die im Winter und im Sommer versetzt ist : , weil sie in die Winterzeit fällt.+1
+2
2012-03-05 18:00:00+01
Postgres hat bereits vergessen, wie dieser Wert eingegeben wurde. Alles, woran es sich erinnert, ist der Wert und der Datentyp. Genau wie bei einer Dezimalzahl. numeric '003.4'
, numeric '3.40'
oder numeric '+3.4'
- alle ergeben genau den gleichen internen Wert.
AT TIME ZONE
Sobald Sie diese Logik verstanden haben, können Sie alles tun, was Sie wollen. Jetzt fehlt nur noch ein Tool zum Interpretieren oder Darstellen von Zeitstempelliteralen für eine bestimmte Zeitzone. Hier kommt das AT TIME ZONE
Konstrukt ins Spiel. Es gibt zwei verschiedene Anwendungsfälle. timestamptz
wird konvertiert timestamp
und umgekehrt.
So geben Sie die UTC ein timestamptz
2012-03-05 17:00:00+0
:
SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC'
... was entspricht:
SELECT timestamptz '2012-03-05 17:00:00 UTC'
So zeigen Sie denselben Zeitpunkt wie EST timestamp
(Eastern Standard Time) an:
SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC' AT TIME ZONE 'EST'
Das stimmt AT TIME ZONE 'UTC'
zweimal . Der erste interpretiert den timestamp
Wert als (gegebenen) UTC-Zeitstempel, der den Typ zurückgibt timestamptz
. Die zweite konvertiert die timestamptz
in die timestamp
in der angegebenen Zeitzone 'EST' - was für eine Uhr in der Zeitzone EST zu diesem eindeutigen Zeitpunkt anzeigt.
Beispiele
SELECT ts AT TIME ZONE 'UTC'
FROM (
VALUES
(1, timestamptz '2012-03-05 17:00:00+0')
, (2, timestamptz '2012-03-05 18:00:00+1')
, (3, timestamptz '2012-03-05 17:00:00 UTC')
, (4, timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6')
, (5, timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC')
, (6, timestamp '2012-03-05 07:00:00' AT TIME ZONE 'US/Hawaii') -- ①
, (7, timestamptz '2012-03-05 07:00:00 US/Hawaii') -- ①
, (8, timestamp '2012-03-05 07:00:00' AT TIME ZONE 'HST') -- ①
, (9, timestamp '2012-03-05 18:00:00+1') -- ② loaded footgun!
) t(id, ts);
Gibt 8 (oder 9) identische Zeilen mit einer Zeitstempelspalte zurück, die denselben UTC-Zeitstempel enthält 2012-03-05 17:00:00
. Die 9. Reihe funktioniert zufällig in meiner Zeitzone, ist aber eine böse Falle. Siehe unten.
① Zeilen 6 bis 8 mit Zeitzonennamen und Zeitzone Abkürzung für Hawaii Zeit unterliegen DST (Sommerzeit) und können sich unterscheiden, wenn auch nicht gerade. Ein Zeitzonenname wie 'US/Hawaii'
kennt die Sommerzeitregeln und alle historischen Verschiebungen automatisch, während eine Abkürzung wie HST
nur ein dummer Code für einen festen Versatz ist. Möglicherweise müssen Sie eine andere Abkürzung für Sommer / Standardzeit anhängen. Der Name interpretiert jeden Zeitstempel in der angegebenen Zeitzone korrekt . Eine Abkürzung ist billig, muss aber für den angegebenen Zeitstempel die richtige sein:
Die Sommerzeit gehört nicht zu den besten Ideen, die die Menschheit jemals entwickelt hat.
② Zeile 9, die als geladene Fußwaffe markiert ist , funktioniert für mich , aber nur durch Zufall. Wenn Sie explizit ein Literal in umwandeln, timestamp [without time zone]
wird jeder Zeitzonenversatz ignoriert ! Es wird nur der bloße Zeitstempel verwendet. Der Wert wird dann timestamptz
im Beispiel automatisch gezwungen, dem Spaltentyp zu entsprechen . Für diesen Schritt wird die timezone
Einstellung der aktuellen Sitzung angenommen, die +1
in meinem Fall dieselbe Zeitzone ist (Europa / Wien). Aber wahrscheinlich nicht in Ihrem Fall - was zu einem anderen Wert führt. Kurz gesagt: Wirf keine timestamptz
Literale in timestamp
oder du verlierst den Zeitzonenversatz.
Deine Fragen
Der Benutzer speichert eine Uhrzeit, z. B. 17. März 2012, 19 Uhr. Ich möchte nicht, dass Zeitzonen-Konvertierungen oder die Zeitzone gespeichert werden.
Die Zeitzone selbst wird niemals gespeichert. Verwenden Sie eine der oben genannten Methoden, um einen UTC-Zeitstempel einzugeben.
Ich verwende nur die vom Benutzer angegebene Zeitzone, um Datensätze "vor" oder "nach" der aktuellen Zeit in der lokalen Zeitzone des Benutzers abzurufen.
Sie können eine Abfrage für alle Clients in verschiedenen Zeitzonen verwenden.
Für die absolute globale Zeit:
SELECT * FROM tbl WHERE time_col > (now() AT TIME ZONE 'UTC')::time
Für die Zeit nach der lokalen Uhr:
SELECT * FROM tbl WHERE time_col > now()::time
Noch nicht müde von Hintergrundinformationen? Das Handbuch enthält mehr.