如果废除夏令时,设置在未来的 PostgreSQL "带时区的时间戳"可能会无声地出错

关于 PostgreSQL 中“timestamp with time zone”存储及相关时区问题的讨论:

  • 问题描述:PostgreSQL 在存储“timestamp with time zone”时会转换为 UTC,这可能导致未来时区定义更改时时间戳出错,且若未在某处记录原始时区,将不知预期时间。
  • 讨论内容

    • 存储策略:对于事件网站,建议将 datetime 存储两次,一次为本地(不带时区)的 yyyy-mm-dd hh:mm 样式,另一次为去规范化的 UTC 版本,同时存储事件的位置时区。如对于 2025 年 11 月 25 日 6 点的事件,应明确是本地时间。
    • UTC 存储的局限性:存储为 UTC 虽可用于“按日期排序”等功能,但不能满足存储未来本地事件时间的需求,因为时区规则可能变化。如乌克兰取消夏令时,导致很多事件的开始时间出错。
    • 不同观点

      • 有人认为始终存储 UTC 时间的建议有些过度,很多时候需要存储本地时间及相应时区名,如处理未来时间时。也有人认为很少需要存储未来时间戳,且可忽略 leap seconds(除特殊高精度需求外)。
      • 对于存储为 epoch 时间戳(Unix 时间),虽方便但也有与存储未来日期相同的问题,即无法应对时区等相关变化。
    • 相关资源:Jon Skeet 的博客https://codeblog.jonskeet.uk/2019/03/27/storing-utc-is-not-a-silver-bullet/对该主题有很好的阐述。
    • 时区数据库问题:TZ 数据库使用“Continent/City”避免未来时区定义变化,但其不能处理时区覆盖区域的变化,如印第安纳州的时区情况。即使存储传统时区名,地理区域移动到不同时区时也会出现问题。
    • 解决思路:最佳方式是让用户在输入时指定 UTC,或存储事件有效的地理位置,在未来临近时间将其解析为 UTC。也可记录多个次要位置及本地时间,通过 iCalendar 数据模型扩展属性存储相关信息,但 iCalendar 数据模型本身不支持。还可存储“本地时间在本城市”或“用户 X 的本地时间”等上下文信息。
    • 递归事件问题:递归事件不能简单存储为 datetime,因为 DST 废除等原因,时间会有所不同。可使用 iCalendar 的 RRULE 来处理递归事件,但知晓未来时区仍是难题。
    • PostgreSQL 与其他数据库:PostgreSQL 并非唯一存在此问题的数据库,MySQL 等也有类似情况。
    • PostgreSQL 自身处理:PostgreSQL 承诺由数据库引擎处理时区和 DST,但迁移到其他引擎时可能出现问题。存储的 timestampz 在更新 tzdb 后无法重新计算,对于未来事件需固定本地时间时,timestampz 不够用。读取存储的 timestampz 时,TZ DB 会根据当时的规则进行处理。

总之,处理未来事件的时间存储和时区问题较为复杂,没有完美的解决方案,需要根据具体情况选择合适的方式,并考虑到时区规则变化等因素。

阅读 25
0 条评论