关于数据库:generate_series超过夏令时更改 – 根据服务器时区的不同结果

generate_series over daylight savings change - varied results depending on server timezone

当我的postgres服务器位于America / New_York时区,或者我使用SET SESSION TIME ZONE 'America/New_York'时,generate_series会尊重夏令时的变化,我可以获得我想要的正确的纪元或时间点:

1
2
3
4
5
6
7
8
9
10
11
12
13
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不会考虑夏令时的变化:

1
2
3
4
5
6
7
8
9
10
11
12
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)

第4/4和第11/5号通知尚未针对DST更改进行调整。

在我查询时没有设置会话时区有什么方法吗?

使用postgres 9.6 ...


会话时区控制如何解释1天的间隔,所以我认为你的问题的答案很简单,没有。

Postgres文档解释如下:

When adding an interval value to (or subtracting an interval value from) a timestamp with time zone value, the days component advances or decrements the date of the timestamp with time zone by the indicated number of days. Across daylight saving time changes (when the session time zone is set to a time zone that recognizes DST), this means interval '1 day' does not necessarily equal interval '24 hours'. For example, with the session time zone set to CST7CDT, timestamp with time zone '2005-04-02 12:00-07' + interval '1 day' will produce timestamp with time zone '2005-04-03 12:00-06', while adding interval '24 hours' to the same initial timestamp with time zone produces timestamp with time zone '2005-04-03 13:00-06', as there is a change in daylight saving time at 2005-04-03 02:00 in time zone CST7CDT.

据我所知,除了将其设置为会话时区之外,没有其他机制可以告诉如何在特定时区内解释间隔。 换句话说,我认为你正在寻找类似'1 day tz America/New_York'::interval的东西,我不相信这样的语法存在。


您可以在连接特定用户(ALTER USER)或特定数据库(ALTER DATABASE)时自动设置时区。

我能想到的另一种方法是使用一个函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;

生产:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    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)