Sembra che tu voglia memorizzare un ora locale rispetto a un determinato fuso orario. In tal caso, memorizza un timestamp
(senza fuso orario) e il timezone
in una colonna separata.
Ad esempio, supponiamo di voler registrare un evento che si verificherà alle 10:00 del 26 febbraio 2030 a Chicago e che debba essere alle 10:00 ora locale indipendentemente dalla regola del fuso orario in vigore in quella data.
Se il database memorizza il timestamp senza fuso orario:
unutbu=# select '2030-02-26 10:00:00'::timestamp as localtime, 'America/Chicago' AS tzone;
+---------------------+-----------------+
| localtime | tzone |
+---------------------+-----------------+
| 2030-02-26 10:00:00 | America/Chicago |
+---------------------+-----------------+
Successivamente, puoi trovare la data e l'ora UTC dell'evento utilizzando
unutbu=# select '2030-02-26 10:00:00'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
| timezone |
+---------------------+
| 2030-02-26 16:00:00 |
+---------------------+
La query restituisce la data e l'ora UTC, 2030-02-26 16:00:00
, che corrisponde a 2030-02-26 10:00:00
ora locale a Chicago.
Utilizzando AT TIME ZONE
ritarda l'applicazione delle regole del fuso orario a quando viene eseguita la query invece di quando il timestamptz
è stato inserito.
Utilizzando AT TIME ZONE
su un timestamp
localizza la data e l'ora nel fuso orario specificato, ma segnala la data e l'ora nel fuso orario dell'utente .Utilizzando AT TIME ZONE
su un timestamptz
converte la data e l'ora nel fuso orario specificato, quindi elimina l'offset, restituendo così un timestamp
.Sopra, AT TIME ZONE
viene utilizzato due volte:prima per localizzare un timestamp
e poi per convertire il timestamptz
restituito a un nuovo fuso orario (UTC). Il risultato è un timestamp
in UTC.
Ecco un esempio, che mostra AT TIME ZONE
comportamento di timestamp
s:
unutbu=# SET timezone = 'America/Chicago';
unutbu=# SELECT '2030-02-26 10:00:00'::timestamp AT TIME ZONE 'America/Chicago';
+------------------------+
| timezone |
+------------------------+
| 2030-02-26 10:00:00-06 |
+------------------------+
unutbu=# SET timezone = 'America/Los_Angeles';
unutbu=# SELECT '2030-02-26 10:00:00'::timestamp AT TIME ZONE 'America/Chicago';
+------------------------+
| timezone |
+------------------------+
| 2030-02-26 08:00:00-08 |
+------------------------+
2030-02-26 10:00:00-06
e 2030-02-26 08:00:00-08
sono le stesse datetime ma riportate in fusi orari utente diversi. Questo mostra che le 10:00 a Chicago sono le 8:00 a Los Angeles (usando le attuali definizioni di fuso orario):
unutbu=# SELECT '2030-02-26 10:00:00-06'::timestamptz AT TIME ZONE 'America/Los_Angeles';
+---------------------+
| timezone |
+---------------------+
| 2030-02-26 08:00:00 |
+---------------------+
Un'alternativa all'utilizzo di AT TIME ZONE
due volte è per impostare il fuso orario dell'utente
a UTC
. Allora potresti usare
select localtime AT TIME ZONE tzone
Nota che, se eseguito in questo modo, viene visualizzato un timestamptz
viene restituito invece di un timestamp
.
Fai attenzione perché la memorizzazione dell'ora locale può essere problematica perché possono esserci orari inesistenti e orari ambigui. Ad esempio, 2018-03-11 02:30:00
è un'ora locale inesistente in America/Chicago
. Postgresql normalizza l'ora locale inesistente assumendo che si riferisca all'ora corrispondente dopo l'inizio dell'ora legale (DST) (come se qualcuno avesse dimenticato di impostare l'orologio in avanti):
unutbu=# select '2018-03-11 02:30:00'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
| timezone |
+---------------------+
| 2018-03-11 08:30:00 |
+---------------------+
(1 row)
unutbu=# select '2018-03-11 03:30:00'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
| timezone |
+---------------------+
| 2018-03-11 08:30:00 |
+---------------------+
(1 row)
Un esempio di ora locale ambigua è 2018-11-04 01:00:00
in America/Chicago
. Si verifica due volte a causa dell'ora legale. Postgresql risolve questa ambiguità scegliendo l'ora successiva, dopo la fine dell'ora legale:
unutbu=# select '2018-11-04 01:00:00'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
| timezone |
+---------------------+
| 2018-11-04 07:00:00 |
+---------------------+
Nota che questo significa che non c'è modo di fare riferimento a 2018-11-04 06:00:00 UTC
memorizzando l'ora locale in America/Chicago
fuso orario:
unutbu=# select '2018-11-04 00:59:59'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
| timezone |
+---------------------+
| 2018-11-04 05:59:59 |
+---------------------+