PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Conversion entre fuseaux horaires dans Postgres

Laissez-moi vous expliquer les deux exemples :

Dans les deux, nous supposons un fuseau horaire UTC (c'est-à-dire SET timezone TO UTC ).

db=# SELECT timezone('US/Pacific', '2016-01-01 00:00');
      timezone
---------------------
 2015-12-31 16:00:00
(1 row)

Cela équivaut à SELECT timezone('US/Pacific', '2016-01-01 00:00'::timestamptz) , c'est-à-dire que Postgres a implicitement converti la chaîne en un timestamptz .

Nous savons que le timezone la fonction convertit dans les deux sens entre timestamp et timestamptz :

Puisque nous lui donnons un timestamptz en entrée, il affichera un timestamp . En d'autres termes, il convertit le point absolu dans le temps 2016-01-01 00:00Z à une heure du mur en US/Pacific , c'est-à-dire ce que l'horloge de Los Angeles indiquait à ce moment précis.

Dans l'exemple 2 nous faisons l'inverse, à savoir prendre un timestamp et le convertir en un timestamptz . En d'autres termes, nous demandons :quel était le moment absolu où l'horloge de Los Angeles indiquait 2016-01-01 00:00 ?

Vous mentionnez :

'2016-01-01 00:00'::timestamp est un timestamp , c'est-à-dire un temps de mur. Il n'a pas de notion de fuseau horaire.

Je pense que vous n'avez peut-être pas bien compris la différence entre timestamp et timestamptz , ce qui est essentiel ici. Considérez-les simplement comme l'heure du mur , c'est-à-dire l'heure affichée quelque part dans le monde sur une horloge accrochée au mur, et l'heure absolue , c'est-à-dire le temps absolu dans notre univers.

Les exemples que vous donnez dans votre propre réponse ne sont pas tout à fait exacts.

SELECT ts FROM  (VALUES
(timestamptz '2012-03-05 17:00:00+0') -- outputs 2012-03-05 17:00:00+00 --1
,(timestamptz '2012-03-05 18:00:00+1') -- outputs 2012-03-05 17:00:00+00 --2
,(timestamp   '2012-03-05 18:00:00+1') -- outputs 2012-03-05 18:00:00+00 --3
,(timestamp   '2012-03-05 11:00:00'  AT TIME ZONE '+6') -- outputs 2012-03-05 17:00:00+00 --4
,(timestamp   '2012-03-05 17:00:00'  AT TIME ZONE 'UTC') -- outputs 2012-03-05 17:00:00+00 --5
,(timestamp   '2012-03-05 17:00:00'::timestamp) -- outputs 2012-03-05 17:00:00+00 --6
,(timestamp   '2012-03-05 17:00:00'::timestamptz) -- outputs 2012-03-05 17:00:00+00 --7
    ) t(ts);

Le problème avec votre exemple est que vous construisez un ensemble de données avec une seule colonne. Puisqu'une colonne ne peut avoir qu'un seul type, chaque ligne (ou valeur unique dans ce cas) est convertie dans le même type, à savoir timestamptz , même si certaines valeurs ont été calculées en tant que timestamp (par exemple valeur 3). Ainsi, vous avez ici une conversion implicite supplémentaire.

Divisons l'exemple en requêtes distinctes et voyons ce qui se passe :

Exemple 1

db=# SELECT timestamptz '2012-03-05 17:00:00+0';
      timestamptz
------------------------
 2012-03-05 17:00:00+00

Comme vous le savez peut-être déjà, timestamptz '2012-03-05 17:00:00+0' et '2012-03-05 17:00:00+0'::timestamptz sont équivalents (je préfère ce dernier). Ainsi, juste pour utiliser la même syntaxe que dans l'article, je vais réécrire :

db=# SELECT '2012-03-05 17:00:00+0'::timestamptz;
      timestamptz
------------------------
 2012-03-05 17:00:00+00

Maintenant, que se passe-t-il ici ? Eh bien, moins que dans votre explication originale. La chaîne est simplement analysée comme un timestamptz . Lorsque le résultat est imprimé, il utilise le timezone actuellement défini config pour le reconvertir en une représentation lisible par l'homme de la structure de données sous-jacente, c'est-à-dire 2012-03-05 17:00:00+00 .

Changeons le timezone config et voyez ce qui se passe :

db=# SET timezone TO 'Europe/Berlin';
SET
db=# SELECT '2012-03-05 17:00:00+0'::timestamptz;
      timestamptz
------------------------
 2012-03-05 18:00:00+01

La seule chose qui a changé est comment le timestamptz est imprimé à l'écran, à savoir en utilisant le Europe/Berlin fuseau horaire.

Exemple 2

db=# SELECT timestamptz '2012-03-05 18:00:00+1';
      timestamptz
------------------------
 2012-03-05 17:00:00+00
(1 row)

Encore une fois, il suffit d'analyser la date.

Exemple 3

db=# SELECT timestamp '2012-03-05 18:00:00+1';
      timestamp
---------------------
 2012-03-05 18:00:00
(1 row)

C'est la même chose que '2012-03-05 18:00:00+1'::timestamp . Ce qui se passe ici, c'est que le décalage du fuseau horaire est simplement ignoré car vous demandez un timestamp .

Exemple 4

db=# SELECT timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6';
        timezone
------------------------
 2012-03-05 17:00:00+00
(1 row)

Réécrivons pour être plus simple :

db=# SELECT timezone('+6', '2012-03-05 11:00:00'::timestamp);
        timezone
------------------------
 2012-03-05 17:00:00+00
(1 row)

C'est demander :quelle était l'heure absolue lorsque l'horloge murale dans le fuseau horaire avec un décalage de +6 heures affichait 2012-03-05 11:00:00 ?

Exemple 5

db=# SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC';
        timezone
------------------------
 2012-03-05 17:00:00+00
(1 row)

Réécrivons :

db=# SELECT timezone('UTC', '2012-03-05 17:00:00'::timestamp);
        timezone
------------------------
 2012-03-05 17:00:00+00
(1 row)

Cela demande :quelle était l'heure absolue lorsque l'horloge murale dans le fuseau horaire UTC affichait 2012-03-05 17:00:00 ?

Exemple 6

db=# SELECT timestamp '2012-03-05 17:00:00'::timestamp;
      timestamp
---------------------
 2012-03-05 17:00:00
(1 row)

Ici, vous diffusez deux fois vers timestamp , ce qui ne fait aucune différence. Simplifions :

db=# SELECT '2012-03-05 17:00:00'::timestamp;
      timestamp
---------------------
 2012-03-05 17:00:00
(1 row)

C'est clair je pense.

Exemple 7

db=# SELECT timestamp '2012-03-05 17:00:00'::timestamptz;
      timestamptz
------------------------
 2012-03-05 17:00:00+00
(1 row)

Réécrivons :

db=# SELECT ('2012-03-05 17:00:00'::timestamp)::timestamptz;
      timestamptz
------------------------
 2012-03-05 17:00:00+00
(1 row)

Vous analysez d'abord la chaîne en tant que timestamp puis en le convertissant en un timestamptz en utilisant le timezone actuellement défini . Si nous changeons le timezone , nous obtenons autre chose car Postgres suppose que ce fuseau horaire lors de la conversion d'un timestamp (ou une chaîne sans informations sur le fuseau horaire) à timestamptz :

db=# SET timezone TO 'Europe/Berlin';
SET
db=# SELECT ('2012-03-05 17:00:00'::timestamp)::timestamptz;
      timestamptz
------------------------
 2012-03-05 17:00:00+01
(1 row)

Cette heure absolue, exprimée en UTC, est 2012-03-05 16:00:00+00 , donc différent de l'exemple original.

J'espère que cela clarifie les choses. Encore une fois, comprendre la différence entre timestamp et timestamptz C est la clé. Pensez au temps du mur par rapport au temps absolu.