Имена часовых поясов с одинаковыми свойствами дают другой результат при применении к метке времени

Я только что провел час в отчаянии из-за расхождений в этих результатах этих двух выражений:


db=# SELECT '2012-01-18 1:0 CET'::timestamptz AT TIME ZONE 'UTC'
           ,'2012-01-18 1:0 Europe/Vienna'::timestamptz AT TIME ZONE 'UTC';
      timezone       |      timezone
---------------------+---------------------
 2012-08-18 00:00:00 | 2012-08-17 23:00:00

Очевидно, что второе выражение вычитает два часа в соответствии с правилами DST, где первое использует только стандартное смещение.

Я проверил каталоги для этих двух названий часовых поясов. Они оба там и выглядят одинаково:

db=# SELECT * FROM pg_timezone_names WHERE name IN ('CET', 'Europe/Vienna');
     name      | abbrev | utc_offset | is_dst
---------------+--------+------------+--------
 Europe/Vienna | CEST   | 02:00:00   | t
 CET           | CEST   | 02:00:00   | t

Я обратился к руководству PostgreSQL по поводу часовых поясов:

PostgreSQL позволяет указывать часовые пояса в трех разных формах:

Полное имя часового пояса, например, America/New_York. Распознанные имена часовых поясов перечислены в представлении pg_timezone_names (см. Раздел 45.67). PostgreSQL использует для этой цели широко используемые данные о часовых поясах zoneinfo, поэтому такие же имена распознаются и многими другими программами.

Сокращение часового пояса, например PST. Такая спецификация просто определяет конкретное смещение от UTC, в отличие от полных имен часовых поясов, которые также могут подразумевать набор правил перехода на летнее время. Распознанные сокращения перечислены в представлении pg_timezone_abbrevs (см. Раздел 45.66). Вы не можете установить параметры конфигурации timezone или log_timezone на сокращение часового пояса, но вы можете использовать сокращения во входных значениях даты / времени и с оператором AT TIME ZONE.

Жирный Акцент мой.

Так почему разница?

