PostgreSQL:无法使带 DST 的时区正常工作

PostgreSQL: Can't get timezones with DST to work properly

我想弄清楚 Postgres 如何结合时间间隔来处理 DST。具体来说,我想让我的用户创建具有重复日期的事件 - 例如每天 16:00 当地时间。

对于我正在做的事情,我需要以用户当地时间存储第一个日期,然后在不更改用户当地时间的一天中的时间的情况下向其添加天数。我希望 timestamptz 具有完整的时区名称(因此它知道何时应用 DST?)结合简单的 1 天间隔可以完成这项工作 - 但它在我的简单示例中失败了:

德国使用 CET (+1:00),并在 3 月 28 日上午 2:00 切换到 CEST (+2:00)。 突尼西亚全年使用 CET。

因此我预计,在 3 月 27 日使用 timestamptz 并向其添加 1 天,我会在柏林看到一个不同的 utc-offset,而在突尼斯没有变化 - 但他们都同样改变了偏移量,因为如果突尼斯使用夏令时:

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 偏移量。

我想很明显,时区和夏令时令我很困惑,但我是不是在处理它们时做错了什么,还是 timezonetz 不是我想的那样?

先吐槽一下。 DST 的概念是惊人的废话。连名字都是明显的废话。 “夏令时”。没有保存任何日光。我不敢相信欧盟仍然没有设法摆脱它,尽管绝大多数人希望它消失,而且现在已经有一段时间取消它的共识。

在我的系统之外,主要的误解是:您假设数据类型 timestamp with time zone 存储 时区信息。 不是。 在这里变得很明显:

Thus I expected, [...] I'd see a different utc-offset in Berlin, and no change in Tunis - but they both changed the offset equally

您在输出中看到 的时区偏移是由会话的当前timezone 设置确定的偏移。时区用作输入/输出修饰符/修饰符。 Postgres 总是在内部存储 UTC 时间。并且没有任何时区信息。

类型名称有点骗人。众所周知,愚弄最好的人:

一旦你掌握了这个概念,剩下的就很明显了。

要保留本地时间(一天中的挂钟时间),请使用数据类型 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