O tipo de dados timestamp
é o nome abreviado para timestamp without time zone
.
A outra opção timestamptz
é a abreviação de timestamp with time zone
.
timestamptz
é o tipo preferido na família de data / hora, literalmente. Foi typispreferred
definido pg_type
, o que pode ser relevante:
Armazenamento interno e época
Internamente, os carimbos de data / hora ocupam 8 bytes de armazenamento no disco e na RAM. É um valor inteiro representando a contagem de microssegundos da época do Postgres, 2000-01-01 00:00:00 UTC.
O Postgres também possui um conhecimento interno do tempo UNIX comumente usado, contando segundos da época UNIX, 1970-01-01 00:00:00 UTC, e usa isso em funções to_timestamp(double precision)
ou EXTRACT(EPOCH FROM timestamptz)
.
O código fonte:
* Os carimbos de data e hora, bem como os campos h / m / s de intervalos, são armazenados como
* valores int64 com unidades de microssegundos. (Era uma vez eles estavam
* valores duplos com unidades de segundos.)
E:
/ * Equivalentes à data juliana do dia 0 no cálculo de Unix e Postgres * /
#define UNIX_EPOCH_JDATE 2440588 / * == date2j (1970, 1, 1) * /
#define POSTGRES_EPOCH_JDATE 2451545 / * == date2j (2000, 1, 1) * /
A resolução de microssegundos é convertida em no máximo 6 dígitos fracionários por segundos.
timestamp
Um valor digitado como informa ao Postgres que nenhum fuso horário é fornecido explicitamente. O fuso horário atual é assumido. O Postgres ignora qualquer modificador de fuso horário adicionado por engano!timestamp
[without time zone]
Nenhuma hora é alterada para exibição. Com o mesmo fuso horário, tudo está bem. Para um fuso horário diferente, o significado muda, mas o valor e a exibição permanecem os mesmos.
timestamptz
O manuseio timestamp with time zone
é sutilmente diferente. Cito o manual aqui :
Pois timestamp with time zone
, o valor armazenado internamente está sempre em UTC (Universal Coordinated Time ...)
Negrito ênfase minha. O fuso horário em si nunca é armazenado . É um modificador de entrada usado para calcular o registro de data e hora UTC correspondente, que é armazenado - ou modificador de saída usado para calcular a hora local a ser exibida - com deslocamento de fuso horário anexado. Se você não anexar um deslocamento para timestamptz
na entrada, a configuração de fuso horário atual da sessão será assumida. Todos os cálculos são feitos com valores de carimbo de data / hora UTC. Se você precisar (ou precisar) lidar com mais de um fuso horário, use timestamptz
.
Clientes como psql ou pgAdmin ou qualquer aplicativo que esteja se comunicando via libpq (como Ruby com a pg gem) são apresentados com o carimbo de data / hora e o deslocamento para o fuso horário atual ou de acordo com o fuso horário solicitado (veja abaixo). É sempre o mesmo momento , apenas o formato de exibição varia. Ou, como o manual coloca :
Todas as datas e horas com reconhecimento de fuso horário são armazenadas internamente no UTC. Eles são convertidos para a hora local na zona especificada pelo
parâmetro de configuração TimeZone antes de serem exibidos para o cliente.
Considere este exemplo simples (em psql):
db = # SELECT timestamptz '2012-03-05 20:00 +03 ';
timestamptz
------------------------
2012-03-05 18:00:00 +01
Negrito ênfase minha. O que aconteceu aqui?
Eu escolhi um deslocamento de fuso horário arbitrário +3
para o literal de entrada. Para o Postgres, esta é apenas uma das muitas maneiras de inserir o carimbo de data / hora UTC 2012-03-05 17:00:00
. O resultado da consulta é exibido para a configuração atual do fuso horário Viena / Áustria em meu teste, que tem um deslocamento +1
durante o inverno e +2
durante o horário de verão:, 2012-03-05 18:00:00+01
porque ocorre no horário de inverno.
O Postgres já esqueceu como esse valor foi inserido. Tudo o que lembra é o valor e o tipo de dados. Assim como com um número decimal. numeric '003.4'
, numeric '3.40'
ou numeric '+3.4'
- todos resultam exatamente no mesmo valor interno.
AT TIME ZONE
Assim que você entender essa lógica, poderá fazer o que quiser. Tudo o que está faltando agora é uma ferramenta para interpretar ou representar literais de registro de data e hora de acordo com um fuso horário específico. É aí que AT TIME ZONE
entra a construção. Existem dois casos de uso diferentes. timestamptz
é convertido timestamp
e vice-versa.
Para inserir o UTC timestamptz
2012-03-05 17:00:00+0
:
SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC'
... o que equivale a:
SELECT timestamptz '2012-03-05 17:00:00 UTC'
Para exibir o mesmo ponto no tempo que o EST timestamp
(Horário padrão do leste):
SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC' AT TIME ZONE 'EST'
Isso mesmo, AT TIME ZONE 'UTC'
duas vezes . O primeiro interpreta o timestamp
valor como (dado) carimbo de data / hora UTC retornando o tipo timestamptz
. O segundo converte timestamptz
para o timestamp
no fuso horário fornecido 'EST' - o que um relógio no fuso horário EST exibe nesse momento único.
Exemplos
SELECT ts AT TIME ZONE 'UTC'
FROM (
VALUES
(1, timestamptz '2012-03-05 17:00:00+0')
, (2, timestamptz '2012-03-05 18:00:00+1')
, (3, timestamptz '2012-03-05 17:00:00 UTC')
, (4, timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6')
, (5, timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC')
, (6, timestamp '2012-03-05 07:00:00' AT TIME ZONE 'US/Hawaii') -- ①
, (7, timestamptz '2012-03-05 07:00:00 US/Hawaii') -- ①
, (8, timestamp '2012-03-05 07:00:00' AT TIME ZONE 'HST') -- ①
, (9, timestamp '2012-03-05 18:00:00+1') -- ② loaded footgun!
) t(id, ts);
Retorna 8 (ou 9) linhas idênticas com colunas timestamptz com o mesmo registro de data e hora UTC 2012-03-05 17:00:00
. Por acaso, a 9ª linha funciona no meu fuso horário, mas é uma armadilha do mal. Ver abaixo.
① As linhas 6 - 8 com o nome do fuso horário e a abreviação do fuso horário do Havaí estão sujeitas ao horário de verão (DST) e podem diferir, embora não atualmente. Um nome de fuso horário como 'US/Hawaii'
conhece as regras de horário de verão e todos os históricos mudam automaticamente, enquanto uma abreviação como HST
é apenas um código idiota para um deslocamento fixo. Pode ser necessário anexar uma abreviação diferente para o horário de verão / padrão. O nome interpreta corretamente qualquer carimbo de data / hora no fuso horário especificado. Uma abreviação é barata, mas precisa ser a correta para o carimbo de data / hora especificado:
O horário de verão não está entre as idéias mais brilhantes que a humanidade já teve.
② A linha 9, marcada como espingarda carregada, funciona para mim , mas apenas por coincidência. Se você converter explicitamente um literal para timestamp [without time zone]
, qualquer deslocamento de fuso horário será ignorado ! Somente o registro de data e hora simples é usado. O valor é coagido automaticamente timestamptz
no exemplo para corresponder ao tipo de coluna. Para esta etapa, timezone
é assumida a configuração da sessão atual, que passa a ser o mesmo fuso horário +1
no meu caso (Europa / Viena). Mas provavelmente não no seu caso - o que resultará em um valor diferente. Em suma: Não molde timestamptz
literais para timestamp
ou você perde o deslocamento de fuso horário.
Suas perguntas
O usuário armazena um horário, digamos 17 de março de 2012, 19:00. Não quero que as conversões de fuso horário ou o fuso horário sejam armazenados.
O fuso horário em si nunca é armazenado. Use um dos métodos acima para inserir um carimbo de data / hora UTC.
Uso apenas o fuso horário especificado pelo usuário para obter registros 'antes' ou 'depois' da hora atual no fuso horário local do usuário.
Você pode usar uma consulta para todos os clientes em diferentes fusos horários.
Para o tempo global absoluto:
SELECT * FROM tbl WHERE time_col > (now() AT TIME ZONE 'UTC')::time
Para o tempo de acordo com o relógio local:
SELECT * FROM tbl WHERE time_col > now()::time
Ainda não está cansado de informações básicas? Há mais no manual.
Se você deseja negociar no UTC por padrão:
Em
config/application.rb
, adicione:Então, se você armazenar o nome atual do fuso horário do usuário,
current_user.timezone
pode dizer.current_user.timezone
deve ser um nome de fuso horário válido; caso contrário, vocêArgumentError: Invalid Timezone
verá uma lista completa .fonte