generate_series при переходе на летнее время - разные результаты в зависимости от часового пояса сервера

Когда мой сервер postgres находится в часовом поясе America / New_York или я использую SET SESSION TIME ZONE 'America/New_York', generate_series учитывает изменение летнего времени, и я могу получить правильную эпоху или момент времени, который я хочу:

postgres=# SET SESSION TIME ZONE 'America/New_York';
SET
postgres=#
postgres=# with seq(ts) as (select * from generate_series('2018-11-03 00:00:00.000 -04:00', '2018-11-06 13:40:39.067 -05:00', '1d'::interval))
postgres-# select ts, extract(epoch from ts) as epoch, ts at time zone 'America/New_York' as eastern
postgres-# from seq;
           ts           |   epoch    |       eastern
------------------------+------------+---------------------
 2018-11-03 00:00:00-04 | 1541217600 | 2018-11-03 00:00:00
 2018-11-04 00:00:00-04 | 1541304000 | 2018-11-04 00:00:00
 2018-11-05 00:00:00-05 | 1541394000 | 2018-11-05 00:00:00
 2018-11-06 00:00:00-05 | 1541480400 | 2018-11-06 00:00:00
(4 rows)

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

postgres=# SET SESSION TIME ZONE 'UTC';
SET
postgres=# with seq(ts) as (select * from generate_series('2018-11-03 00:00:00.000 -04:00', '2018-11-06 13:40:39.067 -05:00', '1d'::interval))
postgres-# select ts, extract(epoch from ts) as epoch, ts at time zone 'America/New_York' as eastern
postgres-# from seq;
           ts           |   epoch    |       eastern
------------------------+------------+---------------------
 2018-11-03 04:00:00+00 | 1541217600 | 2018-11-03 00:00:00
 2018-11-04 04:00:00+00 | 1541304000 | 2018-11-04 00:00:00
 2018-11-05 04:00:00+00 | 1541390400 | 2018-11-04 23:00:00
 2018-11-06 04:00:00+00 | 1541476800 | 2018-11-05 23:00:00
(4 rows)

Уведомления 11/4 и 11/5 не были скорректированы для изменения летнего времени.

Есть ли способ обойти это без установки часового пояса сеанса при запросе?

Использование postgres 9.6 ...


person Kevin Bouwkamp    schedule 12.11.2018    source источник


Ответы (2)


Часовой пояс сеанса - это то, что определяет, как интерпретируется интервал в 1 день, поэтому я думаю, что ответ на ваш вопрос - нет.

В документации Postgres это объясняется следующим образом:

При добавлении значения интервала (или вычитании значения интервала из) метки времени со значением часового пояса компонент дней увеличивает или уменьшает дату метки времени с часовым поясом на указанное количество дней. При переходе на летнее время (когда часовой пояс сеанса установлен на часовой пояс, который распознает DST), это означает, что interval '1 day' не обязательно равно interval '24 hours'. Например, если для часового пояса сеанса установлено значение CST7CDT, timestamp with time zone '2005-04-02 12:00-07' + interval '1 day' создаст timestamp with time zone '2005-04-03 12:00-06', а добавление interval '24 hours' к той же начальной метке времени с часовым поясом даст timestamp with time zone '2005-04-03 13:00-06', поскольку есть изменение летнего времени в 2005-04-03 02:00 в часовом поясе CST7CDT.

Насколько мне известно, нет другого механизма, как указать интервал для интерпретации в определенном часовом поясе, кроме установки этого часового пояса сеанса. Другими словами, я думаю, вы ищете что-то вроде '1 day tz America/New_York'::interval, и я не верю, что такой синтаксис существует.

person Matt Johnson-Pint    schedule 12.11.2018
comment
Да, вроде бы прикинул. В этом есть смысл, хотя и прискорбный. Спасибо - person Kevin Bouwkamp; 13.11.2018

Вы можете установить часовой пояс автоматически при подключении для определенных пользователей (ALTER USER) или для конкретной базы данных (ALTER DATABASE).

Единственный другой способ, о котором я могу думать, - это функция.

BEGIN;

CREATE FUNCTION f1() RETURNS SETOF timestamptz
AS $$
SELECT generate_series('2018-11-03 00:00:00-04', '2018-11-06 00:00:00-05', interval '1 day');
$$
LANGUAGE sql
SET timezone = 'America/New_York';                                                                                                               

SET timezone = 'UTC';
SELECT generate_series('2018-11-03 00:00:00-04', '2018-11-06 00:00:00-05', interval '1 day');
SELECT * FROM f1();

ROLLBACK;

Производит:

    generate_series
------------------------
 2018-11-03 04:00:00+00
 2018-11-04 04:00:00+00
 2018-11-05 04:00:00+00
 2018-11-06 04:00:00+00
(4 rows)

           f1
------------------------
 2018-11-03 04:00:00+00
 2018-11-04 04:00:00+00
 2018-11-05 05:00:00+00
 2018-11-06 05:00:00+00
(4 rows)
person Richard Huxton    schedule 13.11.2018