83

一般来说,最好的做法是在 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,如果转换为 2012 年 7 月 1 日从 UTC 显示为本地时间,它将导致本地时间 17:00,如果转换为 2012 年 10 月 10 日(时区 DST 开启的日期)然后将导致下午 6 点,这不是正确的结束时间。

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

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

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

并且应用层将本地时间转换为UTC时间通过(API GetEndTimeByEvent)发送到其他系统。

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

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

4

7 回答 7

101

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

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

想两件事。首先,关于夏令时时钟移位的时间。您的活动是否有可能发生在凌晨 2 点到凌晨 3 点之间(在时钟轮班结束的那一天)?那应该怎么办?

其次,应用程序是否会受到实际时区变化的影响?换句话说,你打算带着它从伦敦飞到华沙,并适当地改变你的电脑时区吗?在这种情况下应该怎么办?

如果您对这两个问题的回答都是否定的,那么您最好使用当地时间。它将使您的应用程序更简单。但是,如果您至少回答一次,那么我认为您应该多考虑一下。


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

您提到它通过 API 公开时间。应用程序会在每个请求上查询数据库吗?如果您在内部将时间存储为 UTC,您将需要这样做或以其他方式确保在 DST/时区更改时缓存时间将被调整/修剪。

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

于 2012-07-21T07:55:58.850 回答
33

我喜欢这样想:

计算机并不关心时间作为人类可以理解的表示。他们不关心时区、日期和时间字符串格式或任何这些。只有人类关心如何解释和表示时间。

让数据库做它擅长的事情:将时间存储为一个数字——一个 UNIX 纪元(自 1970-01-01 以来经过的秒数)或一个 UTC 时间戳(没有时区或夏令时信息)。仅在必要时才关心以人类可以理解的方式表示时间。这意味着在您的应用程序逻辑、报告系统、控制台应用程序或人类将查看数据的任何其他地方。

于 2013-12-19T16:51:57.693 回答
9

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

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

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

如果您的申请符合以下条件:

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

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

于 2016-04-10T04:55:56.833 回答
6

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

如果您在持久性中转换为 UTC,那么您就失去了从用户的角度来看的偏移量。显示用户十年前签署文件的时间现在是一个难题。从 UTC 解决这个问题将意味着查找当时在该地区有效的 DST 规则。恶梦。

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

于 2017-04-20T14:32:27.140 回答
2

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

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

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

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

于 2012-07-20T15:34:32.243 回答
1

您实际上并没有像大多数时间 API 所假设的那样存储特定的时间点。如果您的数据库支持它(PostgreSQL 支持),则使用间隔,或者将其存储为一个整数,表示自午夜或相应的计划开始(星期一、月初等)以来的秒数(分钟/小时)。无论哪种情况,您都不必担心如何在系统之间处理“时间”,而只需在您的视图中将秒数转换为一天中的时间时,您就会感到非常头疼。

于 2015-07-21T00:50:29.560 回答
1

我们不能总是计算给定 UTC 和时区的本地时间吗?我们不能真正可靠地存储时间本身编码的时间和时区,因为时区的偏移量可能会改变,而 ISO 标准只允许我们对可能改变的偏移量进行编码。所以,我们不能,比如说,存储在本地时区编码的未来时间,因为我们实际上还不知道偏移量!因此,将时间存储在 UTC 中,并将时区存储为单独的条目,并在需要时进行计算,这样更不容易出错。本地时间通常是一个实现细节。似乎当我们存储它时,我们可能会混淆关注点。大部分时间显示相对于时区的时间是视图的业务。通过存储事物的组件并允许计算来组合它们,我们通常可以获得最大的灵活性。

于 2021-09-03T21:00:00.787 回答