Les valeurs d'horodatage sont-elles stockées différemment dans PostgreSQL lorsque le type de données est WITH TIME ZONE
versus WITHOUT TIME ZONE
? Les différences peuvent-elles être illustrées par des cas de test simples?
Réponses:
Les différences sont couvertes dans la documentation PostgreSQL pour les types date / heure . Oui, le traitement de TIME
ou TIMESTAMP
diffère entre l'un WITH TIME ZONE
ou l'autre WITHOUT TIME ZONE
. Cela n'affecte pas la façon dont les valeurs sont stockées; cela affecte la manière dont ils sont interprétés.
Les effets des fuseaux horaires sur ces types de données sont traités spécifiquement dans la documentation. La différence provient de ce que le système peut raisonnablement savoir sur la valeur:
Avec un fuseau horaire dans le cadre de la valeur, la valeur peut être rendue comme une heure locale dans le client.
Sans un fuseau horaire dans le cadre de la valeur, le fuseau horaire par défaut évident est UTC, il est donc rendu pour ce fuseau horaire.
Le comportement diffère en fonction d'au moins trois facteurs:
WITH TIME ZONE
-à- dire ou WITHOUT TIME ZONE
) de la valeur.Voici des exemples couvrant les combinaisons de ces facteurs:
foo=> SET TIMEZONE TO 'Japan';
SET
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 00:00:00+09
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 06:00:00+09
(1 row)
foo=> SET TIMEZONE TO 'Australia/Melbourne';
SET
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 00:00:00+11
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 08:00:00+11
(1 row)
timestamp with time zone
et timestamp without time zone
, dans Postgres, ne stockent pas réellement les informations de fuseau horaire. Vous pouvez le confirmer en jetant un coup d'œil à la page de documentation du type de données: les deux types occupent le même nombre d'octets et ont la plage de valeurs de sauvegarde, donc pas de place pour stocker les informations de fuseau horaire. Le texte de la page le confirme. Quelque chose d'un abus de langage: "sans tz" signifie "ignorer le décalage lors de l'insertion de données" et "avec tz" signifie "utiliser le décalage pour s'ajuster à UTC".
J'essaie de l'expliquer de manière plus compréhensible que la documentation PostgreSQL référencée.
Aucune des deux TIMESTAMP
variantes ne stocke un fuseau horaire (ou un décalage), malgré ce que les noms suggèrent. La différence réside dans l'interprétation des données stockées (et dans l'application prévue), et non dans le format de stockage lui-même:
TIMESTAMP WITHOUT TIME ZONE
stocke la date-heure locale (aka. date du calendrier mural et heure de l'horloge murale). Son fuseau horaire n'est pas spécifié pour autant que PostgreSQL puisse le dire (bien que votre application sache ce que c'est). Par conséquent, PostgreSQL n'effectue aucune conversion liée au fuseau horaire en entrée ou en sortie. Si la valeur a été entrée dans la base de données en tant que '2011-07-01 06:30:30'
, alors peu importe dans quel fuseau horaire vous l'affichez plus tard, elle indiquera toujours année 2011, mois 07, jour 01, 06 heures, 30 minutes et 30 secondes (dans un certain format). De plus, tout décalage ou fuseau horaire que vous spécifiez dans l'entrée est ignoré par PostgreSQL, donc '2011-07-01 06:30:30+00'
et '2011-07-01 06:30:30+05'
sont les mêmes que just '2011-07-01 06:30:30'
. Pour les développeurs Java: c'est analogue à java.time.LocalDateTime
.
TIMESTAMP WITH TIME ZONE
stocke un point sur la ligne horaire UTC. Son aspect (combien d'heures, de minutes, etc.) dépend de votre fuseau horaire, mais il se réfère toujours au même instant «physique» (comme le moment d'un événement physique réel). L'entrée est convertie en interne en UTC, et c'est ainsi qu'elle est stockée. Pour cela, le décalage de l'entrée doit être connu, donc lorsque l'entrée ne contient pas de décalage ou de fuseau horaire explicite (comme '2011-07-01 06:30:30'
), il est supposé être dans le fuseau horaire actuel de la session PostgreSQL, sinon le décalage ou le fuseau horaire explicitement spécifié est utilisé (comme dans '2011-07-01 06:30:30+05'
). La sortie est affichée convertie dans le fuseau horaire actuel de la session PostgreSQL. Pour les développeurs Java: c'est analogue à java.time.Instant
(avec une résolution inférieure cependant), mais avec JDBC et JPA 2.2, vous êtes censé le mapper à java.time.OffsetDateTime
(ou à java.util.Date
oujava.sql.Timestamp
bien sûr).
Certains disent que les deux TIMESTAMP
variantes stockent la date et l'heure UTC. En quelque sorte, mais à mon avis, il est déroutant de le dire ainsi. TIMESTAMP WITHOUT TIME ZONE
est stocké comme un TIMESTAMP WITH TIME ZONE
, qui, rendu avec le fuseau horaire UTC, donne la même année, mois, jour, heures, minutes, secondes et microsecondes que dans la date-heure locale. Mais ce n'est pas censé représenter le point sur la ligne de temps que dit l'interprétation UTC, c'est juste la façon dont les champs de date-heure locaux sont codés. (C'est un groupe de points sur la ligne du temps, car le fuseau horaire réel n'est pas UTC; nous ne savons pas ce que c'est.)
TIMESTAMP WITH TIME ZONE
tant que fichier Instant
. Les deux représentent un point sur la chronologie en UTC. Instant
est préférable, à mon avis, par rapport à OffsetDateTime
car il est plus auto-documenté: A TIMESTAMP WITH TIME ZONE
est toujours récupéré de la base de données en tant que UTC, et an Instant
est toujours en UTC, donc une correspondance naturelle, tandis que an OffsetDateTime
peut transporter d'autres décalages.
OffsetDateTime
que le type Java mappé. Je ne sais pas si Instance
est toujours pris en charge officieusement quelque part.
'2011-07-01 06:30:30+00'
et '2011-07-01 06:30:30+05'
est ignoré mais je suis capable de le faire insert into test_table (date) values ('2018-03-24T00:00:00-05:00'::timestamptz);
et il le convertira correctement en utc. où la date est l'horodatage sans fuseau horaire. J'essaie de comprendre quelle est la valeur principale de l'horodatage avec le fuseau horaire et j'ai des problèmes.
::timestamptz
. Avec cela, vous convertissez la chaîne en TIMESTAMP WITH TIME ZONE
, et quand elle sera ensuite convertie en WITHOUT TIME ZONE
, cela stockera le jour du "calendrier mural" et l'heure de l'horloge murale de cet instant comme vu depuis le fuseau horaire de votre session (qui est peut-être UTC). Il ne s'agira toujours que d'un horodatage local avec un décalage non spécifié (pas de zone).
Voici un exemple qui devrait vous aider. Si vous avez un horodatage avec un fuseau horaire, vous pouvez convertir cet horodatage dans n'importe quel autre fuseau horaire. Si vous n'avez pas de fuseau horaire de base, il ne sera pas converti correctement.
SELECT now(),
now()::timestamp,
now() AT TIME ZONE 'CST',
now()::timestamp AT TIME ZONE 'CST'
Production:
-[ RECORD 1 ]---------------------------
now | 2018-09-15 17:01:36.399357+03
now | 2018-09-15 17:01:36.399357
timezone | 2018-09-15 08:01:36.399357
timezone | 2018-09-16 02:01:36.399357+03
timestamp
que vous timestamptz
voulez dire. timestamptz
signifie un point absolu dans le temps (UTC) alors que timestamp
indique ce que l'horloge a montré dans un certain fuseau horaire. Ainsi, lors de la conversion timestamptz
vers un fuseau horaire, vous vous demandez ce qu'indiquait l'horloge à New York à ce moment absolu? alors que lors de la "conversion" de a timestamp
, vous vous demandez quel était le moment absolu où l'horloge de New York a montré x?
AT TIME ZONE
construction est un casse-tête en soi, même si vous comprenez déjà les types WITH
vs. WITHOUT TIME ZONE
C'est donc un choix curieux pour les expliquer. (: ( AT TIME ZONE
convertit un WITH TIME ZONE
horodatage en WITHOUT TIME ZONE
horodatage, et vice versa ... pas exactement évident.)
now()::timestamp AT TIME ZONE 'CST'
n'a pas de sens, sauf si vous à quel moment une horloge pour la zone 'CST' afficherait l'heure que votre horloge locale affiche actuellement