Как мне получить текущую метку времени Unix из PostgreSQL?

91

Метка времени Unix - это количество секунд с полуночи UTC 1 января 1970 года.

Как получить правильную метку времени Unix из PostgreSQL?

При сравнении с currenttimestamp.com и timestamp.1e5b.de я не получаю ожидаемое время от PostgreSQL:

Это возвращает правильную метку времени:

SELECT extract(epoch from now());

Пока это не так:

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

Я живу в часовом поясе UTC + 02. Как правильно получить текущую метку времени Unix из PostgreSQL?

Это возвращает правильное время и часовой пояс:

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

Еще одно сравнение:

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
источник

Ответы:

82

В postgres, timestamp with time zoneможет быть сокращено как timestamptz, и timestamp without time zoneкак timestamp. Я буду использовать более короткие имена типов для простоты.

Получение метки времени Unix от А Postgres , timestamptzкак now()это просто, как вы говорите, просто:

select extract(epoch from now());

Это действительно все, что вам нужно знать о получении абсолютного времени от чего-либо типа timestamptz, в том числе now().

Все становится сложнее, когда у вас есть timestampполе.

Когда вы помещаете timestamptzданные как now()в это поле, они сначала преобразуются в определенный часовой пояс (либо явно, at time zoneлибо путем преобразования в часовой пояс сеанса), а информация о часовом поясе отбрасывается . Это больше не относится к абсолютному времени. Вот почему вы обычно не хотите хранить временные метки так, как timestampэто обычно используется timestamptz- возможно, фильм выходит в 18:00 в определенную дату в каждом часовом поясе , это тот случай использования.

Если вы когда-либо работаете только в одном часовом поясе, вы можете сойти с рук (неправильно) с помощью timestamp. Обратное преобразование в timestamptzдостаточно умное, чтобы справиться с летним временем, и временные метки, как предполагается, для целей преобразования находятся в текущем часовом поясе. Вот пример для 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

Но обратите внимание на следующее запутанное поведение:

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

Это потому что :

PostgreSQL никогда не проверяет содержимое буквенной строки до определения ее типа и поэтому будет обрабатывать обе метки […] как отметку времени без часового пояса. Чтобы литерал обрабатывался как метка времени с часовым поясом, присвойте ему правильный явный тип… В литерале, который был определен как метка времени без часового пояса, PostgreSQL будет молча игнорировать любой указатель часового пояса

Джек Дуглас
источник
Любая идея, как преобразовать полученное десятичное число в целое число без десятичной точки (я имею в виду объединение числа и десятичного числа в одно большое целое число). Благодарю.
WM
Вот так, но я уверен, что ты не хочешь этого делать. Возможно, вы хотите умножить на степень десять и убрать оставшиеся десятичные дроби?
Джек Дуглас
2
@WM Может быть, так? SELECT FLOOR(EXTRACT(epoch FROM NOW())*1000);
Joe23
21
SELECT extract(epoch from now() at time zone 'utc');

не возвращает правильную метку времени, потому что преобразование часового пояса postgres отбрасывает информацию о часовом поясе из результата

9.9.3. В ВРЕМЕННОЙ ЗОНЕ

Синтаксис: временная метка без часового пояса. AT TIME ZONE zone
Возвращает: временная метка с часовым поясом.
Обрабатывать данную временную метку без часового пояса как расположенную в указанном часовом поясе.

Синтаксис: временная метка с часовым поясом. AT TIME ZONE zone
Возвращает: временная метка без часового пояса.
Преобразовать данную временную метку с часовым поясом в новый часовой пояс без указания часового пояса.

после этого извлечение смотрит на метку времени без часового пояса и считает, что это местное время (хотя на самом деле это уже utc).

Правильный путь будет:

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)

В последней строке первая at time zoneвыполняет преобразование, вторая назначает новый часовой пояс для результата.

пазуха
источник