Belangrijkste dingen om te begrijpen
timestamp without time zone AT TIME ZONE
herinterpreteert een timestamp
omdat het zich in die tijdzone bevindt met als doel het te converteren naar UTC .
timestamp with time zone AT TIME ZONE
converteert een timestamptz
in een timestamp
in de opgegeven tijdzone.
PostgreSQL gebruikt ISO-8601-tijdzones, die specificeren dat ten oosten van Greenwich positief is ... tenzij u een POSIX-tijdzonespecificatie gebruikt, in welk geval deze POSIX volgt. Er ontstaat waanzin.
Waarom de eerste een onverwacht resultaat oplevert
Tijdstempels en tijdzones in SQL zijn verschrikkelijk. Dit:
select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'EST5EDT';
interpreteert de onbekende getypte letterlijke '2011-12-30 00:30:00'
als timestamp without time zone
, waarvan Pg aanneemt dat het zich in de lokale TimeZone bevindt, tenzij anders aangegeven. Wanneer u AT TIME ZONE
. gebruikt , het is (volgens de specificatie) opnieuw geïnterpreteerd als een timestamp with time zone
in de tijdzone EST5EDT
vervolgens opgeslagen als een absolute tijd in UTC - dus het is geconverteerd van EST5EDT
naar UTC, d.w.z. de tijdzone-offset wordt afgetrokken . x - (-5)
is x + 5
.
Deze tijdstempel, aangepast aan UTC-opslag, wordt vervolgens aangepast voor uw server TimeZone
instelling voor weergave zodat deze in lokale tijd wordt weergegeven.
Als je in plaats daarvan wilt zeggen "Ik heb deze tijdstempel in UTC-tijd en wil zien wat de equivalente lokale tijd in EST5EDT is", als je onafhankelijk wilt zijn van de TimeZone-instelling van de server, moet je zoiets schrijven als:
select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
AT TIME ZONE 'EST5EDT';
Dit zegt "Gegeven tijdstempel 2011-12-30 00:30:00, behandel het als een tijdstempel in UTC bij het converteren naar timestamptz, converteer dat timestamptz vervolgens naar een lokale tijd in EST5EDT".
Verschrikkelijk, niet? Ik wil een firma aan het woord laten wie heeft besloten over de gekke semantiek van AT TIME ZONE
- het zou eigenlijk zoiets moeten zijn als timestamp CONVERT FROM TIME ZONE '-5'
en timestamptz CONVERT TO TIME ZONE '+5'
. Ook timestamp with time zone
zou eigenlijk zijn tijdzone met zich mee moeten dragen, niet worden opgeslagen in UTC en automatisch worden geconverteerd naar lokale tijd.
Waarom de tweede werkt (zolang TimeZone =UTC)
Uw originele "werkt" versie:
select '2011-12-30 00:30:00' AT TIME ZONE 'EST5EDT';
zal alleen correct zijn als TimeZone is ingesteld op UTC, omdat de tekst-naar-timestamptz-cast TimeZone aanneemt als er geen is gespecificeerd.
Waarom de derde werkt
Twee problemen heffen elkaar op.
De andere versie die lijkt te werken, is TimeZone-onafhankelijk, maar het werkt alleen omdat twee problemen zichzelf opheffen. Ten eerste, zoals hierboven uitgelegd, timestamp without time zone AT TIME ZONE
herinterpreteert de tijdstempel als zijnde in die tijdzone voor conversie naar een UTC-tijdstempel; dit trekt effectief af de tijdzone-offset.
Echter, om redenen die mij te boven gaan, gebruikt PostgreSQL tijdstempels met het omgekeerde teken van wat ik gewend ben te zien op de meeste plaatsen. Zie de documentatie:
Een ander probleem om in gedachten te houden is dat in POSIX-tijdzonenamen positieve offsets worden gebruikt voor locaties ten westen van Greenwich. Overal elders volgt PostgreSQL de ISO-8601-conventie dat positieve tijdzone-offsets ten oosten van Greenwich liggen.
Dit betekent dat EST5EDT
is hetzelfde als +5
, niet -5
. Dat is waarom het werkt:omdat je de tz-offset aftrekt en niet optelt, maar je trekt een genegeerde offset af!
Wat je nodig hebt om het correct te krijgen is in plaats daarvan:
select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
AT TIME ZONE '+5';