使用 TIMESTAMP WITHOUT TIME ZONE 的有效用例是什么?

Mar*_*tus 55 postgresql timestamp

关于两者之间的差异,有一个很长且相当清晰的答案

  • TIMESTAMP WITH TIME ZONE -vs-
  • TIMESTAMP WITHOUT TIME ZONE

在 SO post Ignoring time zone together in Rails and PostgreSQL 中可用。

我想知道的是:实际使用是否有任何有效的用例,TIMESTAMP WITHOUT TIME ZONE或者应该将其视为反模式?

Mat*_*sOl 43

这在很多地方都有说明,但我认为在我们比较timestamp with time zonewithtimestamp without time zone类型时总是值得一提:thetimestamp WITH time zone不存储时区信息和时间戳。它的作用是将每个数据存储在 UTC 时区,如文档中所述

对于带时区的时间戳,内部存储的值始终采用 UTC(世界协调时间,传统上称为格林威治标准时间,GMT)。使用该时区的适当偏移量将具有指定显式时区的输入值转换为 UTC。如果输入字符串中未指定时区,则假定它位于系统的 TimeZone 参数指示的时区中,并使用时区区域的偏移量转换为 UTC。

timestamp WITHOUT time zone在 (1) 一切都在同一时区或 (2) 应用程序层处理时区并将所有内容存储在某个时区(通常是 UTC)的情况下,它被认为是有效的。但它也被认为是一种反模式,很简单,因为 (1) 的正确解决方案是将 TimeZone 设置配置为系统的给定一个时区,并且 (2) 已经解决,因为 PostgreSQL 已经将所有内容存储在同一时区(世界标准时间)。

现在,有了这两个失败,我只能有一个很好的理由来使用timestamp WITHOUT time zone. 那就是你想在未来存储事件并且当我们到达那个时间时必须触发某种警报的时候。timestamp WITH time zone当且仅当地区法律定义的关于时区的规则从未改变时,这可能是有益的。最常见的更改规则是关于是否采用夏令时 (DST)。

例如,假设您在,比方说,2013-06-15(尚未在 DST 中)安排某个事件发生在2013-01-15 10:00(将已在 DST 中),并且2013-06-15您所在的地区被指定采用 DST;但是,在那之后的一段时间,政府更改了规则并说您所在的地区将不再使用 DST,并且您的预定时间突然变为2013-01-15 11:00(而不是10:00),如果您使用timestamp WITH time zone,并保持您的 TZ 配置最新。当然,您可能还会注意到,如果您跟踪您感兴趣的地区/时区的规则更改,并更新受影响的记录,也可以使用时区处理此类情况。

值得一提的是,某些地区确实经常更改这些规则(例如在巴西,某些州 - 不是整个国家 - 经常会更改),但在大多数情况下,它会更早地更改它,因此您的用户只会受到距离很远的事件的影响当前时间。

说了这么多,我还有一个建议。如果您确实有不同时区的用户、日志或任何内容,请存储它们来自某处的时区并选择并使用timestamp with time zone. 这样,您可以 (1) 为不同来源(独立于它们的时区)交叉发生彼此更近的事件,以及 (2) 显示事件发生的原始时间(时钟时间)。


Bas*_*que 28

是的。有用例TIMESTAMP WITHOUT TIME ZONE

  • 在常见的商业应用程序中,这种类型仅用于:
    • 预订未来的约会
    • 代表不同时区的同一时间,例如东京巴黎的 23 日中午(两个不同的时刻,时间相同)
  • 对于跟踪时刻,时间线上的特定点,始终使用TIMESTAMP WITH TIME ZONE,而不是WITHOUT

TIMESTAMP WITHOUT TIME ZONE价值观不是时间线上的一个点,也不是实际的时刻。它们代表了关于潜在时刻的粗略想法,时间线上可能的点在大约 26-27 小时的范围内(全球时区的范围)。在您应用时区offset-from-UTC之前,它们没有实际意义。

例如:圣诞节

例如,假设您需要记录假期/圣日的开始。

Table: holiday_

Column: year_         Type: SMALLINT
Column: description_  Type: VARCHAR
Column: start_        Type: TIMESTAMP WITHOUT TIME ZONE
Run Code Online (Sandbox Code Playgroud)

为了记录今年12月25日午夜之后圣诞节开始的事实,我们需要说2016-12-25 00:00:00没有任何时区。在圣诞老人当天的早些时候,他在午夜过后访问新西兰奥克兰,因为那是地球上最早的午夜之一。然后他向西走,下一个午夜来临,很快就到达了菲律宾。然后驯鹿向西移动,在午夜到达印度,这恰好发生在奥克兰午夜之后的几个小时。很久以后仍然是法国巴黎的午夜,而加拿大蒙特利尔的午夜仍然更晚。圣诞老人的所有这些访问都发生在不同的时刻,但所有这些都发生在午夜之后不久,每个地方都有自己的午夜。

因此2016-12-25 00:00:00,在圣诞节开始时没有任何时区的录制是有用的和合法的,但只是含糊其辞。在您说“奥克兰的圣诞节”或“蒙特利尔的圣诞节”之前,我们没有特定的时间。如果您要记录每次雪橇着陆的实际时刻,您将使用TIMESTAMP WITH TIME ZONE而不是WITHOUT类型。

