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.