夏令时更改的 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'
。例如,会话时区设置为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
。
据我所知,除了将其设置为会话时区之外,没有其他机制可以告诉如何在特定时区解释间隔。换句话说,我认为你会寻找类似 @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 - 不同的结果取决于服务器时区的主要内容,如果未能解决你的问题,请参考以下文章