sql >> Databáze >  >> RDS >> PostgreSQL

Převod mezi časovými pásmy v Postgresu

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.



  1. Duplicitní záznamy v DB

  2. Bezplatná databáze polní nemocnice pro boj s pandemií COVID-19

  3. Vrátit všechny cizí klíče, které odkazují na danou tabulku v SQL Server

  4. Snížení nákladů na licence SQL Server