我希望能够将这个问题及其答案作为处理夏令时的权威指南,尤其是处理实际的变更问题。
如果您有任何要添加的内容,请执行
许多系统依赖于保持准确的时间,问题在于由于夏令时而改变时间 - 向前或向后移动时钟。
例如,一个订单系统中的业务规则取决于订单的时间 - 如果时钟发生变化,规则可能不那么明确。如何保持订单的时间?当然有无数的场景 - 这只是一个说明性的场景。
同样重要的是,如果不是这样的话:
我对编程,操作系统,数据持久性以及该问题的其他相关方面感兴趣。
一般答案很棒,但我也希望看到详细信息,特别是如果它们仅在一个平台上可用。
答案 0 :(得分:868)
答案 1 :(得分:304)
我不确定我能在上面的答案中添加什么,但这里有几点:
您应该考虑四种不同的时间:
还有历史/交替时间。这些都很烦人,因为它们可能无法映射回标准时间。例如:朱利安日期,日期根据土星的农历,克林贡日历。
以UTC格式存储开始/结束时间戳的效果很好。对于1,您需要与事件一起存储的事件时区名称+偏移量。对于2,您需要为每个区域存储本地时间标识符,并为每个查看器存储本地时区名称+偏移量(如果您处于紧缩状态,则可以从IP中获取此值)。对于3,以UTC秒存储,不需要时区。 4是1或2的特殊情况,具体取决于它是全局事件还是本地事件,但您还需要存储在时间戳创建的,以便您可以判断时区定义是在此之前还是之后更改事件已创建。如果您需要显示历史数据,这是必要的。
以上的一个例子是:
足球世界杯决赛比赛 发生在南非(UTC + 2 - SAST) 于2010年7月11日19:00 UTC。
根据这些信息,我们可以在历史上确定2010年WCS决赛发生的确切时间,即使南非时区定义发生变化,和能够在当地时区向观众展示他们查询数据库的时间。
您还需要使您的操作系统,数据库和应用程序tzdata文件保持同步,彼此之间以及与世界其他地方同步,并在升级时进行广泛测试。您所依赖的第三方应用程序未正确处理TZ更改并非闻所未闻。
确保硬件时钟设置为UTC,如果您在世界各地运行服务器,请确保其操作系统也配置为使用UTC。当您需要从多个时区的服务器复制每小时轮换的apache日志文件时,这一点就变得很明显了。按文件名对它们进行排序仅在所有文件都使用相同的时区命名时才有效。这也意味着当你从一个盒子ssh到另一个盒子并且需要比较时间戳时,你不必在头脑中做日期数学。
此外,在所有方框上运行ntpd。
永远不要相信从客户端计算机获得的时间戳有效。例如,Date:HTTP标头或javascript Date.getTime()
调用。当用作不透明标识符时,或者在同一客户端上的单个会话期间进行日期数学运算时,这些都很好,但不要尝试将这些值与服务器上的内容交叉引用。您的客户端不运行NTP,并且可能不一定有可用于其BIOS时钟的电池。
最后,政府有时会做一些非常奇怪的事情:
荷兰的标准时间是 正好19分32.13秒 从1909年至2005年1月,法律面前的UTC 通过1937-06-30。这个时区 无法准确表示使用 HH:MM格式。
好的,我想我已经完成了。
答案 2 :(得分:85)
这是一个重要且令人惊讶的棘手问题。事实是,没有完全令人满意的标准来坚持时间。例如,SQL标准和ISO格式(ISO 8601)显然是不够的。
从概念的角度来看,通常会处理两种类型的时间 - 日期数据,并且可以方便地区分它们(上述标准没有):“物理时间”和“<强>民间时间“。
“物理”时刻是物理学处理的连续通用时间线中的一个点(当然,忽略相对论)。例如,这个概念可以用UTC充分编码 - 持久化(如果你可以忽略闰秒)。
“民用”时间是遵循民用规范的日期时间规范:此处的时间点由一组日期时间字段(Y,M,D,H,MM,S,FS)加上TZ完全指定(时区规范)(实际上也是一个“日历”;但我们假设我们将讨论限制在格里高利历中)。时区和日历共同允许(原则上)从一个表示映射到另一个表示。但是,民事和物理时间的基本上是不同类型的大小,它们应该在概念上保持分离和区别对待(类比:字节数组和字符串)。
这个问题令人困惑,因为我们可以互换地谈论这些类型的事件,因为民事时代受政治变化的影响。对于未来的事件,问题(以及区分这些概念的必要性)变得更加明显。示例(取自我的讨论here。
John在他的日历中记录了某个日期时间事件的提醒
2019-Jul-27, 10:30:00
,TZ = Chile/Santiago
,(已抵消GMT-4,
因此它对应于UTC 2019-Jul-27 14:30:00
)。但是有一天
在未来,该国决定将TZ抵消额改为GMT-5。
现在,当这一天到来时......该提醒会在
触发 A)2019-Jul-27 10:30:00 Chile/Santiago
= UTC time 2019-Jul-27 15:30:00
?
或
B)2019-Jul-27 9:30:00 Chile/Santiago
= UTC time 2019-Jul-27 14:30:00
?
没有正确答案,除非你知道约翰在概念上的含义
当他告诉日历“请在2019-Jul-27, 10:30:00
TZ=Chile/Santiago
”给我打电话。
他的意思是“民事约会时间”(“我所在城市的时钟告诉我的时候” 10:30“)?在这种情况下,A)是正确答案。
或者他的意思是“物理瞬间的时间”,这是连续体中的一个点 我们宇宙的时间线,比如说,“下次日食时 在这种情况下,回答B)是正确的。
一些日期/时间API正确地区分了它们:Jodatime,它是下一个(第三个!)Java DateTime API(JSR 310)的基础。
答案 3 :(得分:55)
明确关注点的架构分离 - 准确了解哪个层与用户交互,并且必须更改规范表示(UTC)的日期时间。 非UTC日期时间是演示文稿(跟随用户本地时区), UTC时间是模型(对于后端和中间层保持唯一)。
此外,决定您的实际受众群体,您不必投放的内容以及您在哪里划线。除非您确实在那里拥有重要客户,否则请不要触摸异国情调的日历,然后考虑仅针对该区域的单独的面向用户的服务器。
如果您可以获取并维护用户的位置,请使用位置进行系统日期时间转换(例如.NET文化或SQL表),但如果日期时间对您的用户至关重要,则为最终用户提供选择覆盖的方法
如果涉及历史审计义务(比如确切知道2月份2月前Jo在AZ支付账单的时间)那么请保留UTC和当地时间记录(您的转换表会在一段时间内发生变化)。
为批量生成的数据定义时间参照时区 - 比如文件,网络服务等。假设East Coast公司在CA中有数据中心 - 您需要询问并知道他们使用的是什么标准,而不是假设一个或另一个。
不信任嵌入在日期时间的文本表示中的时区偏移,不接受解析和跟踪它们。 而是始终要求必须明确定义时区和/或参考区。您可以轻松地通过PST偏移接收时间,但时间实际上是EST,因为这是客户端的参考时间,而记录只是在PST中的服务器上导出。
答案 4 :(得分:39)
您需要了解Olson tz database,可以从 ftp://elsie.nci.nih.gov/pub http://iana.org/time-zones/获取。它每年更新多次,以处理世界各地不同国家在冬季和夏季(标准和夏令时)之间切换的时间(以及是否)的最后时刻变化。 2009年,最后一个版本是2009年;在2010年,它是2010n;在2011年,它是2011n;截至2012年5月底,发布时间为2012c。请注意,有一组代码可以在两个单独的存档(tzcode20xxy.tar.gz和tzdata20xxy.tar.gz)中管理数据和实际时区数据本身。代码和数据都属于公共领域。
这是时区名称的来源,例如America / Los_Angeles(以及美国/太平洋等同义词)。
如果您需要跟踪不同的区域,那么您需要Olson数据库。正如其他人所建议的那样,您还希望以固定格式存储数据 - 通常是选择的UTC - 以及生成数据的时区记录。您可能希望区分当时与UTC的偏移量和时区名称;这可以在以后产生影响。此外,知道它目前是2010-03-28T23:47:00-07:00(美国/太平洋)可能会或可能不会帮助您解释2010-11-15T12:30的值 - 这可能是在PST中指定的(太平洋标准时间而非PDT(太平洋夏令时)。
标准的C库接口对这类东西并没有太大的帮助。
奥尔森数据已经发生变化,部分原因是因为A D Olson即将退休,部分原因是因为版权侵权的维护者有一项(现已被驳回)法律诉讼。现在,时区数据库是在互联网号码分配机构IANA的主持下进行管理的,首页上有'Time Zone Database'的链接。讨论邮件列表现在是tz@iana.org
;公告列表为tz-announce@iana.org
。
答案 5 :(得分:25)
通常,在存储的时间戳中包括本地时间偏移(包括DST偏移):如果您以后想要在其原始时区(和DST设置)中显示时间戳,则仅使用UTC是不够的。
请记住,偏移量并不总是整数小时(例如印度标准时间为UTC + 05:30)。
例如,合适的格式是元组(unix时间,以分钟为单位的偏移量)或ISO 8601。
答案 6 :(得分:23)
跨越“计算机时间”和“人们时间”的界限是一场噩梦。主要的一点是,时区和夏令时的规则没有任何标准。各国可随时自由改变其时区和DST规则,而且确实如此。
某些国家,例如以色列,巴西,每年决定何时进行夏令时,因此无法提前知道(如果)DST何时生效。其他人有关于DST何时生效的固定(ish)规则。其他国家/地区不使用DST。
时区不一定与格林尼治标准时间完全不同。尼泊尔是+5.45。甚至还有时区为+13。这意味着:
SUN 23:00 in Howland Island (-12)
MON 11:00 GMT
TUE 00:00 in Tonga (+13)
都是同一时间,但却有3天不同!
时区的缩写也没有明确的标准,以及它们在DST时如何变化,所以你最终会得到这样的结果:
AST Arab Standard Time UTC+03
AST Arabian Standard Time UTC+04
AST Arabic Standard Time UTC+03
最好的建议是尽可能远离当地时间,尽可能坚持使用UTC。只在最后一刻转换为当地时间。
测试时,请确保您测试西半球和东半球的国家/地区是否正在进行DST,以及未使用DST的国家/地区(总共6个)。
答案 7 :(得分:19)
对于PHP:
PHP中的DateTimeZone类&gt; 5.2已经基于其他人提到的Olson DB,因此如果您在PHP中而不是在DB中进行时区转换,则可以免除使用(难以理解的)Olson文件。
但是,PHP没有像Olson DB那样频繁更新,因此仅使用PHP时区转换可能会留下过时的DST信息,并影响数据的正确性。虽然预计这种情况不会频繁发生,但如果您在全球拥有大量用户,则可能会发生 。
要解决上述问题,请使用timezonedb pecl package,其功能是更新PHP的时区数据。按照更新的频率安装此软件包。 (我不确定此软件包更新是否完全符合Olson更新,但似乎更新的频率至少非常接近Olson更新。)
答案 8 :(得分:16)
如果您的设计可以容纳它,一起避免本地时间转换!
我知道一些人可能听起来很疯狂,但想想用户体验:用户处理近,相对日期(今天,昨天,下周一)的速度比绝对日期(2010.09.17,周一9月17日)更快。当你考虑更多时,时区(和DST)的准确性越接近now()
,所以如果你能用相对格式表示日期/日期时间为+/- 1或2几个星期,其余的日期可以是UTC,对95%的用户来说都不会太重要。
这样您就可以以UTC格式存储所有日期并以UTC进行相对比较,只显示相对日期阈值之外的用户UTC日期。
这也可以应用于用户输入(但通常以更有限的方式)。从只有{昨天,今天,明天,下周一,下周四}的下拉菜单中选择对于用户来说比日期选择器更简单,更容易。采摘日期是形式填充最痛苦的组成部分。当然这对所有情况都不适用,但你可以看到只需要一点巧妙的设计就可以使它变得非常强大。
答案 9 :(得分:16)
虽然我没有尝试过,但我觉得引人注目的时区调整方法如下:
以UTC格式存储所有内容。
创建一个包含三列的表TZOffsets
:RegionClassId,StartDateTime和OffsetMinutes(int,以分钟为单位)。
在表格中,存储当当地时间发生变化时的日期和时间列表,以及多少。表中的区域数量和日期数量取决于您需要支持的日期和地区范围。可以把它想象成是“历史”日期,即使日期应该包括未来的实际限制。
当您需要计算任何UTC时间的本地时间时,只需执行以下操作:
SELECT DATEADD('m', SUM(OffsetMinutes), @inputdatetime) AS LocalDateTime
FROM TZOffsets
WHERE StartDateTime <= @inputdatetime
AND RegionClassId = @RegionClassId;
您可能希望在应用程序中缓存此表,并使用LINQ或类似方法执行查询而不是访问数据库。
此数据可以从public domain tz database中提取。
这种方法的优点和脚注:
现在,这种方法或任何其他的唯一缺点是从本地时间到GMT的转换并不完美,因为任何具有负偏移的DST更改都是时钟重复给定的本地时间。我担心,没有简单的方法可以解决这个问题,这是存储当地时间的一个原因,这首先是坏消息。
答案 10 :(得分:14)
我已经在两种类型的系统上实现了这一点,“轮班规划系统(例如工厂工人)”和“气体依赖管理系统”...
23小时和25小时长的日子是一个痛苦应对,8小时轮班需要7小时或9小时。问题是你会发现每个客户,甚至是客户的部门都有不同的规则(通常没有记录)他们在这些特殊情况下做了什么。在他们为您的“现成”软件付款之前,最好不要询问客户的一些问题。在购买软件时,很难找到预先考虑此类问题的客户。
我认为在所有情况下,您都应该以UTC记录时间并在存储日期/时间之前转换为/从本地时间转换。但是,即使知道在给定时间内使用夏令时和时区也会很困难。
答案 11 :(得分:12)
对于在服务器上运行的应用程序(包括网站和其他后端服务),应用程序应忽略服务器的时区设置。
常见的建议是将服务器的时区设置为UTC。这确实是一个很好的最佳实践,但它可以作为不遵循其他最佳实践的应用程序的创可贴。例如,服务可能正在写入具有本地时间戳而不是基于UTC的时间戳的日志文件,从而在夏令时回退过渡期间产生歧义。将服务器的时区设置为UTC将修复该应用程序。然而,真正的解决方案是应用程序使用UTC开始记录。
服务器端代码(包括网站)永远不会期望服务器的本地时区特别适合。
在某些语言中,本地时区很容易进入应用程序代码。例如,.NET中的DateTime.ToUniversalTime
方法将从本地时区转换为UTC,DateTime.Now
属性返回本地时区的当前时间。此外,JavaScript中的Date
构造函数使用计算机的本地时区。还有很多这样的例子。执行防御性编程非常重要,避免使用计算机本地时区设置的任何代码。
使用本地时区保留客户端代码,例如桌面应用程序,移动应用程序和客户端JavaScript。
答案 12 :(得分:12)
我最近在一个Web应用程序中遇到了一个问题,在Ajax回发时,返回到服务器端代码的日期时间与提供的日期时间不同。
它很可能与客户端上的JavaScript代码有关,该代码构建了以字符串形式发回客户端的日期,因为JavaScript正在调整时区和夏令时,并且在某些浏览器中计算何时为应用夏令时似乎与其他人不同。
最后,我选择完全删除客户端上的日期和时间计算,然后使用整数键将其发回给我的服务器,然后将其转换为服务器上的日期时间,以实现一致的转换。
我从中学习: 不要在Web应用程序中使用JavaScript日期和时间计算,除非您绝对必须这样做。
答案 13 :(得分:11)
对于网络来说,规则并不复杂......
其余的只是使用服务器端日期时间库的UTC /本地转换。很高兴......
答案 14 :(得分:10)
将您的服务器设置为UTC,并确保它们都配置为ntp或等效服务器。
UTC避免了夏令时问题,而不同步的服务器可能会导致需要一段时间诊断的不可预测的结果。
答案 15 :(得分:9)
在处理存储在FAT32文件系统中的时间戳时要小心 - 它始终保存在本地时间坐标中(包括DST - 请参阅msdn article)。烧了那个。
答案 16 :(得分:7)
另一件事,确保服务器应用了最新的夏令时补丁。
去年我们遇到过这样的情况,即使我们使用的是基于UTC的系统,我们的时间一直在北美用户的三周时间内持续一小时。
最终它是服务器。他们只需要应用最新的补丁(Windows Server 2003)。
答案 17 :(得分:6)
DateTimeZone::listAbbreviations()
输出这个PHP方法返回一个包含一些“主要”时区(如CEST)的关联数组,它们本身包含更具体的“地理”时区(如欧洲/阿姆斯特丹)。
如果您正在使用这些时区及其偏移/ DST信息,那么实现以下内容非常重要:
似乎包含了每个时区的所有不同偏移/ DST配置(包括历史配置)!
例如,欧洲/阿姆斯特丹在此功能的输出中可以找到六次。两次出现(抵消1172/4772)是阿姆斯特丹时间,直到1937年;两个(1200/4800)是1937年至1940年间使用的时间;两个(3600/4800)是自1940年以来使用的时间。
因此,您不能依赖此函数返回的偏移/ DST信息当前正确/正在使用中!
如果你想知道某个时区的当前偏移/ DST,你必须做这样的事情:
<?php
$now = new DateTime(null, new DateTimeZone('Europe/Amsterdam'));
echo $now->getOffset();
?>
答案 18 :(得分:5)
业务规则应始终适用于civil time(除非有立法另有说明)。请注意,民事时间是一团糟,但这是人们使用的,所以这才是最重要的。
在内部,将时间戳保存在类似于民时的秒钟中。 epoch并不重要(我赞成Unix epoch)但它确实比替代方案更容易。假设leap-seconds不存在,除非您正在做真正需要它们的事情(例如,卫星跟踪)。时间戳和显示时间之间的映射是应该应用DST规则的唯一点;规则经常变化(在全球范围内,一年几次;责怪政治家)所以你应该确保你不对映射进行硬编码。奥尔森的TZ database是非常宝贵的。
答案 19 :(得分:5)
如果您碰巧维护使用DST活动的数据库系统,请仔细检查是否需要在秋季过渡期间关闭它们。 Mandy DBS(或其他系统)也不喜欢在(本地)时间两次传递相同的点,这正是你在秋季转回时钟时发生的事情。 SAP通过(恕我直言,非常整洁)的解决方案解决了这个问题 - 他们只是让内部时钟以正常速度的一半运行两个小时......
答案 20 :(得分:4)
您使用的是 .NET框架吗? 如果是这样,让我向您介绍{3. 3}}类型,添加.NET 3.5。
此结构包含DateTime
和偏移量(TimeSpan
),用于指定DateTimeOffset
实例的日期和时间与协调世界时(UTC)之间的差异
DateTimeOffset.Now
静态方法将返回DateTimeOffset
由当前(本地)时间和本地偏移量组成的实例
(如操作系统的区域信息中所定义)。
DateTimeOffset.UtcNow
静态方法将返回一个
DateTimeOffset
实例,包含当前UTC时间(如
如果你在格林威治)。
其他有用的类型是DateTimeOffset和TimeZone类。
答案 21 :(得分:4)
对于那些在.NET上遇到此问题的人,请查看使用DateTimeOffset
和/或TimeZoneInfo
是否值得您这么做。
如果您想使用IANA/Olson time zones,或者发现内置类型不足以满足您的需求,请查看Noda Time,它为.NET提供了更智能的日期和时间API。
答案 22 :(得分:2)
只是想指出两件似乎不准确或至少令人困惑的事情:
始终按照统一标准保持时间 受夏令时影响。 GMT和UTC已被提及 不同的人,虽然UTC似乎最常提到。
对于(几乎)所有实际计算目的,UTC实际上是GMT。除非你看到一个小数秒的时间戳,否则你正在处理GMT,这使得这种区别变得多余。
按原样包括本地时间偏移(包括DST偏移) 存储时间戳。
时间戳始终以GMT表示,因此没有偏移量。
答案 23 :(得分:2)
答案 24 :(得分:2)
以下是我的经验: -
(不需要任何第三方库)
答案 25 :(得分:1)
只是一个例子来证明处理时间是描述的巨大混乱,并且你永远不会自满。在这个页面的几个点上,闰秒被忽略了。
几年前,Android操作系统使用GPS卫星获取UTC时间参考,但忽略了GPS卫星不使用闰秒这一事实。直到新年前夜出现混乱时才有人注意到,当时苹果手机用户和Android手机用户的间隔时间相差约15秒。
我认为它已被修复,但你永远都不会知道这些“小细节”什么时候会回来困扰你。
答案 26 :(得分:1)
struct tm
。¹struct tm
的某些实现会存储UTC偏移量,但这并没有使其成为标准。
答案 27 :(得分:1)
绝不仅仅依赖
之类的构造函数 new DateTime(int year, int month, int day, int hour, int minute, TimeZone timezone)
由于DST,当某个日期时间不存在时,他们可以抛出异常。相反,构建自己的方法来创建此类日期。在其中,捕获由于DST而发生的任何异常,并使用转换偏移调整所需的时间。根据时区,夏令时可能发生在不同日期和不同时间(即使是巴西的午夜)。
答案 28 :(得分:1)
实际上,kernel32.dll不会导出SystemTimeToTzSpecificLocation。但它会输出以下两个:SystemTimeToTzSpecificLocalTime和TzSpecificLocalTimeToSystemTime ...
答案 29 :(得分:-4)
在处理数据库时(特别是MySQL,但这适用于大多数数据库),我发现很难存储UTC。
我发现在数据库中存储服务器日期时间更容易,然后让数据库将存储的日期时间转换回SQL语句中的UTC(即UNIX_TIMESTAMP())。之后,您可以在代码中使用日期时间作为UTC。
如果您100%控制服务器和所有代码,则最好将服务器时区更改为UTC。