PostgreSQL:无法使用 DST 使时区正常工作
Posted
技术标签:
【中文标题】PostgreSQL:无法使用 DST 使时区正常工作【英文标题】:PostgreSQL: Can't get timezones with DST to work properly 【发布时间】:2021-07-06 09:51:18 【问题描述】:我试图弄清楚 Postgres 如何结合时间间隔处理 DST。具体来说,我希望允许我的用户创建具有重复日期的事件 - 例如每天当地时间 16:00。
对于我正在做的事情,我需要将第一个日期存储在用户的本地时间,然后添加天数,而不更改用户本地时间的时间。我希望 timestamptz
带有完整的时区名称(所以它知道何时应用 DST?)结合简单的 1 天间隔可以完成这项工作 - 但在我的简单示例中它失败了:
德国使用 CET (+1:00) 并在 3 月 28 日凌晨 2:00 切换到 CEST (+2:00)。 突尼斯全年使用 CET。
因此,我预计,在 3 月 27 日使用 timestamptz 并添加 1 天,我会在柏林看到不同的 utc 偏移量,而在突尼斯没有变化 - 但它们都改变了偏移量,就像突尼斯一样正在使用 DST:
select
'2021-03-27 16:00:00 Africa/Tunis'::timestamptz as "tunis_before_dst",
'2021-03-27 16:00:00 Africa/Tunis'::timestamptz + INTERVAL '1 day' as "tunis_after_dst",
'2021-03-27 16:00:00 Europe/Berlin'::timestamptz as "berlin_before_dst",
'2021-03-27 16:00:00 Europe/Berlin'::timestamptz + INTERVAL '1 day' as "berlin_after_dst"
结果:
tunis_before_dst: '2021-03-27 16:00:00+01'
tunis_after_dst: '2021-03-28 16:00:00+02'
berlin_before_dst: '2021-03-27 16:00:00+01'
berlin_after_dst: '2021-03-28 16:00:00+02'
查看 pg_timezone_names
,我可以看到我的 Postgres 实例知道 Africa/Tunis
没有 DST - 所以我想知道为什么它会改变它的 UTC 偏移量。
我想很明显,时区和 DST 让我很困惑,但我在处理它们时做错了什么还是 timezonetz
不是我想的那样?
【问题讨论】:
dbfiddle.uk/… @Abelisto 那个似乎工作,但不知何故在 DST 之前它在 15:00+0 和之后在 16:00+1 显示突尼斯 - 没有真正的变化,但为什么显示不同?我希望它始终显示为 16:00+1,因为我用它在突尼斯当地时间 16:00 创建了一个时间戳? @d0n.key:dbfiddle 中的显示受其时区设置控制。见dbfiddle.uk/… 除此之外:您显示的结果显示有不同的偏移量。 Postgres 本身不会在同一个会话中这样做(使用相同的timezone
设置)。
【参考方案1】:
先吐槽。 DST 的概念是令人叹为观止的废话。连名字都是明显的BS。 “夏令时”。没有保存任何白天。我不敢相信欧盟仍然没有设法摆脱它,尽管绝大多数人都希望它消失,而且现在已经有一段时间废除它了。
在我的系统之外,主要的误解是:您假设数据类型timestamp with time zone
将存储时区信息。 它没有。在这里变得很明显:
如我所料,[...] 我会在柏林看到不同的 utc 偏移量,而在 突尼斯 - 但他们都同样改变了偏移量
您在输出中看到的时区偏移量是由会话的当前timezone
设置确定的偏移量。时区用作输入/输出修饰符/装饰器。 Postgres 总是在内部存储 UTC 时间。而且没有任何时区信息。
那里的类型名称有点欺骗性。众所周知,它最能愚弄:
Time zone storage in data type "timestamp with time zone"一旦你掌握了这个概念,其余的就会变得显而易见。
要保留本地时间(挂钟时间),请使用数据类型timestamp without time zone
(timestamp
),甚至只使用time
(切勿使用损坏的timetz
),并存储时区信息另外 - 理想情况下是时区名称('Europe/Berlin' 就像你拥有的那样),而不是时区缩写或数字偏移量。
timestamp with time zone
(timestamptz
) 是存储唯一时间点的正确选择,不受任何时区影响。时区偏移只是一个输入修饰符。以下两个字面值会产生相同的timestamptz
值完全相同,因为两个时区恰好在一年中的这个时候应用相同的偏移量:
'2021-03-27 16:00:00 Africa/Tunis'::timestamptz
'2021-03-27 16:00:00 Europe/Berlin'::timestamptz
但它们相差一小时,因为德国偏移量已根据当地 DST 制度发生变化:
'2021-03-28 16:00:00 Africa/Tunis'::timestamptz
'2021-03-28 16:00:00 Europe/Berlin'::timestamptz
相关:
Ignoring time zones altogether in Rails and PostgreSQL Preserve timezone in PostgreSQL timestamptz type【讨论】:
公平地说,由于 Covid 干扰了适应它的公司,欧盟推迟了 DST 的废除。因此,一旦我的项目准备好发布,DST 很可能在欧盟国家不再存在,但在其他几个国家仍然存在 - 因为它总是会出现不受欢迎的再现,我想最好做好准备. 是的,无论哪种方式,您都必须做好准备。愚蠢很难死。如果有的话。以上是关于PostgreSQL:无法使用 DST 使时区正常工作的主要内容,如果未能解决你的问题,请参考以下文章
从一个本地时区到另一个本地时区的 Python 日期时间转换(+ 奖励 DST)