与圣诞节相似的是除夕。当时代广场舞会在纽约落下时,西雅图的人们仍在喝香槟,准备派对喇叭。然而,我们将记录的想法新年的时刻,作为2017-01-01 00:00:00一个TIMESTAMP WITHOUT TIME ZONE。相比之下,如果我们想记录纽约球落下的时间,或者西雅图人吹响喇叭的时间,我们会改为使用TIMESTAMP WITH TIME ZONE(not WITHOUT) 记录那些实际时刻,每三个小时相隔三个小时。

例如:工厂倒班

另一个示例可能是记录涉及不同位置挂钟时间的策略。假设我们在底特律、杜塞尔多夫和德里设有工厂。如果我们说所有三个工厂的第一个班次从早上 6 点开始,中午在 11:30 休息,那么可以将其记录为TIMESTAMP WITHOUT TIME ZONE. 同样,此信息以模糊的方式很有用,但在我们应用时区之前不会指示特定时刻。新的一天在东部更早的时候破晓。因此,德里工厂将是第一个在早上 6 点开工的工厂。几小时后,杜塞尔多夫工厂早上 6 点开始工作。但底特律工厂要等到 6 小时后,也就是早上 6 点才真正开工。

将此想法(工厂轮班通常何时开始)与每个工厂工人何时在特定日期打卡开始轮班的历史事实进行对比。打卡是一个真实的时刻,时间线上的一个实际点。因此,我们会将其记录在类型TIMESTAMP WITH TIME ZONE而不是WITHOUT类型的列中。

所以,是的,有合法的TIMESTAMP WITHOUT TIME ZONE. 但根据我对商业应用程序的经验,它们相对较少。在业务中,我们倾向于关心实际时刻:发票何时实际到达,合同何时生效,银行交易在何时执行。所以在这种常见的情况下,我们需要TIMESTAMP WITH TIME ZONE类型。

有关更多讨论,请参阅对类似问题的回答我应该为班次存储 UTC 时间戳还是本地时间

Postgres

请注意,Postgres 特别从不保存插入时间戳时指定的时区信息。

  • TIMESTAMP WITH TIME ZONE
    • 输入数据中包含的任何指定时区或偏移量用于将值调整为 UTC 并存储。然后丢弃传递的区域/偏移信息。想想TIMESTAMP WITH TIME ZONE作为TIMESTAMP WITH RESPECT FOR TIME ZONE
    • 印度今年 3 月 7 日中午 12:00 的输入将通过减去五个半小时:6:30 AM 调整为 UTC。
  • TIMESTAMP WITHOUT TIME ZONE
    • 输入数据中包含的任何指定时区或偏移量都将被完全忽略。
    • 印度今年3月7日中午12:00的输入记录为今年3月7日中午12:00,没有调整。

SQL 标准几乎没有涉及日期时间数据类型和行为的问题。因此数据库在处理日期时间方面差异很大

  • 此外,对于工厂(或医院)轮班,任何在夏令时转换日进行夜班工作的人的工作时间都会被错误记录,或者记录时没有时区。 (2认同)

Sha*_*sky 24

我想对此添加另一种观点,这与其他答案中的大部分内容背道而驰。在我看来,timestamp with time zone有效的用例很少timestamp without time zone,一般来说应该是首选。

首先,您必须了解“UTC 无处不在”模式,该模式通常推荐用于所有没有非常特殊要求的应用程序。它只是意味着您的应用程序应该始终以 UTC 格式表示其所有时间戳。当然,您将向用户显示本地日期/时间,但我们的想法是在渲染视图时在程序的最边缘转换这些。这是将 UTC 时间戳(您可能从数据库加载)和用户的时区(可能来自浏览器)组合成本地时间戳的正确位置。

如果你遵循这种模式,那么timestamp with time zone就是一种反模式。所有这种类型的作用是让 PostgreSQL 在读取和写入时间戳时根据您的 PostgreSQLTimeZone会话变量执行时区转换。不是在应用程序的最边缘处理时区,而是将它们引入到它的核心,进入与数据库的通信。您必须始终注意始终TimeZone正确配置PostgreSQL ,从而增加了另一个不必要的故障点和混乱点。

为了什么?如果您遵循 UTC 无处不在的模式,那么您的应用程序无论如何都只有 UTC 时间戳;那么为什么要让您的数据库对它们进行时区转换呢?您可以简单地将它们存储在一个timestamp without time zone列中,并将其视为始终是 UTC。没有转换,没有时区,没有并发症。

  • 首先,如果您的 PostgreSQL TimeZone 设置为 UTC 以外的任何内容,并且您使用的是 timestamptz,则您的程序正在读取非 UTC 时间戳。这根本就不是“无处不在的 UTC”模式,并且取决于您的客户端语言可能会也可能不会造成很多复杂性。要么您到处都是UTC,要么您使用的是本地时间戳.... (7认同)
  • 我认为时间戳的支持者也支持“无处不在的 UTC”模式。它只是在数据库级别强制执行的规则,而不是仅依赖应用程序/API 开发人员来强制执行?如果您能够执行这种做法,为什么不呢?此外,您可以强制所有 postgres 连接将它们的时区设置为 UTC。即使没有,ISO 格式也会包含 tz 偏移量。那么这不是与 UTC 无处不在但强制执行的相同的事情吗? (3认同)
  • 可以,但是使用“timestamptz”就更没有意义了。这种类型的重点是执行时区转换作为读取和写入 PostgreSQL 的值(在数据库内部,时间戳始终保存为 UTC)。始终将会话时区设置为 UTC 会有效地禁用这些转换,那么为什么要使用“timestamptz”呢?换句话说,如果数据库中的值是 UTC,并且进出数据库的值始终是 UTC,那么为什么数据库中需要时区感知呢? (2认同)