基于TimeZone存储DateTime的最佳实践


16

开发一个Web应用程序,该应用程序应允许用户根据其TimeZone安排约会。我将用户计划的日期时间作为服务器日期时间存储到数据库字段中。在显示计划信息时,将从数据库中检索值并转换为用户timzone。在代码库中进行处理我正在根据用户时区转换DateTime。请建议这是最佳做法还是存在任何简便方法?

Answers:


33

欢迎来到非计算编程中最困难的问题之一-正确地向最终用户表示日期和时间。

实际上,无论如何解释,都应将时间戳存储在固定的单一表示形式中,因为无论您多么努力,总是会出现歧义,而且如果没有固定的表示形式就无法解决。您已经选择了最糟糕的用例之一-安排约会。唯一最糟糕的常见用例是空中旅行,其中旅行可能在一个时区开始,而在另一个时区结束,可能在当地时间更早。

始终始终将UTC存储在UTC中,并在用户的首选或明确指定的时区中显示。 如果有可能,请让用户告诉您输入时相信的时间戳是哪个时区(例如,具有一个明确的时区字段,并使用其首选时区预填充该时区)。


1
+1。并且它的灵活性。具有通用,一致,通用的标准参考时间-任何本地区域“解引用”都将正确完成。
2013年

实际上,安排约会是您可能将其存储为UTC 的几次机会之一:如果DST规则更改(或与UTC的偏移量),约会时间会怎样?在大多数情况下,您希望约会时间在本地保持不变,这意味着UTC值会更改。您很可能希望存储一个表示“ TimeInTimeZone + TimeZone”的值,从中可以轻松得出UTC。跨时区调度(如航空公司)有一些限制,这可能意味着必须结合使用两者。
Clockwork-Muse

1
哦,比这还糟。我定期与其他国家/地区的同事开会,他们的时变规则与我的不同。发生这种情况时,正确的答案是什么?实际上,没有计算机可以知道:-(
Ross Patterson

3

处理日期/时间信息的最简单方法取决于应用程序的要求。

如果用户输入了日期和时间,并且服务器不对该日期/时间信息进行任何处理,只是将其存储在数据库中,并且不希望输入的时间适合查看的位置(例如,则用户输入了“ 8:00 PM”,并且无论在世界上哪个地方查看,都应该显示为“ 8:00 PM”),然后最简单的方法是将DateTime存储为本地时间而没有任何时区信息。

另一方面,如果服务器必须使用输入的DateTime作为执行某事的触发器,或者如果应该将时间正确调整为当前时区,那么最好的选择是将DateTime存储为UTC时间并将其调整为本地时间。每次从数据库中读取时间(针对用户的当前位置)。


-1表示“以本地时间存储”,+ 1表示“以UTC存储”。
罗斯·帕特森

@RossPatterson:您能解释一下“ -1以“将其存储在本地时间””吗?考虑到我给予的条件,我很好奇为什么这应该是一个坏主意。
巴特·范·英根·谢瑙

1
例如,使用用户本地时间意味着每个值实际上是几十种不同数据类型之一。这意味着您不能对它们执行有趣的操作,例如“ TIMESTAMP>'2013-08-27T12:00:00'”。这甚至意味着您不知道何时以及是否应用夏令时转换。
罗斯·帕特森

@RossPatterson:谢谢。我同意在大多数情况下,本地时间不是正确的解决方案。
Bart van Ingen Schenau 2013年

2

不要混淆两个相关概念,这一点很重要。

如果要操纵实际时间点,即特定日期的特定时间,那么唯一的方法就是始终使用通用UTC时间值。 切勿尝试将其存储为与时区相关的值。向用户显示此时间值时,请始终转换为该用户的时区。(并且不要忘记时间和日期都取决于时区。)

另一个概念是“时间表达”的概念,例如“每个星期二8:00 PM”。您特别提到允许人员安排约会,如果您有定期约会,则需要这样的表达。我不知道任何标准表达语言,但是无论您使用什么语言,它都会与指定它的人所在的时区有关。最好将其标明,例如“太平洋时区的每个星期二晚上8:00 PM”。对于时间表达式,您始终将其存储为字符串,并且永远不涉及任何系统时间格式。

在我的博客上查看有关此内容的更多讨论


1

这里的许多答案都表示存储为UTC。但是要特别小心。例如,如果您将约会安排在12:00,但是约会发生在更改夏令时之后,那么会发生什么?在存储约会时,UTC不会保留有关dst是否处于活动状态的任何信息。许多大型的著名系统都犯了此错误,用户在夏季的上午9点发送电子邮件,然后在冬季的晚上8点发送电子邮件,因为从UTC返回的时间取决于您查看日期时间的时间,记录日期时间的日期。

更好的做法是假设您的用户希望始终拥有自己选择的时间。没有UTC转换,没有时间转换,没有时区信息,什么都没有。在2016年3月21日从08:00到12:00进行预约就是这样。不要使用本地时间或UTC时间,而是使用未指定的时间(在json中既没有z也没有+,基本上在.NET中没有DateTime.Kind = DateTimeKind.Unspecified)。

当然,如果您的用例是您是一家与来自不同时区的人举行会议的公司,并且您想在公司日历中查看此信息,但允许用户查看他们所在时区的时间,变得更加复杂。对于不同时区的不同人员(供应商和客户),时间必须正确。

在这种情况下,你甚至可能要录制预约保存到数据库中,在什么时区以及如果包含DST与否。这样,您始终可以将本地时间计算为其他时间,无论是现在的时间还是历史的预期时间。因为时区在很大程度上不是静态的,所以使事情变得更加复杂。

幸运的是,强烈推荐使用http://nodatime.org/之类的库。他们使用日期更加一致。即使那样,我还是建议您使用接口将所有日期时间变量和逻辑包装在自己的包装器中,以便可以对它们进行模拟,然后您以后仍可以切换逻辑。


我不太同意您的结论,但是安排在夏令时期间安排的中午会议是一个特殊的挑战,就像未来几年预定的每周一次会议一样。
弯曲

当然,如果您的用例是您是一家与来自不同时区的人举行会议的公司,并且希望在公司日历中查看此信息,但允许用户查看他们所在时区的时间,它变得更加复杂。 ”-几乎所有公司都如此。中国和印度以外的公司很少可以忽略跨时区的任命。
罗斯·帕特森

“通过这种方式,您始终可以将本地时间计算为其他时间,无论是现在的时间还是历史的预期时间。因为时区不是很固定,所以使事情变得更加复杂。 ”-您接受要进行计算的时间,您确实需要选择一个时间表示形式。UTC,EST(但不是EST5EDT),IST等。但是,您根本无法在同一字段中具有基于多个时区的值,并且无法对其进行任何合法的合计处理。确定显示处理。但这是容易的部分。
罗斯·帕特森

至少您仍然可以正确转换为UTC。您可以随时回顾此信息来创建计算表或值。但是,您永远都不会走这条路。请注意,SQL 2016开始本机支持,尽管不幸的是,它仍然依赖(至少在历史上)不完全正确的注册表数据,但这是另一个改进。
阿温
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.