Моя настройка (более подробная информация добавлена)

  • PostgreSQL 9.1.4 на Debian Squeeze (стандартные squeeze-backports с http://backports.debian.org/debian-backports)

  • Местный timezone настройка по умолчанию на язык системы de_AT.UTF-8, но не должно иметь значения для примера.

SELECT version();
                                                version
-------------------------------------------------------------------------------------------------------
 PostgreSQL 9.1.4 on x86_64-unknown-linux-gnu, compiled by gcc-4.4.real (Debian 4.4.5-8) 4.4.5, 64-bit

SHOW timezone_abbreviations;

 timezone_abbreviations
------------------------
 Default

.. который (я предполагаю) загружает сокращения из этого файла: /usr/share/postgresql/9.1/timezonesets/Default

Я в недоумении, где название часового пояса CET происходит от. Но, очевидно, это есть в моих установках. Быстрый тест на sqlfiddle показывает тот же результат.

Я тестировал на двух разных серверах с похожей настройкой. Также с PostgreSQL 8.4. Найдено CET в качестве имени часового пояса в pg_timezone_names во всех них.

2 ответа

Решение

Сразу после публикации я запустил еще один запрос, чтобы проверить наличие подозрений:

SELECT * FROM pg_timezone_abbrevs
WHERE  abbrev IN ('CEST', 'CET');

 abbrev | utc_offset | is_dst
--------+------------+--------
 CEST   | 02:00:00   | t
 CET    | 01:00:00   | f

Как оказалось, есть также сокращение часового пояса с именем CET (что имеет смысл, "CET" является аббревиатурой). И похоже, что PostgreSQL выбирает аббревиатуру вместо полного имени. Итак, хотя я нашел CET в именах часовых поясов выражение "2012-01-18 1:0 CET"::timestamptz интерпретируется в соответствии с немного различающимися правилами для сокращений часовых поясов.

SELECT '2012-01-18 1:0 CEST'::timestamptz(0)
      ,'2012-01-18 1:0 CET'::timestamptz(0)
      ,'2012-01-18 1:0 Europe/Vienna'::timestamptz(0);

      timestamptz       |      timestamptz       |      timestamptz
------------------------+------------------------+------------------------
 2012-01-18 00:00:00+01 | 2012-01-18 01:00:00+01 | 2012-01-18 01:00:00+01


SELECT '2012-08-18 1:0 CEST'::timestamptz(0)
      ,'2012-08-18 1:0 CET'::timestamptz(0)
      ,'2012-08-18 1:0 Europe/Vienna'::timestamptz(0);

      timestamptz       |      timestamptz       |      timestamptz
------------------------+------------------------+------------------------
 2012-08-18 01:00:00+02 | 2012-08-18 02:00:00+02 | 2012-08-18 01:00:00+02

Я нахожу 10 случаев аббревиатур часовых поясов в названиях часовых поясов и не понимаю, почему они существуют. Какова цель?

Среди них смещение времени (utc_offset) не согласен в четырех случаях из-за настройки DST:

SELECT n.*, a.*
FROM   pg_timezone_names n 
JOIN   pg_timezone_abbrevs a ON  a.abbrev = n.name
WHERE  n.utc_offset <> a.utc_offset;

 name | abbrev | utc_offset | is_dst | abbrev | utc_offset | is_dst
------+--------+------------+--------+--------+------------+--------
 CET  | CEST   | 02:00:00   | t      | CET    | 01:00:00   | f
 EET  | EEST   | 03:00:00   | t      | EET    | 02:00:00   | f
 MET  | MEST   | 02:00:00   | t      | MET    | 01:00:00   | f
 WET  | WEST   | 01:00:00   | t      | WET    | 00:00:00   | f

В этих случаях люди могут быть одурачены (как и я), ища имя tz и находя временное смещение, которое фактически не применяется. Это неудачный дизайн - если не ошибка, то, по крайней мере, ошибка документации.

Я ничего не могу найти в руководстве о том, как разрешаются неоднозначности между названиями часовых поясов и сокращениями. Очевидно, что сокращения имеют приоритет.

Приложение Б.1. В интерпретации ввода даты / времени упоминается поиск сокращений часовых поясов, но остается неясным, как идентифицируются имена часовых поясов и какое из них имеет приоритет в случае неоднозначного токена.

Если токен является текстовой строкой, сопоставьте возможные строки:

Выполните поиск в таблице двоичного поиска для токена как сокращения часового пояса.

Что ж, в этом предложении есть небольшой намек на то, что аббревиатуры стоят на первом месте, но ничего определенного. Также есть колонка abbrev в обеих таблицах pg_timezone_names а также pg_timezone_abbrevs...

Причина того, что сокращения часовых поясов не включают правила перехода на летнее время (DST), заключается в том, что они имеют тенденцию подразумевать состояние. Здесь, на Среднем Западе США, мы используем CST (центральное стандартное время) в зимние месяцы и CDT (центральное летнее время) в остальное время года. Есть аномальные области, которые не используют DST, поэтому это становится сложным.

PostgreSQL не поддерживает свои собственные данные часового пояса, хотя он упаковывает последние данные часового пояса Олсона в каждом выпуске для тех операционных систем, которые их не предоставляют. Как правило, PostgreSQL будет использовать информацию о часовом поясе от ОС, поэтому, если у вас возникли проблемы, убедитесь, что у вас установлена ​​последняя версия.

Для справки, в моей системе сегодня я получил эти результаты:

test = # SELECT '2012-01-18 1: 0 CET':: timestamptz В ВРЕМЕННОЙ ЗОНЕ 'UTC'
test- #, '2012-01-18 1: 0 Европа / Вена':: timestamptz AT TIME ZONE 'UTC';
      часовой пояс | часовой пояс       
---------------------+---------------------
 2012-01-18 00:00:00 | 2012-01-18 00:00:00
(1 ряд)

test = # SELECT * FROM pg_timezone_names WHERE name IN ('CET', 'Europe / Vienna');
     имя | сокращенно | utc_offset | is_dst 
---------------+--------+------------+--------
 CET           | CEST   | 02:00:00   | T
 Европа / Вена | CEST | 02:00:00 | T
(2 ряда)
test = # SELECT * FROM pg_timezone_abbrevs
test- # WHERE abbrev IN ('CEST', 'CET');
 сокращенно | utc_offset | is_dst 
--------+------------+--------
 CEST   | 02:00:00   | T
 CET | 01:00:00 | е
(2 ряда)

test = # SELECT '2012-01-18 1: 0 CEST':: timestamptz (0)
test- #, '2012-01-18 1: 0 CET':: timestamptz (0)
test- #, '2012-01-18 1: 0 Европа / Вена'::timestamptz(0);
      отметка времени | отметка времени |      timestamptz       
------------------------+------------------------+------------------------
 2012-01-17 17:00:00-06 | 2012-01-17 18:00:00-06 | 2012-01-17 18:00:00-06
(1 ряд)

test = # SELECT '2012-08-18 1: 0 CEST':: timestamptz (0)
test- #, '2012-08-18 1: 0 CET':: timestamptz (0)
test- #, '2012-08-18 1: 0 Европа / Вена'::timestamptz(0);
      отметка времени | отметка времени |      timestamptz       
------------------------+------------------------+------------------------
 2012-08-17 18:00:00-05 | 2012-08-17 19:00:00-05 | 2012-08-17 18:00:00-05
(1 ряд)
Другие вопросы по тегам