Lad mig forklare de to eksempler:
I begge antager vi en tidszone UTC (dvs. SET timezone TO UTC
).
db=# SELECT timezone('US/Pacific', '2016-01-01 00:00');
timezone
---------------------
2015-12-31 16:00:00
(1 row)
Dette svarer til SELECT timezone('US/Pacific', '2016-01-01 00:00'::timestamptz)
, dvs. Postgres konverterede implicit strengen til en timestamptz
.
Vi ved, at timezone
funktion konverterer frem og tilbage mellem timestamp
og timestamptz
:
Da vi giver den en timestamptz
som input udsender den et timestamp
. Med andre ord konverterer den det absolutte tidspunkt 2016-01-01 00:00Z
til en vægtid i US/Pacific
, altså hvad uret i Los Angeles viste på det absolutte tidspunkt.
I eksempel 2 gør vi det modsatte, nemlig at tage et timestamp
og konvertere den til en timestamptz
. Med andre ord, vi spørger:hvad var det absolutte tidspunkt, hvor uret i Los Angeles viste 2016-01-01 00:00
?
Du nævner:
'2016-01-01 00:00'::timestamp
er et timestamp
, altså en vægtid. Den har ikke en forestilling om tidszone.
Jeg tror, du måske ikke helt har forstået forskellen mellem timestamp
og timestamptz
, hvilket er nøglen her. Tænk bare på dem som vægtid , altså tiden, der viste et sted i verden på et ur, der hænger på væggen, og absolut tid , altså den absolutte tid i vores univers.
De eksempler, du laver i dit eget svar, er ikke helt præcise.
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);
Problemet med dit eksempel er, at du konstruerer et datasæt med en enkelt kolonne. Da en kolonne kun kan have én type, bliver hver række (eller enkelt værdi i dette tilfælde) konverteret til den samme type, nemlig timestamptz
, selvom nogle værdier blev beregnet som timestamp
(f.eks. værdi 3). Du har således en ekstra implicit konvertering her.
Lad os opdele eksemplet i separate forespørgsler og se, hvad der foregår:
Eksempel 1
db=# SELECT timestamptz '2012-03-05 17:00:00+0';
timestamptz
------------------------
2012-03-05 17:00:00+00
Som du måske allerede ved, timestamptz '2012-03-05 17:00:00+0'
og '2012-03-05 17:00:00+0'::timestamptz
er ækvivalente (jeg foretrækker det sidste). Bare for at bruge den samme syntaks som i artiklen, vil jeg omskrive:
db=# SELECT '2012-03-05 17:00:00+0'::timestamptz;
timestamptz
------------------------
2012-03-05 17:00:00+00
Hvad sker der nu her? Nå, mindre end i din oprindelige forklaring. Strengen er simpelthen parset som en timestamptz
. Når resultatet bliver udskrevet, bruger det den aktuelt indstillede timezone
config for at konvertere den tilbage til en menneskelig læsbar repræsentation af den underliggende datastruktur, dvs. 2012-03-05 17:00:00+00
.
Lad os ændre timezone
config og se hvad der sker:
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
Det eneste, der ændrede sig, er hvordan timestamptz
bliver udskrevet på skærmen, nemlig ved hjælp af Europa/Berlin tidszone.
Eksempel 2
db=# SELECT timestamptz '2012-03-05 18:00:00+1';
timestamptz
------------------------
2012-03-05 17:00:00+00
(1 row)
Igen, bare parsing af datoen.
Eksempel 3
db=# SELECT timestamp '2012-03-05 18:00:00+1';
timestamp
---------------------
2012-03-05 18:00:00
(1 row)
Dette er det samme som '2012-03-05 18:00:00+1'::timestamp
. Det, der sker her, er, at tidszoneforskydningen simpelthen ignoreres, fordi du beder om et timestamp
.
Eksempel 4
db=# SELECT timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6';
timezone
------------------------
2012-03-05 17:00:00+00
(1 row)
Lad os omskrive for at være enklere:
db=# SELECT timezone('+6', '2012-03-05 11:00:00'::timestamp);
timezone
------------------------
2012-03-05 17:00:00+00
(1 row)
Dette spørger:hvad var det absolutte tidspunkt, da uret på væggen i tidszonen med en offset på +6 timer viste 2012-03-05 11:00:00
?
Eksempel 5
db=# SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC';
timezone
------------------------
2012-03-05 17:00:00+00
(1 row)
Lad os omskrive:
db=# SELECT timezone('UTC', '2012-03-05 17:00:00'::timestamp);
timezone
------------------------
2012-03-05 17:00:00+00
(1 row)
Dette spørger:hvad var det absolutte tidspunkt, da uret på væggen i tidszonen UTC viste 2012-03-05 17:00:00
?
Eksempel 6
db=# SELECT timestamp '2012-03-05 17:00:00'::timestamp;
timestamp
---------------------
2012-03-05 17:00:00
(1 row)
Her caster du to gange til timestamp
, hvilket ikke gør nogen forskel. Lad os forenkle:
db=# SELECT '2012-03-05 17:00:00'::timestamp;
timestamp
---------------------
2012-03-05 17:00:00
(1 row)
Det er klart, synes jeg.
Eksempel 7
db=# SELECT timestamp '2012-03-05 17:00:00'::timestamptz;
timestamptz
------------------------
2012-03-05 17:00:00+00
(1 row)
Lad os omskrive:
db=# SELECT ('2012-03-05 17:00:00'::timestamp)::timestamptz;
timestamptz
------------------------
2012-03-05 17:00:00+00
(1 row)
Du parser først strengen som et timestamp
og derefter konvertere den til en timestamptz
ved at bruge den aktuelt indstillede timezone
. Hvis vi ændrer timezone
, får vi noget andet, fordi Postgres antager den tidszone, når den konverterer et timestamp
(eller en streng, der mangler tidszoneoplysninger) til 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)
Denne absolutte tid, udtrykt i UTC, er 2012-03-05 16:00:00+00
, således forskellig fra det oprindelige eksempel.
Jeg håber, at dette afklarer tingene. Igen, at forstå forskellen mellem timestamp
og timestamptz
er nøglen. Tænk på vægtid versus absolut tid.