Postgres har två olika tidsstämpeldatatyper:
timestamp with time zone
, kortnamn:timestamptz
timestamp without time zone
, kortnamn:timestamp
timestamptz
är den föredragna skriv in datum/tid-familjen, bokstavligen. Den har typispreferred
ställs in i pg_type
, vilket kan vara relevant:
- Genererar tidsserier mellan två datum i PostgreSQL
Intern lagring och epok
Internt upptar tidsstämplar 8 byte lagringsutrymme på disk och i RAM. Det är ett heltalsvärde som representerar antalet mikrosekunder från Postgres-epoken, 2000-01-01 00:00:00 UTC.
Postgres har också inbyggd kunskap om den vanliga UNIX-tidsräkningssekunderna från UNIX-epoken, 1970-01-01 00:00:00 UTC, och använder det i funktionerna to_timestamp(double precision)
eller EXTRACT(EPOCH FROM timestamptz)
.
Källkoden:
* 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.)
Och:
/* 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) */
Mikrosekundsupplösningen översätts till maximalt 6 bråksiffror i sekunder.
timestamp
För timestamp
ingen tidszon anges uttryckligen. Postgres ignorerar någon tidszonsmodifierare som av misstag har lagts till i inmatningen!
Inga timmar förskjuts för visning. Med allt som händer i samma tidszon är det bra. För en annan tidszon innebörden ändringar, men värde och visa förbli densamma.
timestamptz
Hantering av timestamptz
är subtilt annorlunda. Jag citerar manualen här:
För
timestamp with time zone
, är det internt lagrade värdet alltid i UTC (Universal Coordinated Time ...)
Djärv betoning min. Själva tidszonen lagras aldrig . Det är en ingångsmodifierare som används för att beräkna enligt UTC-tidsstämpeln, som lagras - eller och utgångsdekoratorn används för att beräkna den lokala tiden för visning - med bifogad tidszonförskjutning. Om du inte lägger till en offset för timestamptz
vid inmatning antas den aktuella tidszonsinställningen för sessionen. Alla beräkningar görs med UTC-tidsstämpelvärden. Om du (kan) behöva hantera mer än en tidszon, använd timestamptz
. Med andra ord:Om det kan finnas några tvivel eller missförstånd om den antagna tidszonen, gå till timestamptz
. Gäller i de flesta användningsfall.
Klienter som psql eller pgAdmin eller andra program som kommunicerar via libpq (som Ruby med pg gem) presenteras med tidsstämpeln plus offset för aktuell tidszon eller enligt en begäran tidszon (se nedan). Det är alltid samma tidpunkt , endast visningsformatet varierar. Eller, som bruksanvisningen uttrycker det:
Alla tidszonsmedvetna datum och tider lagras internt i UTC. De konverteras till lokal tid i zonen som anges av
TimeZone
konfigurationsparameter innan den visas för klienten.
Exempel i psql:
db=# SELECT timestamptz '2012-03-05 20:00+03';
timestamptz
------------------------
2012-03-05 18:00:00+01
Vad hände här?
Jag valde en godtycklig tidszonförskjutning +3
för ingången bokstavlig. För Postgres är detta bara ett av många sätt att mata in UTC-tidsstämpeln 2012-03-05 17:00:00
. Resultatet av frågan visas för den aktuella tidszonsinställningen Wien/Österrike i mitt test, som har en offset +1
under vintern och +2
under sommartid ("sommartid", sommartid). Så 2012-03-05 18:00:00+01
eftersom sommartid kommer in först senare.
Postgres glömmer ingången bokstavligt omedelbart. Allt den kommer ihåg är värdet för datatypen. Precis som med ett decimaltal. numeric '003.4'
eller numeric '+3.4'
- båda resulterar i exakt samma interna värde.
AT TIME ZONE
Allt som saknas nu är ett verktyg för att tolka eller representera tidsstämpelns bokstavliga ord enligt en specifik tidszon. Det är där AT TIME ZONE
konstruktionen kommer in. Det finns två olika användningsfall. timestamptz
konverteras till timestamp
och vice versa.
För att ange UTC timestamptz
2012-03-05 17:00:00+0
:
SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC'
... vilket motsvarar:
SELECT timestamptz '2012-03-05 17:00:00 UTC'
För att visa samma tidpunkt som EST timestamp
(Eastern Standard Time):
SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC' AT TIME ZONE 'EST'
Det stämmer, AT TIME ZONE 'UTC'
två gånger . Den första tolkar timestamp
värde som (givet) UTC-tidsstämpel som returnerar typen timestamptz
. Den andra konverterar timestamptz
till timestamp
i den givna tidszonen 'EST' - vad en väggklocka visar i tidszonen EST vid denna tidpunkt.
Exempel
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);
Returnerar 8 (eller 9) identiska rader med en tidsstämpeltz-kolumn som innehåller samma UTC-tidsstämpel 2012-03-05 17:00:00
. Den nionde raden råkar fungera i min tidszon, men är en ond fälla. Se nedan.
① Rad 6 - 8 med tidszon namn och tidszon förkortning för Hawaii gäller tid sommartid (sommartid) och kan skilja sig åt, men inte för närvarande. Ett tidszonsnamn som 'US/Hawaii'
är medveten om sommartidsregler och alla historiska skift automatiskt, medan en förkortning som HST
är bara en dum kod för en fast offset. Du kan behöva lägga till en annan förkortning för sommar-/normaltid. namnet tolkar alla korrekt tidsstämpel vid den givna tidszonen. En förkortning är billig, men måste vara den rätta för den givna tidsstämpeln:
- Tidszonsnamn med identiska egenskaper ger olika resultat när de tillämpas på tidsstämpel
Sommartid är inte bland de smartaste idéer som mänskligheten någonsin kommit på.
② Rad 9, markerad som laddat fotgevär fungerar för mig , men bara av en slump. Om du uttryckligen castar en bokstavlig till timestamp [without time zone]
, alla tidszonförskjutningar ignoreras! Endast den blotta tidsstämpeln används. Värdet tvingas sedan automatiskt till timestamptz
i exemplet för att matcha kolumntypen. För detta steg, timezone
inställningen för den aktuella sessionen antas, vilket råkar vara samma tidszon +1
i mitt fall (Europa/Wien). Men förmodligen inte i ditt fall - vilket kommer att resultera i ett annat värde. Kort sagt:Casta inte timestamptz
bokstaver till timestamp
eller så förlorar du tidszonförskjutningen.
Dina frågor
Användaren lagrar en tid, t.ex. 17 mars 2012, kl. Jag vill inte att tidszonomvandlingar eller tidszonen ska lagras.
Själva tidszonen lagras aldrig. Använd en av metoderna ovan för att ange en UTC-tidsstämpel.
Jag använder bara användarens angivna tidszon för att få poster "före" eller "efter" den aktuella tiden i användarens lokala tidszon.
Du kan använda en fråga för alla klienter i olika tidszoner.
För absolut global tid:
SELECT * FROM tbl WHERE time_col > (now() AT TIME ZONE 'UTC')::time
För tid enligt den lokala klockan:
SELECT * FROM tbl WHERE time_col > now()::time
Har du inte tröttnat på bakgrundsinformation ännu? Det finns mer i manualen.