sql >> Base de Datos >  >> RDS >> PostgreSQL

PostgreSQL conversión incorrecta de marca de tiempo sin zona horaria a marca de tiempo con zona horaria

Cosas clave para entender

timestamp without time zone AT TIME ZONE reinterpreta una timestamp como estar en esa zona horaria con el propósito de convertirla a UTC .

timestamp with time zone AT TIME ZONE convierte un timestamptz en una timestamp en la zona horaria especificada.

PostgreSQL usa zonas horarias ISO-8601, que especifican que el este de Greenwich es positivo... a menos que use un especificador de zona horaria POSIX, en cuyo caso sigue a POSIX. Se produce la locura.

Por qué el primero produce un resultado inesperado

Las marcas de tiempo y las zonas horarias en SQL son horribles. esto:

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

interpreta el literal de tipo desconocido '2011-12-30 00:30:00' como timestamp without time zone , que Pg asume que está en la zona horaria local a menos que se indique lo contrario. Cuando usas AT TIME ZONE , es (según la especificación) reinterpretado como una timestamp with time zone en la zona horaria EST5EDT luego se almacena como un tiempo absoluto en UTC, por lo que se convierte desde EST5EDT a UTC, es decir, el desplazamiento de la zona horaria se resta . x - (-5) es x + 5 .

Esta marca de tiempo, ajustada al almacenamiento UTC, luego se ajusta para su servidor TimeZone configuración de visualización para que se muestre en la hora local.

Si, en cambio, desea decir "Tengo esta marca de tiempo en hora UTC y deseo ver cuál es la hora local equivalente en EST5EDT", si desea ser independiente de la configuración de la zona horaria del servidor, debe escribir algo como:

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

Esto dice "Dada la marca de tiempo 2011-12-30 00:30:00, trátela como una marca de tiempo en UTC al convertir a timestamptz, luego convierta esa timestamptz a una hora local en EST5EDT".

Horrible, ¿no? Quiero hablar con una empresa quien haya decidido la loca semántica de AT TIME ZONE - debería ser algo así como timestamp CONVERT FROM TIME ZONE '-5' y timestamptz CONVERT TO TIME ZONE '+5' . Además, timestamp with time zone en realidad debería llevar consigo su zona horaria, no almacenarse en UTC y convertirse automáticamente a la hora local.

Por qué funciona el segundo (siempre que TimeZone =UTC)

Su versión original "funciona":

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

solo será correcto si la zona horaria está configurada en UTC, porque la conversión de texto a marca de tiempo asume la zona horaria cuando no se especifica ninguna.

Por qué funciona el tercero

Dos problemas se anulan entre sí.

La otra versión que parece funcionar es independiente de TimeZone, pero solo funciona porque dos problemas se anulan. Primero, como se explicó anteriormente, timestamp without time zone AT TIME ZONE reinterpreta la marca de tiempo como si estuviera en esa zona horaria para la conversión a una marca de tiempo UTCtz; esto efectivamente resta el desplazamiento de la zona horaria.

Sin embargo, por razones que están más allá de mi conocimiento, PostgreSQL usa marcas de tiempo con el signo inverso a lo que estoy acostumbrado a ver en la mayoría de los lugares. Consulte la documentación:

Otro problema a tener en cuenta es que en los nombres de zona horaria POSIX, se utilizan compensaciones positivas para las ubicaciones al oeste de Greenwich. En cualquier otro lugar, PostgreSQL sigue la convención ISO-8601 de que las compensaciones de zona horaria positivas están al este de Greenwich.

Esto significa que EST5EDT es lo mismo que +5 , no -5 . Es por eso que funciona:porque está restando el desplazamiento tz sin sumarlo, ¡pero está restando un desplazamiento negado!

En su lugar, lo que necesitarías para corregirlo es:

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