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.
