原始 UTC 值的 Postgres 时间戳和时区

Mat*_*len 3 .net c# postgresql datetime timestamp

我对重新讨论这个过度讨论的话题犹豫不决,但我创建了一组表来存储数据,其中许多表包括一个名为“createdate”的字段,指定为“没有时区的时间戳”。代码库提供给这些的日期/时间值始终采用 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 的格式来说可能过于主观了)

Bas*_*que 6

不是片刻

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

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

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

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

时刻

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

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

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

用例示例:

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


Erw*_*ter 5

就像您在引用的来源中反复看到的那样,在可能涉及不同时区的所有情况下,timestamp with time zone( timestamptz) 是更合适的选择。

在内部,存储是相同的。但是对于timestamptz,Postgres 已经知道时间戳值适用于何处,而在这方面它对于timestamp without time zone( timestamp)却一无所知。

请注意,不会存储给定的时区。这是一个普遍的误解。它仅用于计算 UTC 时间。看:

AT TIME ZONE 是用于获取给定时区的相应时间的 SQL 构造。

SELECT my_timestamptz AT TIME ZONE 'US/Hawaii';
Run Code Online (Sandbox Code Playgroud)

随着timestamp它变得尴尬:

SELECT my_timestamp AT TIME ZONE 'UTC' AT TIME ZONE 'US/Hawaii';
Run Code Online (Sandbox Code Playgroud)

此外,当在文字中使用timestamp任何附加的时区信息时会忽略,这通常不是您想要的。

看: