PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Conversione tra fusi orari in Postgres

Mi spiego i due esempi:

In entrambi assumiamo un fuso orario UTC (cioè SET timezone TO UTC ).

db=# SELECT timezone('US/Pacific', '2016-01-01 00:00');
      timezone
---------------------
 2015-12-31 16:00:00
(1 row)

Equivale a SELECT timezone('US/Pacific', '2016-01-01 00:00'::timestamptz) , ovvero Postgres ha convertito implicitamente la stringa in un timestamptz .

Sappiamo che il timezone la funzione converte avanti e indietro tra timestamp e timestamptz :

Dal momento che gli stiamo dando un timestamptz come input, genererà un timestamp . In altre parole, sta convertendo il momento assoluto 2016-01-01 00:00Z a un muro in US/Pacific , ovvero cosa mostrava l'orologio di Los Angeles in quel momento assoluto.

Nell'esempio 2 stiamo facendo l'opposto, ovvero prendendo un timestamp e convertendolo in un timestamptz . In altre parole, ci chiediamo:qual è stato il momento assoluto in cui l'orologio di Los Angeles ha mostrato 2016-01-01 00:00 ?

Menzioni:

'2016-01-01 00:00'::timestamp è un timestamp , cioè un muro. Non ha una nozione di fuso orario.

Penso che potresti non aver compreso appieno la differenza tra timestamp e timestamptz , che qui è fondamentale. Considerali come tempo del muro , ovvero l'ora che mostrava da qualche parte nel mondo su un orologio appeso al muro e ora assoluta , ovvero il tempo assoluto nel nostro universo.

Gli esempi che fai nella tua risposta non sono del tutto accurati.

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);

Il problema con il tuo esempio è che stai costruendo un set di dati con una singola colonna. Poiché una colonna può avere un solo tipo, ogni riga (o un singolo valore in questo caso) viene convertita nello stesso tipo, ovvero timestamptz , anche se alcuni valori sono stati calcolati come timestamp (es. valore 3). Pertanto, qui hai un'ulteriore conversione implicita.

Dividiamo l'esempio in query separate e vediamo cosa sta succedendo:

Esempio 1

db=# SELECT timestamptz '2012-03-05 17:00:00+0';
      timestamptz
------------------------
 2012-03-05 17:00:00+00

Come forse già saprai, timestamptz '2012-03-05 17:00:00+0' e '2012-03-05 17:00:00+0'::timestamptz sono equivalenti (io preferisco quest'ultimo). Quindi, solo per usare la stessa sintassi dell'articolo, riscrivo:

db=# SELECT '2012-03-05 17:00:00+0'::timestamptz;
      timestamptz
------------------------
 2012-03-05 17:00:00+00

Ora, cosa sta succedendo qui? Bene, meno che nella tua spiegazione originale. La stringa viene semplicemente analizzata come timestamptz . Quando il risultato viene stampato, utilizza il timezone attualmente impostato config per riconvertirlo in una rappresentazione leggibile dall'uomo della struttura dei dati sottostante, ad esempio 2012-03-05 17:00:00+00 .

Cambiamo il timezone config e guarda cosa succede:

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

L'unica cosa che è cambiata è come il timestamptz viene stampato sullo schermo, in particolare utilizzando Europa/Berlino fuso orario.

Esempio 2

db=# SELECT timestamptz '2012-03-05 18:00:00+1';
      timestamptz
------------------------
 2012-03-05 17:00:00+00
(1 row)

Ancora una volta, sto solo analizzando la data.

Esempio 3

db=# SELECT timestamp '2012-03-05 18:00:00+1';
      timestamp
---------------------
 2012-03-05 18:00:00
(1 row)

È lo stesso di '2012-03-05 18:00:00+1'::timestamp . Quello che succede qui è che l'offset del fuso orario viene semplicemente ignorato perché stai chiedendo un timestamp .

Esempio 4

db=# SELECT timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6';
        timezone
------------------------
 2012-03-05 17:00:00+00
(1 row)

Riscriviamo per essere più semplici:

db=# SELECT timezone('+6', '2012-03-05 11:00:00'::timestamp);
        timezone
------------------------
 2012-03-05 17:00:00+00
(1 row)

Questo è chiedere:qual era l'ora assoluta in cui l'orologio sul muro nel fuso orario con un offset di +6 ore mostrava 2012-03-05 11:00:00 ?

Esempio 5

db=# SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC';
        timezone
------------------------
 2012-03-05 17:00:00+00
(1 row)

Riscriviamo:

db=# SELECT timezone('UTC', '2012-03-05 17:00:00'::timestamp);
        timezone
------------------------
 2012-03-05 17:00:00+00
(1 row)

Questo è chiedere:qual era l'ora assoluta in cui l'orologio sul muro nel fuso orario UTC mostrava 2012-03-05 17:00:00 ?

Esempio 6

db=# SELECT timestamp '2012-03-05 17:00:00'::timestamp;
      timestamp
---------------------
 2012-03-05 17:00:00
(1 row)

Qui stai trasmettendo due volte a timestamp , che non fa differenza. Semplifichiamo:

db=# SELECT '2012-03-05 17:00:00'::timestamp;
      timestamp
---------------------
 2012-03-05 17:00:00
(1 row)

È chiaro, credo.

Esempio 7

db=# SELECT timestamp '2012-03-05 17:00:00'::timestamptz;
      timestamptz
------------------------
 2012-03-05 17:00:00+00
(1 row)

Riscriviamo:

db=# SELECT ('2012-03-05 17:00:00'::timestamp)::timestamptz;
      timestamptz
------------------------
 2012-03-05 17:00:00+00
(1 row)

Stai prima analizzando la stringa come timestamp e poi convertendolo in un timestamptz utilizzando il timezone attualmente impostato . Se cambiamo il timezone , otteniamo qualcos'altro perché Postgres presuppone quel fuso orario durante la conversione di un timestamp (o una stringa priva di informazioni sul fuso orario) in 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)

Questo tempo assoluto, espresso in UTC, è 2012-03-05 16:00:00+00 , quindi diverso dall'esempio originale.

Spero che questo chiarisca le cose. Ancora una volta, capire la differenza tra timestamp e timestamptz è la chiave. Pensa al tempo del muro rispetto al tempo assoluto.