Postgres hat zwei verschiedene Zeitstempel-Datentypen:
timestamp with time zone
, Kurzname:timestamptz
timestamp without time zone
, Kurzname:timestamp
timestamptz
ist bevorzugt Geben Sie wörtlich die Datums-/Uhrzeitfamilie ein. Es hat typispreferred
gesetzt in pg_type
, die relevant sein können:
- Generieren von Zeitreihen zwischen zwei Daten in PostgreSQL
Interner Speicher und Epoche
Zeitstempel belegen intern 8 Byte Speicherplatz auf der Festplatte und im RAM. Es ist ein ganzzahliger Wert, der die Anzahl der Mikrosekunden seit der Postgres-Epoche, 2000-01-01 00:00:00 UTC darstellt.
Postgres verfügt auch über integrierte Kenntnisse der häufig verwendeten UNIX-Zeitzählung von Sekunden aus der UNIX-Epoche 1970-01-01 00:00:00 UTC und verwendet diese in den Funktionen to_timestamp(double precision)
oder EXTRACT(EPOCH FROM timestamptz)
.
Der Quellcode:
* Timestamps, as well as the h/m/s fields of intervals, are stored as * int64 values with units of microseconds. (Once upon a time they were * double values with units of seconds.)
Und:
/* Julian-date equivalents of Day 0 in Unix and Postgres reckoning */ #define UNIX_EPOCH_JDATE 2440588 /* == date2j(1970, 1, 1) */ #define POSTGRES_EPOCH_JDATE 2451545 /* == date2j(2000, 1, 1) */
Die Mikrosekundenauflösung entspricht maximal 6 Nachkommastellen für Sekunden.
timestamp
Für timestamp
Es wird keine Zeitzone explizit angegeben. Postgres ignoriert jeder Zeitzonenmodifikator, der versehentlich zum Eingabeliteral hinzugefügt wurde!
Es werden keine Stunden für die Anzeige verschoben. Da alles in der gleichen Zeitzone passiert, ist das in Ordnung. Für eine andere Zeitzone die Bedeutung Änderungen, aber Wert und anzeigen gleich bleiben.
timestamp
Umgang mit timestamptz
ist dezent anders. Ich zitiere das Handbuch hier:
Für timestamp with time zone
, ist der intern gespeicherte Wert immer in UTC (Koordinierte Weltzeit ...)
Fette Hervorhebung von mir. Die Zeitzone selbst wird nie gespeichert . Es ist ein Eingabemodifikator, der verwendet wird, um den entsprechenden UTC-Zeitstempel zu berechnen, der gespeichert wird - oder ein Ausgabedekorator, der verwendet wird, um die lokale Zeit für die Anzeige zu berechnen - mit angehängtem Zeitzonen-Offset. Wenn Sie keinen Offset für timestamptz
anhängen bei der Eingabe wird die aktuelle Zeitzoneneinstellung der Sitzung übernommen. Alle Berechnungen werden mit UTC-Zeitstempelwerten durchgeführt. Wenn Sie (möglicherweise) mit mehr als einer Zeitzone zu tun haben, verwenden Sie timestamptz
. Mit anderen Worten:Wenn Zweifel oder Missverständnisse bezüglich der angenommenen Zeitzone bestehen, verwenden Sie timestamptz
. Gilt für die meisten Anwendungsfälle.
Clients wie psql oder pgAdmin oder jede Anwendung, die über libpq kommuniziert (wie Ruby mit dem pg-Gem), wird der Zeitstempel plus Offset für die aktuelle Zeitzone präsentiert oder nach einem angeforderten Zeitzone (siehe unten). Es ist immer der selbe Zeitpunkt , nur das Anzeigeformat variiert. Oder, wie es im Handbuch heißt:
Alle zeitzonenabhängigen Daten und Zeiten werden intern in UTC gespeichert. Sie werden in die Ortszeit der durch TimeZone
angegebenen Zone konvertiert Konfigurationsparameter, bevor sie dem Client angezeigt werden.
Beispiel in psql:
db=# SELECT timestamptz '2012-03-05 20:00+03';
timestamptz
------------------------
2012-03-05 18:00:00+01
Was ist hier passiert?
Ich habe einen beliebigen Zeitzonenversatz +3
gewählt für das Eingabeliteral. Für Postgres ist dies nur eine von vielen Möglichkeiten, den UTC-Zeitstempel 2012-03-05 17:00:00
einzugeben . Das Ergebnis der Abfrage wird angezeigt für die aktuelle Zeitzoneneinstellung Wien/Österreich in meinem Test, der einen Offset +1
hat im Winter und +2
während der Sommerzeit ("Daylight Saving Time", DST). Also 2012-03-05 18:00:00+01
da die Sommerzeit erst später einsetzt.
Postgres vergisst das Eingabeliteral sofort. Es erinnert sich nur an den Wert für den Datentyp. Genau wie bei einer Dezimalzahl. numeric '003.4'
oder numeric '+3.4'
- beide ergeben den exakt gleichen internen Wert.
AT TIME ZONE
Jetzt fehlt nur noch ein Werkzeug, um Zeitstempelliterale entsprechend einer bestimmten Zeitzone zu interpretieren oder darzustellen. Dort befindet sich AT TIME ZONE
Konstrukt kommt ins Spiel. Es gibt zwei verschiedene Anwendungsfälle. timestamptz
wird in timestamp
umgewandelt und umgekehrt.
Zur Eingabe der UTC timestamptz
2012-03-05 17:00:00+0
:
SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC'
... was äquivalent ist zu:
SELECT timestamptz '2012-03-05 17:00:00 UTC'
Um den gleichen Zeitpunkt wie EST timestamp
anzuzeigen (Eastern Standard Time):
SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC' AT TIME ZONE 'EST'
Richtig, AT TIME ZONE 'UTC'
zweimal . Der erste interpretiert den timestamp
Wert als (gegebener) UTC-Zeitstempel, der den Typ timestamptz
zurückgibt . Der zweite konvertiert den timestamptz
zum timestamp
in der angegebenen Zeitzone 'EST' - was eine Wanduhr zu diesem Zeitpunkt in der Zeitzone EST 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) identisch zurück Zeilen mit einem timestamptz Spalten mit demselben UTC-Zeitstempel 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 - 8 mit Zeitzone name und Abkürzung der Zeitzone für die Hawaii-Zeit unterliegen der Sommerzeit (DST) und können abweichen, wenn auch derzeit nicht. Ein Zeitzonenname wie 'US/Hawaii'
kennt die Sommerzeitregeln und alle historischen Verschiebungen automatisch, während eine Abkürzung wie HST
ist nur ein dummer Code für einen festen Offset. Eventuell müssen Sie ein anderes Kürzel für Sommer-/Normalzeit anhängen. Der Name interpretiert any richtig Zeitstempel in der angegebenen Zeitzone. Eine Abkürzung ist billig, muss aber für den gegebenen Zeitstempel richtig sein:
- Zeitzonennamen mit identischen Eigenschaften führen zu unterschiedlichen Ergebnissen, wenn sie auf den Zeitstempel angewendet werden
Die Sommerzeit gehört nicht zu den klügsten Ideen, die die Menschheit je hatte.
② Reihe 9, markiert als geladenes Fußgewehr funktioniert für mich , aber nur zufällig. Wenn Sie explizit ein Literal in timestamp [without time zone]
umwandeln , ein eventueller Zeitzonenoffset wird ignoriert! Es wird nur der bloße Zeitstempel verwendet. Der Wert wird dann automatisch in timestamptz
umgewandelt im Beispiel passend zum Spaltentyp. Für diesen Schritt die timezone
wird die Einstellung der aktuellen Sitzung angenommen, die zufällig dieselbe Zeitzone +1
ist in meinem Fall (Europa/Wien). Aber wahrscheinlich nicht in Ihrem Fall - was zu einem anderen Wert führen wird. Kurz gesagt:Caste nicht timestamptz
Literale in timestamp
oder Sie verlieren den Zeitzonen-Offset.
Ihre Fragen
Der Benutzer speichert eine Uhrzeit, z. B. 17. März 2012, 19:00 Uhr. Ich möchte nicht, dass Zeitzonenumrechnungen oder die Zeitzone gespeichert werden.
Die Zeitzone selbst wird nie gespeichert. Verwenden Sie eine der oben genannten Methoden, um einen UTC-Zeitstempel einzugeben.
Ich verwende nur die vom Benutzer angegebene Zeitzone, um Aufzeichnungen 'vor' oder 'nach' der aktuellen Zeit in der lokalen Zeitzone des Benutzers zu erhalten.
Sie können eine Abfrage für alle Clients in verschiedenen Zeitzonen verwenden.
Für absolute globale Zeit:
SELECT * FROM tbl WHERE time_col > (now() AT TIME ZONE 'UTC')::time
Für Zeit nach lokaler Uhr:
SELECT * FROM tbl WHERE time_col > now()::time
Noch nicht genug von Hintergrundinformationen? Im Handbuch steht mehr.