Comment puis-je obtenir le timestamp unix actuel de PostgreSQL?

91

L'horodatage Unix est le nombre de secondes écoulées depuis le 1er janvier 1970 à minuit UTC.

Comment puis-je obtenir le timestamp unix correct de PostgreSQL?

En comparant avec currenttimestamp.com et timestamp.1e5b.de, je ne reçois pas l'heure attendue de PostgreSQL:

Cela retourne l'horodatage correct:

SELECT extract(epoch from now());

Bien que cela ne soit pas:

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

Je vis dans le fuseau horaire UTC +02. Quelle est la bonne façon d’obtenir le timestamp unix actuel de PostgreSQL?

Cela renvoie l'heure et le fuseau horaire corrects:

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

Une autre comparaison:

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
la source

Réponses:

82

Dans postgres, timestamp with time zonepeut être abrégé en timestamptz, et timestamp without time zoneen timestamp. Je vais utiliser les noms de types les plus courts pour plus de simplicité.

Obtenir le timestamp Unix depuis un postgres timestamptzlike now()est simple, comme vous dites, simplement:

select extract(epoch from now());

C'est tout ce que vous devez savoir pour obtenir l'heure absolue de tout type timestamptz, y compris now().

Les choses ne se compliquent que lorsque vous avez un timestampchamp.

Lorsque vous placez des timestamptzdonnées comme now()dans ce champ, elles sont d'abord converties dans un fuseau horaire particulier (explicitement avec at time zoneou en convertissant dans le fuseau horaire de la session) et les informations relatives au fuseau horaire sont supprimées . Il ne fait plus référence à un temps absolu. C’est la raison pour laquelle vous ne souhaitez généralement pas stocker les horodatages de la manière timestamphabituelle timestamptz. Un film est peut-être diffusé à 18 heures à une date donnée, dans chaque fuseau horaire , c’est le genre de cas d’utilisation.

Si vous ne travaillez que dans un seul fuseau horaire, vous pouvez vous en tirer (mal) timestamp. La conversion vers timestamptzest suffisamment intelligente pour prendre en charge l'heure d'été, et les horodatages sont supposés, pour les besoins de la conversion, se trouver dans le fuseau horaire actuel. Voici un exemple pour 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

Mais notez le comportement déroutant suivant:

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

C'est parce que :

PostgreSQL ™ n'examine jamais le contenu d'une chaîne littérale avant de déterminer son type, et traitera donc les deux […] comme des horodatages sans fuseau horaire. Pour vous assurer qu'un littéral est traité comme un horodatage avec fuseau horaire, donnez-lui le type explicite correct… Dans un littéral déterminé comme étant un horodatage sans fuseau horaire, PostgreSQL ™ ignorera en silence toute indication de fuseau horaire.

Jack Douglas
la source
Toute idée de la façon de convertir le nombre décimal obtenu en un entier sans point décimal (je parle de la fusion du nombre et du nombre décimal en un seul grand nombre). Merci.
WM
Comme ça, mais je suis sûr que tu ne veux pas vraiment faire ça. Vous voulez peut-être multiplier par une puissance de dix et éliminer les décimales restantes?
Jack Douglas
2
@WM Peut-être comme ça? SELECT FLOOR(EXTRACT(epoch FROM NOW())*1000);
Joe23
21
SELECT extract(epoch from now() at time zone 'utc');

ne retourne pas l'horodatage correct car la conversion de fuseau horaire postgres supprime les informations de fuseau horaire du résultat:

9.9.3. À l'heure

Syntaxe: horodatage sans fuseau horaire dans la zone de fuseau horaire
Retour: horodatage avec fuseau horaire
Traiter l' horodatage donné sans fuseau horaire comme situé dans le fuseau horaire spécifié

Syntaxe: horodatage avec fuseau horaire dans la zone FUSEAU HORAIRE
Renvoie: horodatage sans fuseau horaire
Convertir un horodatage donné avec fuseau horaire dans le nouveau fuseau horaire, sans désignation de fuseau horaire

Ensuite, extraire regarde l'horodatage sans fuseau horaire et considère qu'il s'agit d'une heure locale (bien que l'heure soit déjà utc).

La bonne façon serait:

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)

Dans la dernière ligne, la première at time zoneeffectue la conversion, la seconde attribue un nouveau fuseau horaire au résultat.

aile
la source