Parece que você deseja armazenar um localtime em relação a um determinado fuso horário. Nesse caso, armazene um
timestamp
(sem fuso horário) e o timezone
em uma coluna separada. Por exemplo, suponha que você queira gravar um evento que ocorrerá às 10h do dia 26 de fevereiro de 2030 em Chicago e deve ser às 10h localtime independentemente da regra de fuso horário em vigor nessa data.
Se o banco de dados armazenar o timestamp sem fuso horário:
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 |
+---------------------+-----------------+
Então, mais tarde, você pode encontrar a data e hora UTC do evento usando
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 |
+---------------------+
A consulta retorna a data e hora UTC,
2030-02-26 16:00:00
, que corresponde a 2030-02-26 10:00:00
hora local em Chicago. Usando
AT TIME ZONE
atrasa a aplicação das regras de fuso horário para quando a consulta é feita em vez de quando o timestamptz
foi inserido. Usando
AT TIME ZONE
em um timestamp
localiza a data e hora para o fuso horário especificado, mas relatórios o datetime no fuso horário do usuário .Usando AT TIME ZONE
em um timestamptz
converte a data e hora para o fuso horário especificado e, em seguida, descarta o deslocamento, retornando um timestamp
.Acima, AT TIME ZONE
é usado duas vezes:primeiro para localizar um timestamp
e em seguida para converter o timestamptz
retornado para um novo fuso horário (UTC). O resultado é um timestamp
em UTC. Aqui está um exemplo, demonstrando
AT TIME ZONE
comportamento do 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
são os mesmos datetimes, mas relatados em fusos horários de usuários diferentes. Isso mostra que 10h em Chicago é 8h em Los Angeles (usando as definições atuais de fuso horário):unutbu=# SELECT '2030-02-26 10:00:00-06'::timestamptz AT TIME ZONE 'America/Los_Angeles';
+---------------------+
| timezone |
+---------------------+
| 2030-02-26 08:00:00 |
+---------------------+
Uma alternativa ao uso de
AT TIME ZONE
duas vezes é definir o fuso horário do usuário
para UTC
. Então você poderia usar select localtime AT TIME ZONE tzone
Observe que, quando feito dessa maneira, um
timestamptz
é retornado em vez de um timestamp
. Esteja ciente de que armazenar horários locais pode ser problemático porque pode haver horários inexistentes e horários ambíguos. Por exemplo,
2018-03-11 02:30:00
é um localtime inexistente em America/Chicago
. O Postgresql normaliza horários locais inexistentes assumindo que se refere ao horário correspondente após o início do horário de verão (DST) (como se alguém tivesse esquecido de adiantar o relógio):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)
Um exemplo de hora local ambígua é
2018-11-04 01:00:00
em America/Chicago
. Ocorre duas vezes devido ao horário de verão. O Postgresql resolve essa ambiguidade escolhendo a hora posterior, após o término do horário de verão: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 |
+---------------------+
Observe que isso significa que não há como fazer referência a
2018-11-04 06:00:00 UTC
armazenando horários locais no America/Chicago
fuso horário: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 |
+---------------------+