Vigtige ting at forstå
timestamp without time zone AT TIME ZONE
omfortolker et timestamp
som at være i den tidszone med det formål at konvertere den til UTC .
timestamp with time zone AT TIME ZONE
konverterer en timestamptz
ind i et timestamp
i den angivne tidszone.
PostgreSQL bruger ISO-8601-tidszoner, som angiver, at øst for Greenwich er positiv ... medmindre du bruger en POSIX-tidszonespecifikation, i hvilket tilfælde den følger POSIX. Der opstår sindssyge.
Hvorfor den første giver et uventet resultat
Tidsstempler og tidszoner i SQL er forfærdelige. Dette:
select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'EST5EDT';
fortolker den ukendte bogstavelige '2011-12-30 00:30:00'
som timestamp without time zone
, som Pg antager er i den lokale tidszone, medmindre andet er fortalt. Når du bruger AT TIME ZONE
, det er (ifølge specifikationen) genfortolket som et timestamp with time zone
i tidszonen EST5EDT
gemmes derefter som en absolut tid i UTC - så den konverteres fra EST5EDT
til UTC, dvs. tidszoneforskydningen bliver trukket fra . x - (-5)
er x + 5
.
Dette tidsstempel, justeret til UTC-lagring, justeres derefter for din server TimeZone
indstilling for visning, så den bliver vist i lokal tid.
Hvis du i stedet ønsker at sige "Jeg har dette tidsstempel i UTC-tid og ønsker at se, hvad den ækvivalente lokale tid i EST5EDT er", hvis du vil være uafhængig af serverens TimeZone-indstilling, skal du skrive noget som:
select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
AT TIME ZONE 'EST5EDT';
Dette siger "Givet tidsstempel 2011-12-30 00:30:00, behandle det som et tidsstempel i UTC, når du konverterer til timestamptz, og konverter derefter det tidsstempel til en lokal tid i EST5EDT".
Forfærdeligt, ikke? Jeg vil gerne give en virksomhed tale med hvem der end har besluttet sig for den vanvittige semantik i AT TIME ZONE
- det burde virkelig være noget i stil med timestamp CONVERT FROM TIME ZONE '-5'
og timestamptz CONVERT TO TIME ZONE '+5'
. Også timestamp with time zone
skal faktisk bære sin tidszone med sig, ikke gemmes i UTC og automatisk konverteres til lokaltid.
Hvorfor den anden virker (så længe TimeZone =UTC)
Din originale "virker"-version:
select '2011-12-30 00:30:00' AT TIME ZONE 'EST5EDT';
vil kun være korrekt, hvis TimeZone er indstillet til UTC, fordi tekst-til-tidsstempel-castet antager TimeZone, når en ikke er angivet.
Hvorfor virker den tredje
To problemer ophæver hinanden.
Den anden version, der ser ud til at virke, er TimeZone-uafhængig, men den virker kun, fordi to problemer ophæver sig selv. For det første, som forklaret ovenfor, timestamp without time zone AT TIME ZONE
omfortolker tidsstemplet som værende i denne tidszone for konvertering til et UTC-tidsstempel; dette fratrækker effektivt tidszoneforskydningen.
Men af årsager, som jeg ikke kender, bruger PostgreSQL tidsstempler med det omvendte fortegn til det, jeg er vant til at se de fleste steder. Se dokumentationen:
Et andet problem at huske på er, at i POSIX-tidszonenavne bruges positive forskydninger for lokationer vest for Greenwich. Alle andre steder følger PostgreSQL ISO-8601-konventionen om, at positive tidszoneforskydninger er øst for Greenwich.
Det betyder, at EST5EDT
er det samme som +5
, ikke -5
. Det er derfor, det virker:fordi du trækker tz-forskydningen, ikke tilføjer det, men du trækker en negeret offset!
Det du skal bruge for at få det korrekt er i stedet:
select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
AT TIME ZONE '+5';