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

Conversione errata di PostgreSQL da timestamp senza fuso orario a timestamp con fuso orario

Cose chiave da capire

timestamp without time zone AT TIME ZONE reinterpreta un timestamp essendo in quel fuso orario allo scopo di convertirlo in UTC .

timestamp with time zone AT TIME ZONE convertiti un timestamptz in un timestamp nel fuso orario specificato.

PostgreSQL utilizza i fusi orari ISO-8601, che specificano che a est di Greenwich è positivo ... a meno che non si utilizzi uno specificatore di fuso orario POSIX, nel qual caso segue POSIX. Ne consegue la follia.

Perché il primo produce un risultato inaspettato

Timestamp e fusi orari in SQL sono orribili. Questo:

select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'EST5EDT';

interpreta il letterale di tipo sconosciuto '2011-12-30 00:30:00' come timestamp without time zone , che Pg presume sia nel fuso orario locale se non diversamente indicato. Quando usi AT TIME ZONE , è (secondo le specifiche) reinterpretato come timestamp with time zone nel fuso orario EST5EDT quindi memorizzato come ora assoluta in UTC, quindi viene convertito da EST5EDT a UTC, ovvero l'offset del fuso orario viene sottratto . x - (-5) è x + 5 .

Questo timestamp, adattato allo spazio di archiviazione UTC, viene quindi adattato al TimeZone del tuo server impostazione per la visualizzazione in modo che venga visualizzata nell'ora locale.

Se invece desideri dire "Ho questo timestamp nell'ora UTC e desideri vedere qual è l'ora locale equivalente in EST5EDT", se vuoi essere indipendente dall'impostazione del fuso orario del server, devi scrivere qualcosa del tipo:

select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
       AT TIME ZONE 'EST5EDT';

Questo dice "Data il timestamp 2011-12-30 00:30:00, consideralo come un timestamp in UTC quando converti in timestamptz, quindi converti quel timestamptz in un'ora locale in EST5EDT".

Orribile, vero? Voglio parlare con un ditta chiunque abbia deciso la folle semantica di AT TIME ZONE - dovrebbe essere davvero qualcosa come timestamp CONVERT FROM TIME ZONE '-5' e timestamptz CONVERT TO TIME ZONE '+5' . Inoltre, timestamp with time zone dovrebbe effettivamente portare con sé il suo fuso orario, non essere archiviato in UTC e convertito automaticamente in ora locale.

Perché il secondo funziona (purché TimeZone =UTC)

La tua versione originale "funziona":

select '2011-12-30 00:30:00' AT TIME ZONE 'EST5EDT';

sarà corretto solo se TimeZone è impostato su UTC, perché il cast da testo a timestamptz assume TimeZone quando non è specificato.

Perché il terzo funziona

Due problemi si annullano a vicenda.

L'altra versione che sembra funzionare è TimeZone indipendente, ma funziona solo perché due problemi si annullano. Innanzitutto, come spiegato sopra, timestamp without time zone AT TIME ZONE reinterpreta il timestamp come in quel fuso orario per la conversione in un timestamptz UTC; questo effettivamente sottrae l'offset del fuso orario.

Tuttavia, per ragioni che esulano dalle mie conoscenze, PostgreSQL utilizza timestamp con il segno inverso rispetto a ciò che sono abituato a vedere nella maggior parte dei luoghi. Consulta la documentazione:

Un altro problema da tenere a mente è che nei nomi dei fusi orari POSIX vengono utilizzati offset positivi per le località a ovest di Greenwich. Ovunque, PostgreSQL segue la convenzione ISO-8601 secondo cui gli offset positivi del fuso orario sono a est di Greenwich.

Ciò significa che EST5EDT è lo stesso di +5 , non -5 . Ecco perché funziona:perché stai sottraendo l'offset tz non aggiungendolo, ma stai sottraendo un offset negato!

Quello di cui avresti bisogno per farlo correttamente è invece:

select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
       AT TIME ZONE '+5';