夏令时更改的 generate_series - 不同的结果取决于服务器时区

Posted

技术标签:

【中文标题】夏令时更改的 generate_series - 不同的结果取决于服务器时区【英文标题】:generate_series over daylight savings change - varied results depending on server timezone 【发布时间】:2019-04-15 15:51:39 【问题描述】:

当我的 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 未针对 DST 更改进行调整。

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

使用 postgres 9.6...

【问题讨论】:

【参考方案1】:

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

Postgres docs 解释如下:

当向带有时区值的时间戳添加间隔值(或从中减去间隔值)时,天数组件会将带有时区的时间戳的日期提前或递减指定的天数。在夏令时更改期间(当会话时区设置为可识别 DST 的时区时),这意味着 interval '1 day' 不一定等于 interval '24 hours'。例如,会话时区设置为CST7CDTtimestamp 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

据我所知,除了将其设置为会话时区之外,没有其他机制可以告诉如何在特定时区解释间隔。换句话说,我认为你会寻找类似 @​​987654331@ 的东西,但我不相信这样的语法存在。

【讨论】:

是的,有点想。这是有道理的,尽管很不幸。谢谢【参考方案2】:

您可以在连接时为特定用户 (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)

【讨论】:

【参考方案3】:

您可以尝试在所需时区生成系列,然后转换为 UTC:

select generate_series('2018-11-03 00:00:00.000', '2018-11-06 13:40:39.067', '1d'::interval)::timestamp at time zone 'America/New_York'

输出:

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

【讨论】:

为了澄清它的作用是在 纽约时间 中生成 1 天的增量,即 1 小时。因此,以 UTC 显示的结果说明了时差。

以上是关于夏令时更改的 generate_series - 不同的结果取决于服务器时区的主要内容,如果未能解决你的问题,请参考以下文章

夏令时规则更改是不是使 C 运行时库无效?

postgresql 不正确的时区从夏令时更改

Joda-Time、夏令时更改和日期时间解析

如果日期是进行 DST(夏令时)更改的实际日期,有没有办法在 Python 中推断?

JVM 未在服务器中进行夏令时更改

如何更改 p:calendar 组件中的夏令时 (DST) 日期