[英]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 尊重夏令时的变化,我可以获得我想要的正确纪元或时间点:
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天间隔的原因,所以我认为您的问题的答案很简单,不是。
Postgres文档对此进行了如下解释:
当向带有时区值的时间戳添加间隔值(或从中减去间隔值)时,天组件将带时区的时间戳记的日期提前或减少指定的天数。 跨夏令时更改(将会话时区设置为可识别DST的时区),这意味着
interval '1 day'
不一定等于interval '24 hours'
。 例如,将会CST7CDT
时区设置为CST7CDT
,timestamp with time zone '2005-04-02 12:00-07' + interval '1 day'
timestamp with time zone '2005-04-03 12:00-06'
将产生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
,但我不相信这样的语法存在。
您可以在连接时自动为特定用户(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)
您可以尝试在所需时区生成系列,然后转换为 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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.