Klíčové věci, kterým je třeba porozumět
timestamp without time zone AT TIME ZONE
znovu interpretuje timestamp
jako v daném časovém pásmu za účelem převodu na UTC .
timestamp with time zone AT TIME ZONE
konvertuje timestamptz
do timestamp
v zadaném časovém pásmu.
PostgreSQL používá časová pásma ISO-8601, která specifikují, že východně od Greenwiche je kladné ... pokud nepoužijete specifikátor časového pásma POSIX, v takovém případě následuje POSIX. Následuje šílenství.
Proč první z nich vede k neočekávanému výsledku
Časová razítka a časová pásma v SQL jsou hrozné. Toto:
select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'EST5EDT';
interpretuje literál neznámého typu '2011-12-30 00:30:00'
jako timestamp without time zone
, o kterém Pg předpokládá, že je v místním časovém pásmu, pokud není uvedeno jinak. Když používáte AT TIME ZONE
, je (podle specifikace) znovu interpretován jako timestamp with time zone
v časovém pásmu EST5EDT
pak uložen jako absolutní čas v UTC – takže je převeden z z EST5EDT
komu UTC, tj. posun časového pásma se odečte . x - (-5)
je x + 5
.
Toto časové razítko, přizpůsobené úložišti UTC, je pak upraveno pro váš server TimeZone
nastavení zobrazení tak, aby se zobrazovalo v místním čase.
Pokud místo toho chcete říct „Mám toto časové razítko v čase UTC a chcete vidět, jaký je ekvivalent místního času v EST5EDT“, pokud chcete být nezávislí na nastavení časového pásma serveru, musíte napsat něco jako:
select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
AT TIME ZONE 'EST5EDT';
Zde je napsáno:"Uvedené časové razítko 2011-12-30 00:30:00, zacházejte s ním jako s časovým razítkem v UTC při převodu na časové razítko a poté toto časové razítko převeďte na místní čas v EST5EDT."
Hrozné, že? Chci dát firmu mluvící kdokoli rozhodl o bláznivé sémantice AT TIME ZONE
- ve skutečnosti by to mělo být něco jako timestamp CONVERT FROM TIME ZONE '-5'
a timestamptz CONVERT TO TIME ZONE '+5'
. Také timestamp with time zone
měl by ve skutečnosti nést své časové pásmo, neměl by být uložen v UTC a automaticky převeden na místní čas.
Proč to druhé funguje (pokud TimeZone =UTC)
Vaše původní „funkční“ verze:
select '2011-12-30 00:30:00' AT TIME ZONE 'EST5EDT';
bude správné pouze v případě, že je časové pásmo nastaveno na UTC, protože přenos textu na časové razítko předpokládá časové pásmo, pokud není určeno.
Proč funguje třetí
Dva problémy se navzájem ruší.
Druhá verze, která vypadá, že funguje, je nezávislá na TimeZone, ale funguje pouze proto, že se dva problémy samy vyruší. Nejprve, jak je vysvětleno výše, timestamp without time zone AT TIME ZONE
znovu interpretuje časové razítko jako v tomto časovém pásmu pro převod na časové razítko UTC; to efektivně odečítá posun časového pásma.
Nicméně z důvodů, které jsou mimo mé znalosti, PostgreSQL používá časová razítka s opačným znaménkem, než jsem zvyklý vidět na většině míst. Viz dokumentace:
Dalším problémem, který je třeba mít na paměti, je, že v názvech časových pásem POSIX se pro umístění západně od Greenwiche používají kladné posuny. Všude jinde se PostgreSQL řídí konvencí ISO-8601, že kladné posuny časových pásem jsou východně od Greenwiche.
To znamená, že EST5EDT
je stejné jako +5
, nikoli -5
. Což je důvod, proč to funguje:protože offset tz odečítáte, nikoli sčítáte, ale odečítáte negovaný offset!
Místo toho byste potřebovali, aby to bylo správné:
select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
AT TIME ZONE '+5';