Dovolte mi vysvětlit dva příklady:
V obou případech předpokládáme časové pásmo UTC (tj. SET timezone TO UTC ).
db=# SELECT timezone('US/Pacific', '2016-01-01 00:00');
timezone
---------------------
2015-12-31 16:00:00
(1 row)
To je ekvivalentní SELECT timezone('US/Pacific', '2016-01-01 00:00'::timestamptz) , tj. Postgres implicitně převedl řetězec na timestamptz .
Víme, že timezone funkce převádí tam a zpět mezi timestamp a timestamptz :
Protože mu dáváme timestamptz jako vstup vypíše timestamp . Jinými slovy, převádí absolutní bod v čase 2016-01-01 00:00Z na čas zdi v US/Pacific , tedy to, co ukazovaly hodiny v Los Angeles v tom absolutním časovém okamžiku.
V příkladu 2 děláme opak, totiž bereme timestamp a převést jej na timestamptz . Jinými slovy, ptáme se:jaký byl absolutní čas, když hodiny v Los Angeles ukazovaly 2016-01-01 00:00 ?
Zmiňujete:
'2016-01-01 00:00'::timestamp je timestamp , tedy nástěnný čas. Nemá pojem časové pásmo.
Myslím, že jste možná úplně nepochopili rozdíl mezi timestamp a timestamptz , která je zde klíčová. Představte si je jako čas zdi , tj. čas, který někde na světě ukazoval hodiny visící na zdi, a absolutní čas , tj. absolutní čas v našem vesmíru.
Příklady, které uvádíte ve své vlastní odpovědi, nejsou zcela přesné.
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);
Problém s vaším příkladem je v tom, že vytváříte jednu datovou sadu s jedním sloupcem. Protože sloupec může mít pouze jeden typ, každý řádek (nebo jedna hodnota v tomto případě) se převádí na stejný typ, konkrétně timestamptz , i když některé hodnoty byly vypočteny jako timestamp (např. hodnota 3). Máte zde tedy další implicitní konverzi.
Rozdělme příklad na samostatné dotazy a podívejme se, co se děje:
Příklad 1
db=# SELECT timestamptz '2012-03-05 17:00:00+0';
timestamptz
------------------------
2012-03-05 17:00:00+00
Jak už možná víte, timestamptz '2012-03-05 17:00:00+0' a '2012-03-05 17:00:00+0'::timestamptz jsou ekvivalentní (preferuji to druhé). Proto, abych použil stejnou syntaxi jako v článku, přepíšu:
db=# SELECT '2012-03-05 17:00:00+0'::timestamptz;
timestamptz
------------------------
2012-03-05 17:00:00+00
Co se tady děje? Tedy méně než ve vašem původním vysvětlení. Řetězec je jednoduše analyzován jako timestamptz . Když se výsledek vytiskne, použije aktuálně nastavené timezone config, abyste jej převedli zpět na lidsky čitelnou reprezentaci základní datové struktury, tj. 2012-03-05 17:00:00+00 .
Pojďme změnit timezone config a uvidíte, co se stane:
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
Jediná věc, která se změnila, je jak timestamptz se vytiskne na obrazovce, konkrétně pomocí Evropa/Berlín časové pásmo.
Příklad 2
db=# SELECT timestamptz '2012-03-05 18:00:00+1';
timestamptz
------------------------
2012-03-05 17:00:00+00
(1 row)
Opět stačí analyzovat datum.
Příklad 3
db=# SELECT timestamp '2012-03-05 18:00:00+1';
timestamp
---------------------
2012-03-05 18:00:00
(1 row)
Toto je stejné jako '2012-03-05 18:00:00+1'::timestamp . Zde se stane, že posun časového pásma je jednoduše ignorován, protože požadujete timestamp .
Příklad 4
db=# SELECT timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6';
timezone
------------------------
2012-03-05 17:00:00+00
(1 row)
Pojďme to přepsat, aby to bylo jednodušší:
db=# SELECT timezone('+6', '2012-03-05 11:00:00'::timestamp);
timezone
------------------------
2012-03-05 17:00:00+00
(1 row)
To se ptá:jaký byl absolutní čas, když hodiny na stěně v časovém pásmu s posunem +6 hodin ukazovaly 2012-03-05 11:00:00 ?
Příklad 5
db=# SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC';
timezone
------------------------
2012-03-05 17:00:00+00
(1 row)
Přepišme:
db=# SELECT timezone('UTC', '2012-03-05 17:00:00'::timestamp);
timezone
------------------------
2012-03-05 17:00:00+00
(1 row)
To se ptá:jaký byl absolutní čas, když hodiny na zdi v časovém pásmu UTC ukazovaly 2012-03-05 17:00:00 ?
Příklad 6
db=# SELECT timestamp '2012-03-05 17:00:00'::timestamp;
timestamp
---------------------
2012-03-05 17:00:00
(1 row)
Zde odesíláte dvakrát do timestamp , což není rozdíl. Pojďme to zjednodušit:
db=# SELECT '2012-03-05 17:00:00'::timestamp;
timestamp
---------------------
2012-03-05 17:00:00
(1 row)
To je myslím jasné.
Příklad 7
db=# SELECT timestamp '2012-03-05 17:00:00'::timestamptz;
timestamptz
------------------------
2012-03-05 17:00:00+00
(1 row)
Přepišme:
db=# SELECT ('2012-03-05 17:00:00'::timestamp)::timestamptz;
timestamptz
------------------------
2012-03-05 17:00:00+00
(1 row)
Nejprve analyzujete řetězec jako timestamp a poté jej převést na timestamptz pomocí aktuálně nastaveného timezone . Pokud změníme timezone , dostaneme něco jiného, protože Postgres předpokládá toto časové pásmo při převodu timestamp (nebo řetězec postrádající informace o časovém pásmu) na 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)
Tento absolutní čas, vyjádřený v UTC, je 2012-03-05 16:00:00+00 , čímž se liší od původního příkladu.
Doufám, že se tím věci vyjasní. Opět, pochopení rozdílu mezi timestamp a timestamptz je klíčová. Představte si čas zdi versus absolutní čas.
