Utilisation de l'heure actuelle en UTC comme valeur par défaut dans PostgreSQL

172

J'ai une colonne du TIMESTAMP WITHOUT TIME ZONEtype et j'aimerais avoir cette valeur par défaut à l'heure actuelle en UTC. Obtenir l'heure actuelle en UTC est facile:

postgres=# select now() at time zone 'utc';
          timezone          
----------------------------
 2013-05-17 12:52:51.337466
(1 row)

Comme pour l'utilisation de l'horodatage actuel pour une colonne:

postgres=# create temporary table test(id int, ts timestamp without time zone default current_timestamp);
CREATE TABLE
postgres=# insert into test values (1) returning ts;
             ts             
----------------------------
 2013-05-17 14:54:33.072725
(1 row)

Mais cela utilise l'heure locale. Essayer de forcer cela à UTC entraîne une erreur de syntaxe:

postgres=# create temporary table test(id int, ts timestamp without time zone default now() at time zone 'utc');
ERROR:  syntax error at or near "at"
LINE 1: ...int, ts timestamp without time zone default now() at time zo...
Wichert Akkerman
la source

Réponses:

297

Une fonction n'est même pas nécessaire. Mettez simplement des parenthèses autour de l'expression par défaut:

create temporary table test(
    id int, 
    ts timestamp without time zone default (now() at time zone 'utc')
);
Daniel Vérité
la source
2
Je peux voir une fonction comme now_utc () brille vraiment lors de l'écriture de requêtes
misaxi
Cela fonctionne même lorsque l'heure recule d'une heure - now () renvoie un horodatage qui connaît le décalage par rapport à UTC.
Clay Lenhart
1
FWIW, l' exécution de cette requête dans PostgreSQL 11.5: ALTER TABLE testcase_result ADD COLUMN date_created TIMESTAMP WITHOUT TIME ZONE DEFAULT (NOW() AT TIME ZONE "UTC") NOT NULL;échoue avec: ERROR: column "UTC" does not exist. Assurez-vous que 'utc'tout est en minuscules.
code_dredd
2
Correction: la 'utc'chaîne doit également être entre guillemets simples et non entre guillemets doubles.
code_dredd
56

Encore une autre solution:

timezone('utc', now())
Martti
la source
26

Enveloppez-le dans une fonction:

create function now_utc() returns timestamp as $$
  select now() at time zone 'utc';
$$ language sql;

create temporary table test(
  id int,
  ts timestamp without time zone default now_utc()
);
Denis de Bernardy
la source
16

Qu'en est-il de

now()::timestamp

Si votre autre horodatage est sans fuseau horaire, cette distribution donnera le type correspondant «horodatage sans fuseau horaire» pour l'heure actuelle.

J'aimerais toutefois lire ce que les autres pensent de cette option. Je n'ai toujours pas confiance en ma compréhension de ce truc de fuseau horaire "avec / sans".

EDIT: Ajout du commentaire de Michael Ekoka ici car il clarifie un point important:

Caveat. La question est de générer un horodatage par défaut en UTC pour une colonne d'horodatage qui ne stocke pas le fuseau horaire (peut-être parce qu'il n'est pas nécessaire de stocker le fuseau horaire si vous savez que tous vos horodatages partagent le même). Ce que fait votre solution est de générer un horodatage local (qui pour la plupart des gens ne sera pas nécessairement défini sur UTC) et de le stocker sous forme d'horodatage naïf (qui ne spécifie pas son fuseau horaire).

Risadinha
la source
C'est une astuce intéressante mais elle peut prêter à confusion à moins que vous ne soyez conscient de ce comportement. La réponse acceptée est limpide sur l'action et le résultat souhaité. Même chose avec la fonction mais je reste à l'écart des fonctions dans les DB ...
Frank V
dans mon option, il génère une date
heure
Caveat. La question est de générer un horodatage par défaut en UTC pour une colonne d'horodatage qui ne stocke pas le fuseau horaire (peut-être parce qu'il n'est pas nécessaire de stocker le fuseau horaire si vous savez que tous vos horodatages partagent le même). Ce que fait votre solution est de générer un horodatage local (qui pour la plupart des gens ne sera pas nécessairement défini sur UTC) et de le stocker sous forme d'horodatage naïf (qui ne spécifie pas son fuseau horaire).
Michael Ekoka
@MichaelEkoka J'ai ajouté votre commentaire dans la réponse - allez-y et modifiez-le si vous le souhaitez. Vous l'expliquez très clairement. Merci!
Risadinha
Attention : un horodatage avec champ de fuseau horaire ne stocke PAS le fuseau horaire, contrairement à une hypothèse raisonnable. Consultez le lien ci-dessous et recherchez la page UTC. En entrée, un horodatage avec fuseau horaire convertit la valeur entrante en utc et stocke cette valeur SANS INFORMATIONS DE FUSEAU HORAIRE OU DE DÉCALAGE . En sortie, la valeur utc stockée est convertie en heure locale en utilisant le fuseau horaire du client si disponible. Ce type concerne la conversion de valeur au moment de la requête. Vous devriez tester cela en stockant dans un fuseau horaire et en sélectionnant dans un autre. postgresql.org/docs/11/datatype-datetime.html
Tom
7

Ce sont 2 solutions équivalentes:

(dans le code suivant, vous devez remplacer 'UTC'par zone et now()pour l' horodatage )

  1. timestamp AT TIME ZONE zone - Conforme à la norme SQL
  2. timezone(zone, timestamp) - sans doute plus lisible

La fonction timezone (zone, horodatage) équivaut à l'horodatage de construction conforme à SQL AT TIME ZONE zone.


Explication:

  • la zone peut être spécifiée soit sous forme de chaîne de texte (par exemple, 'UTC'), soit sous forme d'intervalle (par exemple, INTERVAL '-08:00') - voici une liste de tous les fuseaux horaires disponibles
  • timestamp peut être n'importe quelle valeur de type timestamp
  • now()renvoie une valeur de type horodatage (exactement ce dont nous avons besoin) avec le fuseau horaire par défaut de votre base de données attaché (par exemple 2018-11-11T12:07:22.3+05:00).
  • timezone('UTC', now())transforme notre heure actuelle (de type horodatage avec fuseau horaire ) en l'équivalent sans fuseau horaire dans UTC.
    Par exemple, SELECT timestamp with time zone '2020-03-16 15:00:00-05' AT TIME ZONE 'UTC'reviendra 2020-03-16T20:00:00Z.

Docs: fuseau horaire ()

Lakesare
la source
1
Merci d'avoir résumé ces choses. J'ai utilisé la version minuscule at timezone 'utc'et cela ne fonctionnait pas avec ma configuration PostgreSQL. Utilisation de lettres majuscules résolu problème
Geradlus_RU
2

La fonction existe déjà: timezone ('UTC' :: text, now ())

utilisateur10259440
la source