首页 > 解决方案 > 原始 UTC 值的 Postgres 时间戳和时区

问题描述

我不愿重新讨论这个被过度讨论的话题,但我创建了一组表来存储数据,其中许多表包含一个名为“createdate”的字段,指定为“timestamp without time zone”。代码库提供给这些的日期/时间值始终采用 UTC。旨在让 UI 能够控制如何向用户呈现数据,因此某些设置会要求在 UI 中转换为时区。

其中一些时间戳将用于报告,以便为最终用户显示信息。其他时候,这些值将用于确定针对数据运行的夜间作业。

这是一个典型的多租户云托管系统,客户端跨越不同时区。永远不要移动服务器,尽管我认为更改托管区域的可能性很小。它是在.net 平台上编写的。不使用 noda 时间,只使用内置的 DateTime 东西(现在)。

文档非常清楚带有时区的时间戳如何存储信息: https ://www.postgresql.org/docs/current/datatype-datetime.html

这个答案对两种主要时间戳数据类型的差异也有很好的背景: https ://stackoverflow.com/a/14616640/1905693

这个答案也有一些很好的信息,但面向 Java: 使用 Java 在 PostgreSQL 中存储时间的最推荐方法是什么?

Josh Berkus 有一篇过时的文章很有帮助: https ://it.toolbox.com/blogs/josh-berkus/zone-of-misunderstanding-092811

似乎大多数都推荐带时区的时间戳,但就我而言,没有时区的时间戳合适吗?

如果我确实想依靠 pg 进行转换,AT TIME ZONE 子句可以吗?

从整体系统架构来看,依靠 UI 改变呈现方式是不是一种普遍合理的做法?(是的,这个对于 SO 的格式来说可能太有意见了)

标签: c#.netpostgresqldatetimetimestamp

解决方案


一刻都没有

正如其他人所说,TIMESTAMP WITHOUT TIME ZONE是错误的数据类型。

该类型仅包含带有时间的日期,例如 2021 年 1 月 21 日中午。但我们不知道这是否意味着日本东京的中午、法国图卢兹的中午或美国俄亥俄州托莱多的中午——三个截然不同的时刻,相隔数小时。所以这个类型不能代表一个时刻,不是时间轴上的一个特定时刻。

TIMESTAMP WITHOUT TIME ZONE类型适用于三种用例:

  • 代表在他们的地方都知道的多个时刻。例如,Acme Corp 要求德里杜塞尔多夫底特律各工厂的经理在两天内当地时间中午发布公告。
  • 表示预期时区未知的日期和时间。我认为应该拒绝这个错误的数据。但是如果你坚持把它写入数据库,那么这种类型是合适的。
  • 即使那些讨厌的政客改变他们管辖范围内的时区偏移量,我们也希望保留时间的未来约会。这些政治变化经常出人意料地发生。因此,请使用两列进行预约:TIMESTAMP WITHOUT TIME ZONE一列,另一列中的预定时区名称。时区以格式命名Continent/Region例如Africa/Tunis. 在运行时,当您需要一个时刻进行日历时,将时区应用于日期和时间,以根据当前时区规则动态确定时刻。在Noda Time中,您将检索 aLocalDateTime和时区,以生成ZonedDateTime日历。

时刻

当您关心时刻、时间轴上的特定点时,请使用TIMESTAMP WITH TIME ZONE.

在 Postgres 和许多其他数据库中,这种类型有点用词不当。时区实际上并未与日期和时间一起保存。相反,在提交到数据库时,任何时区指标或与 UTC 的偏移量都用于调整到 UTC(零小时-分钟-秒的偏移量)。该 UTC 值是 Postgres 写入数据库的值。UTC 是 Postgres 总是从数据库中检索的。

注意:一些中间件和工具具有善意但非常令人困惑的反特性,即动态地将默认时区应用于检索的值,从 UTC 调整到该时区。这会产生该时区已与数据一起保存的错觉。但是,不,不是这样。列仅TIMESTAMP WITH TIME ZONE存储UTC值。

用例示例:

  • 跟踪创建、修改或删除数据库记录的时刻。
  • 用于调试或系统管理员工作的日志记录。
  • 跟踪关键合同何时签署或生效。

推荐阅读