关于




TIMESTAMP WITH TIME ZONE
-vs-
TIMESTAMP WITHOUT TIME ZONE

之间的区别,有一个很长很清楚的答案在Rails和PostgreSQL中完全忽略时区的SO中。

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

评论

这个问题存在被“主要基于意见”封闭的风险。我在下面的回答中尝试给出一些客观的观点。

这个问题是很现实的,每种数据类型都有不同的含义。因此,我当然不会认为此问题主要是基于意见的。

#1 楼

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


对于具有时区的时间戳,内部存储的值始终以UTC(全球标准时间,传统上称为格林威治标准时间(GMT)。使用该时区的适当偏移量,将指定了明确时区的输入值转换为UTC。如果在输入字符串中未指定时区,则假定该时区位于系统的TimeZone参数指示的时区中,并使用时区时区的偏移量转换为UTC。


在某些情况下(q)(1)所有内容都在同一时区或(2)应用程序层处理时区并仅将所有内容存储在特定时区(通常为UTC)下,使用timestamp WITHOUT time zone被认为是有效的。但这也被认为是反模式,因为(1)的正确解决方案是将TimeZone设置配置为系统的给定时区,并且(2)已解决,因为PostgreSQL已将所有内容存储在同一时区上(UTC)。

现在,将这两个条件降低了,我只有一个很好的理由来使用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)显示事件发生的原始时间(时钟时间)。

评论


codeblog.jonskeet.uk/2019/03/27/…确切地深入讨论了这种未来事件规则变更的情况(具有相同的结论)

–贝尼(Beni Cherniavsky)-帕斯金(Paskin)
19年11月7日在13:22

#2 楼

是。有TIMESTAMP WITHOUT TIME ZONE的用例。


在常见的商务应用中,此类型仅用于:


预订未来的约会
表示相同的时间跨不同时区的日期,例如东京和巴黎的23日正午(相隔两个不同的时刻,同一天的时间)


用于跟踪时刻,在时间轴上,请始终使用TIMESTAMP WITH TIME ZONE而不是WITHOUT

TIMESTAMP WITHOUT TIME ZONE值不是时间轴上的点,不是实际时刻。它们代表了有关潜在时刻的粗略想法,可能涉及时间轴上大约26-27小时(全球时区范围)内的时间点。在您应用时区或UTC偏移量之前,它们没有实际含义。

例如:圣诞节

例如,说您需要记录假期的开始时间/神圣的日子。

 Table: holiday_

Column: year_         Type: SMALLINT
Column: description_  Type: VARCHAR
Column: start_        Type: TIMESTAMP WITHOUT TIME ZONE
 


为了记录圣诞节今年12月25日午夜之后开始的事实,我们需要说2016-12-25 00:00:00没有任何时区。圣诞老人那天凌晨,他在午夜之后访问NZ奥克兰,因为这是全球最早的午夜之一。然后,随着下一个午夜的到来,他向西行驶,不久便到达了菲律宾。然后,驯鹿向西方向移动,在午夜到达印度,恰好发生在奥克兰午夜后的几个小时。更晚之后仍然是巴黎FR的午夜,并且仍然晚了在加利福尼亚州蒙特利尔的午夜。圣诞老人的所有这些拜访都是在不同的时间发生,但都是在每个地点自己的午夜之后的午夜之后进行的。

因此,在圣诞节开始时录制没有任何时区的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(而不是WITHOUT)记录这些实际时刻,彼此间隔三个小时。 br />
例如:工厂倒班

另一个例子可能是记录涉及跨各个位置的挂钟时间的策略。假设我们在底特律,杜塞尔多夫和德里都有工厂。如果我们说在所有三个工厂中,第一个班次从上午6点开始,在上午11:30休息,这可以记录为TIMESTAMP WITHOUT TIME ZONE。同样,此信息以模糊的方式很有用,但在我们应用时区之前不会指示特定的时刻。东方较新的一天更早了。因此,德里工厂将是第一个早上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调整为UTC。



TIMESTAMP WITHOUT TIME ZONE


输入数据中包含的任何指定时区或偏移都将被完全忽略。
印度今年3月7日中午12:00的输入被记录为今年3月7日中午12:00的输入。



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

评论


进一步,随着工厂(或医院)的轮班,在夏令时转换之日进行墓地轮班的任何人都将错误地记录其工时,而没有时区。

–詹森
19年1月21日,下午1:47

我认为最后一个示例中有一个拼写错误:“ 3月7日中午12:00的输入..应该被记录为12:00(而不是21:00)”

– TmTron
19年1月21日在16:02

@Jasen在实际记录时间时,您正在跟踪时刻,即时间轴上的特定点。要跟踪时刻,请不要使用TIMESTAMP WITHTIME TIME ZONE。要跟踪某个时刻(例如某人开始和完成其轮班),请使用TIMESTAMP WITH TIME ZONE。在Java代码中,将为ZonedDateTime shiftEnded = ZonedDateTime.of(2020,1,23,2,0,0,0,ZoneId.of(“ Africa / Tunis”)));。然后将其写入带时区类型的TIMESTAMP数据库列:myPreparedStatement.setObject(shiftEnded.toOffsetDateTime());

