¿Cómo obtengo la marca de tiempo de Unix actual de PostgreSQL?

91

La marca de tiempo de Unix es el número de segundos desde la medianoche UTC del 1 de enero de 1970.

¿Cómo obtengo la marca de tiempo correcta de Unix de PostgreSQL?

Al comparar con currenttimestamp.com y timestamp.1e5b.de , no obtengo el tiempo esperado de PostgreSQL:

Esto devuelve la marca de tiempo correcta:

SELECT extract(epoch from now());

Si bien esto no:

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

Vivo en la zona horaria UTC +02. ¿Cuál es la forma correcta de obtener la marca de tiempo de Unix actual de PostgreSQL?

Esto devuelve la hora y zona horaria correctas:

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

Otra comparación:

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
fuente

Respuestas:

82

En postgres, timestamp with time zonese puede abreviar como timestamptz, y timestamp without time zonecomo timestamp. Usaré los nombres de tipo más cortos por simplicidad.

Obtener la marca de tiempo de Unix de un postgres timestamptzcomo now()es simple, como usted dice, solo:

select extract(epoch from now());

Eso es realmente todo lo que necesita saber para obtener el tiempo absoluto de cualquier tipo timestamptz, incluso now().

Las cosas solo se complican cuando tienes un timestampcampo.

Cuando coloca timestamptzdatos como now()en ese campo, primero se convertirá a una zona horaria particular (ya sea explícitamente at time zoneo mediante la conversión a la zona horaria de la sesión) y la información de la zona horaria se descartará . Ya no se refiere a un tiempo absoluto. Es por eso que generalmente no desea almacenar las marcas de tiempo como las timestampusaría normalmente timestamptz; tal vez una película se estrene a las 6 p.m. en una fecha particular en cada zona horaria , ese es el tipo de caso de uso.

Si solo trabaja en una zona horaria única, podría salirse con la suya timestamp. La conversión de regreso a timestamptzes lo suficientemente inteligente como para hacer frente a DST, y se supone que las marcas de tiempo, para fines de conversión, se encuentran en la zona horaria actual. Aquí hay un ejemplo 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

Pero, tenga en cuenta el siguiente comportamiento 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

Esto es porque :

PostgreSQL nunca examina el contenido de una cadena literal antes de determinar su tipo y, por lo tanto, tratará a ambos [...] como marca de tiempo sin zona horaria. Para asegurarse de que un literal se trate como marca de tiempo con zona horaria, dele el tipo explícito correcto ... En un literal que se ha determinado que es una marca de tiempo sin zona horaria, PostgreSQL ignorará en silencio cualquier indicación de zona horaria

Jack Douglas
fuente
Alguna idea de cómo convertir el decimal resultante en un entero sin punto decimal (me refiero a fusionar el número y el decimal como un entero grande). Gracias.
WM
Al igual que este , pero estoy seguro de que realmente no quiere hacer eso. ¿Quizás quieras multiplicar por una potencia de diez y despojar los decimales restantes?
Jack Douglas
2
@WM Tal vez así? SELECT FLOOR(EXTRACT(epoch FROM NOW())*1000);
Joe23
21
SELECT extract(epoch from now() at time zone 'utc');

no devuelve la marca de tiempo correcta porque la conversión de la zona horaria de postgres arroja la información de la zona horaria del resultado:

9.9.3. EN LA ZONA HORARIA

Sintaxis: marca de tiempo sin zona horaria EN ZONA HORA zona
Devuelve: marca de tiempo con zona horaria
Tratar la marca de tiempo dada sin zona horaria como se encuentra en la zona horaria especificada

Sintaxis: marca de tiempo con zona horaria EN ZONA HORA zona
Devuelve: marca de tiempo sin zona horaria
Convertir sello de tiempo dado con zona horaria a la nueva zona horaria, sin designación de zona horaria

luego, extract mira la marca de tiempo sin zona horaria y considera que es una hora local (aunque de hecho ya es utc).

La forma correcta sería:

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)

En la última línea, el primero at time zonerealiza la conversión, el segundo asigna una nueva zona horaria al resultado.

axila
fuente