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

PostgreSQL mal converti de l'horodatage sans fuseau horaire à l'horodatage avec fuseau horaire

Éléments clés à comprendre

timestamp without time zone AT TIME ZONE ré-interprète un timestamp comme étant dans ce fuseau horaire dans le but de le convertir en UTC .

timestamp with time zone AT TIME ZONE convertit un timestamptz dans un timestamp dans le fuseau horaire spécifié.

PostgreSQL utilise les fuseaux horaires ISO-8601, qui spécifient que l'est de Greenwich est positif ... sauf si vous utilisez un spécificateur de fuseau horaire POSIX, auquel cas il suit POSIX. La folie s'ensuit.

Pourquoi le premier produit un résultat inattendu

Les horodatages et les fuseaux horaires en SQL sont horribles. Ceci :

select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'EST5EDT';

interprète le littéral de type inconnu '2011-12-30 00:30:00' comme timestamp without time zone , que Pg suppose être dans le fuseau horaire local, sauf indication contraire. Lorsque vous utilisez AT TIME ZONE , il est (selon la spécification) réinterprété sous la forme d'un timestamp with time zone dans le fuseau horaire EST5EDT puis stocké en tant qu'heure absolue en UTC - il est donc converti de EST5EDT à UTC, c'est-à-dire que le décalage horaire est soustrait . x - (-5) est x + 5 .

Cet horodatage, ajusté au stockage UTC, est ensuite ajusté pour votre serveur TimeZone paramètre d'affichage pour qu'il soit affiché à l'heure locale.

Si vous souhaitez plutôt dire "J'ai cet horodatage en heure UTC et je souhaite voir quelle est l'heure locale équivalente dans EST5EDT", si vous voulez être indépendant du paramètre TimeZone du serveur, vous devez écrire quelque chose comme :

select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
       AT TIME ZONE 'EST5EDT';

Cela dit "Étant donné l'horodatage 2011-12-30 00:30:00, traitez-le comme un horodatage en UTC lors de la conversion en horodatage, puis convertissez cet horodatage en une heure locale dans EST5EDT".

Horrible, n'est-ce pas ? Je veux parler à une entreprise celui qui a décidé de la sémantique folle de AT TIME ZONE - cela devrait vraiment être quelque chose comme timestamp CONVERT FROM TIME ZONE '-5' et timestamptz CONVERT TO TIME ZONE '+5' . En outre, timestamp with time zone devrait en fait porter son fuseau horaire avec lui, ne pas être stocké en UTC et automatiquement converti en heure locale.

Pourquoi la seconde fonctionne (tant que TimeZone =UTC)

Votre version "fonctionnelle" d'origine :

select '2011-12-30 00:30:00' AT TIME ZONE 'EST5EDT';

ne sera correct que si TimeZone est défini sur UTC, car le cast text-to-timestamptz suppose TimeZone lorsqu'aucun n'est spécifié.

Pourquoi le troisième fonctionne

Deux problèmes s'annulent.

L'autre version qui semble fonctionner est indépendante du fuseau horaire, mais elle ne fonctionne que parce que deux problèmes s'annulent. Tout d'abord, comme expliqué ci-dessus, timestamp without time zone AT TIME ZONE ré-interprète l'horodatage comme étant dans ce fuseau horaire pour la conversion en un horodatage UTCtz ; cela soustrait efficacement le décalage horaire.

Cependant, pour des raisons que j'ignore, PostgreSQL utilise des horodatages avec le signe inverse de ce que j'ai l'habitude de voir dans la plupart des endroits. Voir la documentation :

Un autre problème à garder à l'esprit est que dans les noms de fuseaux horaires POSIX, des décalages positifs sont utilisés pour les emplacements à l'ouest de Greenwich. Partout ailleurs, PostgreSQL suit la convention ISO-8601 selon laquelle les décalages de fuseau horaire positifs sont à l'est de Greenwich.

Cela signifie que EST5EDT est identique à +5 , pas -5 . C'est pourquoi cela fonctionne :parce que vous soustrayez le décalage tz sans l'ajouter, mais vous soustrayez un décalage négatif !

Ce dont vous auriez besoin pour le faire correctement, c'est :

select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
       AT TIME ZONE '+5';