–罗勒·布尔克
20 Mar 22 '20 at 18:30

#3 楼

我想为此添加另一种观点,这与其他答案中的大部分内容背道而驰。我认为timestamp with time zone几乎没有有效的用例,通常应该首选timestamp without time zone

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

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

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

评论


我认为时间戳的支持者也支持“无处不在的UTC”模式。只是规则只是在数据库级别执行,而不是仅依靠应用程序/ API开发人员来执行它?如果您能够执行该做法,为什么不呢?另外,您可以强制所有postgres连接将其时区设置为UTC。即使没有,ISO格式也会包含tz偏移量。难道不是UTC在各地都被强制执行吗?

–iamnat
18年8月22日在9:46



首先,如果您的PostgreSQL时区设置为UTC以外的任何值,并且您使用的是时间戳,则您的程序正在读取非UTC时间戳。这根本不是“无处不在的UTC”模式,并且取决于您的客户端语言可能会也可能不会带来很多麻烦。您到处都是UTC,或者您正在使用本地时间戳记...。

– Shay Rojansky
18年8月22日在10:04

再次,很容易想象在某个服务器上设置了新的PostgreSQL实例,而意外保留了默认配置的本地计算机时区。应用程序从该服务器读取并在完全任意的时区(该服务器恰好位于该时区)中获取本地时间戳。我们为什么要这种额外的并发症?

– Shay Rojansky
18年8月22日在10:11

可以,但是使用timestamptz甚至没有意义。这种类型的全部目的是将时区转换为对PostgreSQL进行读写的值(在数据库内部,时间戳始终保存为UTC)。始终将会话时区设置为UTC会有效地禁用这些转换,那么为什么要完全使用timestamptz?换句话说,如果数据库中的值是UTC,而进出数据库的值始终是UTC,那么为什么在数据库中根本没有时区意识?

– Shay Rojansky
19年1月21日在13:45

之所以使用时间戳记TZ,是因为它具有准确清晰的信息,而不是依靠诸如“ UTC Anywhere”之类的约定来解释其价值。可以通过更改数据库名称设置时区'UTC'将客户端时区设置默认为UTC;

–詹森
20-3-25在1:27



#4 楼

date时间戳也不包含时区信息,但是很多人都在使用它。

当然,它本身并不是答案。

使用它是有效的timestampdate对于始终在同一时区或相对于某个已知时区存储的任何时间戳和日期。

如果时区始终相同或隐式,则存储的反模式更多而不是存储它(冗余信息)。

时间戳还可用于存储技术信息,例如在应用程序日志中使用或用于性能测量。在这种情况下,时区也可能不重要。



相反,如果您正在设计或在分布式系统上或在其中系统的各个部分将分布在不同时区中的任何系统上工作, ,尽管某些系统可能设计为在一个时区运行,例如UTC,但不使用timestamp with timezone可能被认为是一种反模式。在这种情况下,时区逻辑可能会被推送到应用程序层中,但是我认为这是一种反模式,是的。

评论


我了解在某些情况下使用没有时区的时间戳不会有什么坏处,但是它能为我带来任何好处吗?否则,如果带有时区数据类型的时间戳总是相等或更合适,为什么还要打扰呢?

– Marcus Junius Brutus
2014年2月16日15:35

不存储冗余信息将是我的主要论点。我认为不带时区的时间戳上的日期算术也会更快,但这仅在您要处理数十亿行时才重要。

–科林·哈特(Colin't Hart)
14年2月16日在15:52

@ Colin'tHart时间戳和timestamptz都以相同的方式存储。时间戳中没有存储任何时区信息,而是将其转换为UTC进行存储。我要说的是,当相关时间戳表示绝对时间时,请始终使用timestamptz。这就是时间戳的全部含义。我已经在这里写过这个。

– fphilipe
16年6月20日在14:19

#5 楼

如果您的应用程序被限制在一个时区,则似乎很想在本地时间存储和处理日期/时间,但我认为这是一个错误。

从夏时制切换回标准时重复当地时间一小时的时间(假设时差为一小时)。我住的地方通常发生在凌晨2点左右,因此本地时间运行在01:58、01:59、01:00,并且只会在重复的小时结束时才前进到02:00。

如果您尝试使用本地时间按时间对事件进行排序,这意味着来自两个不同小时的事件会混合在一起,并且不会按照实际发生的顺序出现。

相比之下,UTC不会出现此问题,并且排序整齐。因此,即使仅使用一个时区,您也希望DB以UTC存储和处理时间,并转换为本地时间或从本地时间转换为输入/显示。这是TIMESTAMP WITH TIME ZONE的行为。