将时间存储在UTC中总是一个好主意吗?还是以本地时间存储更好的情况?


67

通常,最佳做法是将时间存储在UTC中,如此此处所述

假设有一个重复发生的事件,例如结束时间总是与当地时间相同,例如17:00,无论该时区是否启用了夏令时。并且还要求在特定时区打开或关闭DST时不要手动更改时间。还要求任何其他系统通过API(例如GetEndTimeByEvent)要求结束时间时,都必须以UTC格式发送结束时间。

方法1: 如果决定以UTC存储,则可以将其存储在数据库表中,如下所示。

Event      UTCEndTime
=====================
ABC         07:00:00
MNO         06:00:00
PQR         04:00:00

对于第一个事件ABC,UTC的结束时间是上午07:00,如果将其转换为从UTC到2012年7月1日的本地时间,则将转换为17:00的本地时间,如果转换为2012年10月10日(日期(DST在该时区为ON的日期),则将导致下午6点,这不是正确的结束时间。

我想的一种可能方法是将DST时间存储在附加列中,并在时区DST ON时使用该时间。

方法2: 但是,如果将其存储为本地时间,例如事件ABC,则它将在任何日期始终为17:00,因为没有从UTC到本地时间的转换。

Event      LocalEndTime
=======================
ABC         17:00:00
MNO         16:00:00
PQR         14:00:00

应用程序层将本地时间转换为UTC时间,以通过(API GetEndTimeByEvent)发送给其他系统。

在这种情况下,将时间存储在UTC还是一个好主意吗?如果是,那么如何获取恒定的本地时间?

相关问题:是否有充分的理由不使用UTC来存储时间?


该程序仅用于此类事件吗?是否需要为DST更改时发生的事件提供服务?该程序应处理更改的时区吗?
2012年

@Michal是的,无论DST处于ON还是OFF状态,所有事件始终都是特定本地时区的本地时间(即4PM或9PM等)。假设某个特定事件“ MNO”发生在伦敦当地时间每天下午4点,无论DST处于打开还是关闭状态。
太阳

Answers:


88

我认为,为了回答这个问题,我们应该考虑使用UTC存储时间戳的好处。

我个人认为,这样做的主要好处是可以始终(大部分)保证时间是一致的。换句话说,无论何时更改时区或应用DST,您都不会及时往返。这在文件系统,日志等中特别有用。但是在您的应用程序中有必要吗?

想两件事。首先,关于DST时钟移位的时间。您的事件是否有可能在凌晨2点至凌晨3点之间发生(在时钟转换当天)?那应该怎么办?

其次,应用程序是否会受到实际时区的更改?换句话说,您是否打算从伦敦飞往华沙,并适当更改计算机时区?在这种情况下应该怎么办?

如果您对这两个问题均未回答,那么最好选择当地时间。它将使您的应用程序更简单。但是,如果你的回答至少一次,那么我认为你应该给它更多的思考。


这就是关于数据库的全部。另一件事是应用程序内部使用的时间格式,这应该取决于您实际将使用该时间做什么。

您提到它通过API公开时间。应用程序是否会根据每个请求查询数据库?如果您在内部将时间存储为UTC,则需要执行此操作,或者确保在DST /时区更改时,将调整/修剪缓存时间。

时间本身会做什么?像打印一样,该事件将在8小时内发生还是在大约该时间暂停?如果是,那么UTC可能会更好。当然,您需要考虑所有上述问题。


因此,如果在数据库中将DateTime保存为UTC DateTime(而不是UTC DateTime(而不是varchar,ToUniversalTime c#)),则在进行填充时,是否总是需要在客户端进行转换以获取确切的日期?
AhammadaliPK

26

我喜欢这样想:

计算机并不关心时间是人类可以理解的表示。他们不在乎时区,日期和时间字符串格式或其他任何格式。只有人类关心如何解释和表示时间。

让数据库执行其擅长的工作:将时间存储为数字-UNIX时期(自1970-01-01起经过的秒数)或UTC时间戳(无时区或夏令时信息)。仅在需要时才关心自己以人类可理解的方式表示时间。这意味着在您的应用程序逻辑,报告系统,控制台应用程序或其他任何要查看数据的地方。


8
问题在于,您无法从纪元或UTC日期/时间值返回到用户的原始本地时区日期/时间(也不会存储其他信息)。从本地时区转换为纪元或UTC是有损转换。
彼得

3
很难回答600个字符,因此,我不得不指出其他内容。这太问题是一个良好的开端,并含有大量的环节,进一步阅读:stackoverflow.com/questions/2532729/...
彼得·

1
至关重要的是了解问题领域以及它是否包含物理时间,民事时间或两者兼而有之。Epoch在处理物理时间方面是可以通过的(尽管我更喜欢UTC),但是对于我在民用时间上工作的系统而言,这种情况通常要普遍得多。
彼得

1
通过浮点表示形式时可能会发生有损。例如,持有x以来的纪元是保存时间戳的一种极好的方式,除非JavaScript中没有long,因为JavaScript很不错。当您经过时间I且超出时区感知和不了解表示形式(看着您的Python sql驱动程序)时,也会发生时区截断
user48956

8

以下内容不适用于真正的多租户全球SaaS产品,因此,此意见针对的是简单的“业务线”应用程序开发人员。

存储为UTC很好,但是如果您这样做,有一个引起痛苦的要求:“您能给我写一份报告,告诉我每天发生多少X吗?”

如果将日期存储为UTC,则此要求会引起痛苦;您需要在应用程序服务器和报告中编写时区调整代码;您对包含日期条件的数据执行的每个临时查询都需要考虑这一点。

如果您的应用程序符合以下条件:

  1. 每个实例都基于单个时区。
  2. 时区转换通常在办公时间以外进行,或者您实际上并不关心事物的“持续时间”,以至于缺少一个小时左右就很重要。

我建议您将datetime存储为本地日期时间,同时使用可将您与服务器时区配置问题(例如,.net中的Noda.Time)隔离开的库。


5
我认为您有道理,但我发现您的语气使我分心。我认为,如果您以更中立/正式的方式写它,您的答案将受到更多赞赏:)。
Zero3'1

6

如果您的系统间消息使用ISO 8601,并且您的数据库正在存储原始本地时间+偏移量(例如datetimeoffset在MSSQL或ISODateMongo中,因为ISO 8601会捕获它),而您仅DateTimeOffset在.NET或OffsetDateTimeJava中使用您的代码,那么根本就不需要转换。您只需存储它。所有比较功能都将起作用。

如果您坚持不懈地转换为UTC,那么您将失去从用户角度出发的偏移量。现在,显示用户在十年前签署文档的时间非常困难。从UTC进行处理将意味着查找该地区当时正在使用的DST规则。恶梦。

我相信我们都习惯于转换为UTC以实现持久性的原因是因为我们从来没有拥有正确的数据结构/数据类型来允许我们做正确的事情。


有什么资源可以分享吗?
prasanthv

使用非UTC时间戳有几个问题,很多问题都无法在注释中列出,但是我将在最后添加一些链接。如果您需要时间戳的时区,可以将其存储在其他注释中。然后,您可以获得UTC的所有好处,同时仍然能够将时间戳转换为捕获所有原始数据的时间戳。- 2018.javazone.no/program/0507d823-bfa9-483c-8aef-3881c38c315e - ideas.kentico.com/forums/239189-kentico-product-ideas/...
oligofren

乔恩·斯基特(Jon Skeet)今天就此主题发表了文章。codeblog.jonskeet.uk/2019/03/27/...
卢克Puplett

从用户的角度来看,转换为UTC不会给您带来任何损失。您可以将UTC转换为任何时区,但不能将所有时区都转换为UTC。这就是为什么UTC是标准的原因以及为什么将其称为Univeral。有些人更进一步,并且总是存储两种时间格式,以防出现问题(指定了错误的时区)。
jgmjgm

2

我只会存储“时间”组件而没有任何区域。每当API需要提供服务时,请添加正确的日期,并将该日期作为该日期的本地时间转换为UTC。

数据库= 17:00(使用不带日期的时间戳,小时作为字节,分钟作为字节,字符串)

检索=我们想要事件的日期+数据库17:00 =>将其从本地转换为UTC

这样,您将始终在UTC中提供正确的时间。


3
切换期间在DST日期发生的事件如何?我有一些数据发生在3月13日凌晨1点或凌晨2点之间。尝试将其转换为UTC时出现错误,并且我不知道时间戳是否占了开关的比例。
2016年

1

实际上,您并没有像大多数时间API所假定的那样存储特定的时间点。如果数据库支持(PostgreSQL支持),则使用时间间隔,或将其存储为整数,表示自午夜或日程表的相应开始(秒,月初等)以来的秒数(分钟/小时)。在这两种情况下,您都不必为担心系统之间如何处理“时间”而烦恼很多,而仅将视图中的秒数转换为一天中的时间只增加了很小的麻烦。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.