Como obtenho o carimbo de data / hora unix atual do PostgreSQL?

91

O carimbo de data e hora do Unix é o número de segundos desde a meia-noite UTC de 1 de janeiro de 1970.

Como obtenho o carimbo de data / hora unix correto do PostgreSQL?

Ao comparar com currenttimestamp.com e timestamp.1e5b.de , não obtenho o tempo esperado no PostgreSQL:

Isso retorna o carimbo de data / hora correto:

SELECT extract(epoch from now());

Embora isso não ocorra:

SELECT extract(epoch from now() at time zone 'utc');

Eu moro no fuso horário UTC +02. Qual é a maneira correta de obter o carimbo de data / hora unix atual do PostgreSQL?

Isso retorna a hora e o fuso horário corretos:

SELECT now();
              now
-------------------------------
 2011-05-18 10:34:10.820464+02

Outra comparação:

select now(), 
extract(epoch from now()), 
extract(epoch from now() at time zone 'utc');
              now              |    date_part     |    date_part
-------------------------------+------------------+------------------
 2011-05-18 10:38:16.439332+02 | 1305707896.43933 | 1305700696.43933
(1 row)

Unix timestamp from the web sites:
1305707967
Jonas
fonte

Respostas:

82

Em postgres, timestamp with time zonepode ser abreviado como timestamptz, e timestamp without time zonecomo timestamp. Usarei os nomes de tipo mais curtos para simplificar.

Obter o timestamp Unix de um postgres timestamptzcomo now()é simples, como você diz, apenas:

select extract(epoch from now());

É realmente tudo o que você precisa saber sobre como obter o tempo absoluto de qualquer coisa do tipo timestamptz, inclusive now().

As coisas só ficam complicadas quando você tem um timestampcampo.

Quando você coloca timestamptzdados como now()esse campo, eles primeiro são convertidos em um fuso horário específico (explicitamente com at time zoneou pela conversão no fuso horário da sessão) e as informações do fuso horário são descartadas . Já não se refere a um tempo absoluto. É por isso que você normalmente não deseja armazenar carimbos de data timestampe hora como normalmente usaria timestamptz- talvez um filme seja lançado às 18h em uma data específica em cada fuso horário , esse é o tipo de caso de uso.

Se você trabalhar apenas em um único fuso horário, poderá se safar (mis) de usar timestamp. A conversão de volta para timestamptzé inteligente o suficiente para lidar com o horário de verão, e os registros de data e hora são assumidos, para fins de conversão, no fuso horário atual. Aqui está um exemplo para GMT / BST:

select '2011-03-27 00:59:00.0+00'::timestamptz::timestamp::timestamptz
     , '2011-03-27 01:00:00.0+00'::timestamptz::timestamp::timestamptz;

/*
|timestamptz           |timestamptz           |
|:---------------------|:---------------------|
|2011-03-27 00:59:00+00|2011-03-27 02:00:00+01|
*/

DBFiddle

Mas, observe o seguinte comportamento confuso:

set timezone to 0;

values(1, '1970-01-01 00:00:00+00'::timestamp::timestamptz)
    , (2, '1970-01-01 00:00:00+02'::timestamp::timestamptz);

/*
|column1|column2               |
|------:|:---------------------|
|      1|1970-01-01 00:00:00+00|
|      2|1970-01-01 00:00:00+00|
*/

DBFiddle

Isso ocorre porque :

O PostgreSQL nunca examina o conteúdo de uma string literal antes de determinar seu tipo e, portanto, tratará ambos […] como carimbo de data / hora sem fuso horário. Para garantir que um literal seja tratado como registro de data e hora com fuso horário, forneça o tipo explícito correto ... Em um literal que foi determinado como registro de data e hora sem fuso horário, o PostgreSQL ignorará silenciosamente qualquer indicação de fuso horário

Jack Douglas
fonte
Alguma idéia de como converter o decimal resultante em um número inteiro sem ponto decimal (quero dizer, mesclar o número e o decimal como um grande número inteiro). Obrigado.
WM
Como este , mas eu tenho certeza que você realmente não quer fazer isso. Talvez você queira multiplicar por uma potência de dez e retirar os decimais restantes?
Jack Douglas
2
@WM Talvez assim? SELECT FLOOR(EXTRACT(epoch FROM NOW())*1000);
Joe23
21
SELECT extract(epoch from now() at time zone 'utc');

não retorna o carimbo de data e hora correto porque a conversão do fuso horário do postgres joga fora as informações do fuso horário do resultado:

9.9.3 NO FUSO HORÁRIO

Sintaxe: registro de data e hora sem fuso horário NO FUSO FUSO HORÁRIO
Retorna: registro de data e hora com fuso horário
Trate o registro de data e hora sem fuso horário conforme localizado no fuso horário especificado

Sintaxe: registro de data e hora com fuso horário NO FUSO FUSO HORÁRIO
Retorna: registro de data e hora sem fuso horário
Converta o registro de data e hora com fuso horário no novo fuso horário, sem designação de fuso horário

depois, extrair examina o carimbo de data / hora sem fuso horário e o considera um horário local (embora já seja utc de fato).

A maneira correta seria:

select now(),
       extract(epoch from now()),                                          -- correct
       extract(epoch from now() at time zone 'utc'),                       -- incorrect
       extract(epoch from now() at time zone 'utc' at time zone 'utc');    -- correct

          now                  |    date_part     |    date_part     |    date_part
-------------------------------+------------------+------------------+------------------
 2014-10-14 10:19:23.726908+02 | 1413274763.72691 | 1413267563.72691 | 1413274763.72691
(1 row)

Na última linha, a primeira at time zonerealiza a conversão, a segunda atribui um novo fuso horário ao resultado.

axila
fonte