"AT TIME ZONE" con el error de PostgreSQL de nombre de zona?

12

Estaba respondiendo a esta pregunta de stackoverflow y encontré un resultado extraño:

 select * from  pg_timezone_names where name = 'Europe/Berlin' ;
     name      | abbrev | utc_offset | is_dst 
---------------+--------+------------+--------
 Europe/Berlin | CET    | 01:00:00   | f

y siguiente consulta

select id, 
  timestampwithtimezone, 
  timestampwithtimezone at time zone 'Europe/Berlin' as berlin, 
  timestampwithtimezone at time zone 'CET' as cet 
from data ;
 id  | timestampwithtimezone  |       berlin        |         cet         
 -----+------------------------+---------------------+---------------------
 205 | 2012-10-28 01:30:00+02 | 2012-10-28 01:30:00 | 2012-10-28 00:30:00
 204 | 2012-10-28 02:00:00+02 | 2012-10-28 02:00:00 | 2012-10-28 01:00:00
 203 | 2012-10-28 02:30:00+02 | 2012-10-28 02:30:00 | 2012-10-28 01:30:00
 202 | 2012-10-28 02:59:59+02 | 2012-10-28 02:59:59 | 2012-10-28 01:59:59
 106 | 2012-10-28 02:00:00+01 | 2012-10-28 02:00:00 | 2012-10-28 02:00:00

Estoy usando PostgreSQL 9.1.2 y ubuntu 12.04.
Acabo de comprobar que en 8.2.11 el resultado es el mismo.

Según la documentación , no importa si uso el nombre o la abreviatura.

¿Es esto un error?
¿Estoy haciendo algo mal?
¿Alguien puede explicar este resultado?

EDITAR Para el comentario de que CET no es Europa / Berlín.

Solo estoy seleccionando valores de pg_timezone_names.

select * from  pg_timezone_names  where abbrev ='CEST';
 name | abbrev | utc_offset | is_dst 
------+--------+------------+--------

y

select * from  pg_timezone_names  where abbrev ='CET';
        name         | abbrev | utc_offset | is_dst 
---------------------+--------+------------+--------
 Africa/Tunis        | CET    | 01:00:00   | f
 Africa/Algiers      | CET    | 01:00:00   | f
 Africa/Ceuta        | CET    | 01:00:00   | f
 CET                 | CET    | 01:00:00   | f
 Atlantic/Jan_Mayen  | CET    | 01:00:00   | f
 Arctic/Longyearbyen | CET    | 01:00:00   | f
 Poland              | CET    | 01:00:00   | f
 .....

Durante el invierno Europa / Berlín es +01. Durante el verano es +02.

EDITAR2 En el 28/10/2012, la zona horaria ha cambiado del horario de verano al horario de invierno a las 2:00.
Estos dos registros tienen el mismo valor en Europa / Berlín:

204 | 2012-10-28 02:00:00+02 | 2012-10-28 02:00:00 | 2012-10-28 01:00:00
106 | 2012-10-28 02:00:00+01 | 2012-10-28 02:00:00 | 2012-10-28 02:00:00

Esto sugiere que si uso una de las abreviaturas (CET o CEST) para el rango de datos grandes (horario de verano e invierno), el resultado será incorrecto para algunos de los registros. Será bueno si uso 'Europa / Berlín'.

Cambié la hora del sistema a '2012-01-17' y pg_timezone_names también ha cambiado.

select * from  pg_timezone_names  where name ='Europe/Berlin';
     name      | abbrev | utc_offset | is_dst 
---------------+--------+------------+--------
 Europe/Berlin | CEST   | 02:00:00   | t
sufleR
fuente
1
Es bastante seguro que 2012-10-28 01:30:00es CEST, no CET.
dezso
1
Por lo que sé, no loCET es , al menos no durante el horario de verano. Europe/Berlin
a_horse_with_no_name

Respuestas:

9

En realidad, la documentación dice claramente que el nombre de la zona horaria y la abreviatura se comportarán de manera diferente.

En resumen, esta es la diferencia entre las abreviaturas y los nombres completos: las abreviaturas siempre representan un desplazamiento fijo de UTC, mientras que la mayoría de los nombres completos implican una regla local de horario de verano y, por lo tanto, tienen dos posibles compensaciones de UTC. Referencia

FWIW, esa misma referencia también dice

No recomendamos utilizar el tipo de hora con zona horaria (aunque es compatible con PostgreSQL para aplicaciones heredadas y para el cumplimiento del estándar SQL).

Mike Sherrill 'Retiro del gato'
fuente
6

¡Y eso todavía no es lo esencial! Me encontré con un problema muy similar hace algún tiempo.

Los principales inconvenientes de las abreviaturas de zona horaria ya se han presentado aquí: no tienen en cuenta el horario de verano (DST). El principal profesional: simplicidad que resulta en un rendimiento superior . Tener en cuenta las reglas de DST hace que los nombres de zona horaria sean lentos en comparación. Las abreviaturas de zona horaria son compensaciones de tiempo simples y simbólicas, los nombres de zonas horarias están sujetos a un conjunto de reglas en constante cambio. Ejecuté puntos de referencia en esta respuesta relacionada en SO , la diferencia es notable. Pero cuando se aplica a un conjunto, generalmente es necesario usar nombres de zona horaria para cubrir el posible estado de horario de verano diferente por fila (y también diferencias históricas).

Estamos hablando de CET . La parte realmente difícil es que "CET" no sólo es (obviamente) una abreviación del huso horario , es también un nombre de zona horaria , por lo menos según mi instalación (PostgreSQL 9.1.6 en Debian Squeeze con locale "de_AT.UTF-8 ") y todos los demás que he visto hasta ahora. Menciono estos detalles, porque Postgres usa la información local del sistema operativo subyacente si está disponible.

Ver por ti mismo:

SELECT * FROM pg_timezone_names WHERE name = 'CET';

SELECT * FROM pg_timezone_abbrevs WHERE abbrev = 'CET';

SQL Fiddle.

Postgres elige la abreviatura sobre el nombre completo. Entonces, aunque encontré CET en los nombres de zona horaria , la expresión '2012-01-18 01:00 CET'::timestamptzse interpreta de acuerdo con las reglas sutilmente diferentes para las abreviaturas de zona horaria .

Si esa no es una pistola cargada, no sé qué es.

Para evitar ambigüedades, vaya con el nombre de zona horaria 'Europa / Berlín' (o 'Europa / Viena' en mi caso, que es efectivamente el mismo, excepto por las diferencias históricas). Encuentre más detalles sobre el tema en la pregunta estrechamente relacionada que mencioné anteriormente .

Para terminar, me gustaría expresar mi profundo desprecio por el concepto tonto de DST. Debería eliminarse de la existencia y nunca más volver a hablarse de él.

Erwin Brandstetter
fuente
3

Mira esto:

select  
    '2012-10-28 02:30:00+02'::timestamp with time zone at time zone 'Europe/Berlin' as berlin,
    '2012-10-28 02:30:00+02'::timestamp with time zone at time zone 'CET' as cet,
    '2012-10-28 02:30:00+02'::timestamp with time zone at time zone 'CEST' as cest

+02 es CEST en Berlín, no CET.

dezso
fuente