题 夏令时和时区最佳实践[关闭]


我希望能够将这个问题及其答案作为处理夏令时的权威指南,特别是处理实际的变更问题。

如果您有任何要添加的内容,请执行此操作

许多系统依赖于保持准确的时间,问题在于由于夏令时而改变时间 - 向前或向后移动时钟。

例如,一个订单系统中的业务规则取决于订单的时间 - 如果时钟发生变化,规则可能不那么明确。如何保持订单的时间?当然有无数的场景 - 这只是一个说明性的场景。

  • 你是如何处理夏令时问题的?
  • 您的解决方案中有哪些假设? (在这里寻找背景)

同样重要的是,如果不是这样的话:

  • 你尝试了什么不起作用?
  • 为什么不起作用?

我会对编程,操作系统,数据持久性以及该问题的其他相关方面感兴趣。

一般答案很棒,但我也希望看到细节,特别是如果它们只在一个平台上可用。


1886


起源


@abatishchev - 这样 GETDATE() 在SQL上将是UTC(原样 DateTime.Now)。并且服务器不会受到任何类型的自动DST更改的影响。 - Oded
@Oded:我同意是否会添加“on server”。但这仍然会影响另一个需要当地时间的应用程序。由于这个原因和其他原因,我认为最好明确地请求Utc时间。 - abatishchev
UTC比GMT更受欢迎,因为它更精确地定义,并且因为GMT定义在某些操作系统上搞砸了。人们普遍认为“GMT”和“UTC”这两个术语是可以互换的,但它们并不完全相同。对于几乎所有软件/系统用途,请使用UTC。看到 stackoverflow.com/questions/2292334/... - Chris Johnson
@JoshStodola - Jon Skeet''回答”。 - Kenny Evitt
@Oded你不能假设服务器将在UTC上,我已经看到生产服务器,其中时区是“UTC”但是已经应用了DST,所以实际上是UTC + 1超过半年。同意@abatishchev明确并使用 DateTime.UtcNow 和 GETUTCDATE(),它还显示了您实际想到的其他开发者 - ono2012


答案:



答案和其他数据摘要:(请添加您的)

做:

  • 无论何时提到确切的时刻,都要按照不受夏令时影响的统一标准来保持时间。 (GMT和UTC在这方面是相同的,但最好使用术语UTC。请注意,UTC也称为 祖鲁 要么 ž 时间。)
  • 相反,如果您选择使用本地时间值保留时间,请包括UTC时此特定时间的本地时间偏移量(此偏移量可能会在一年中发生变化),以便稍后可以明确地解释时间戳。
  • 在某些情况下,您可能需要存储  UTC时间和当地时间等价物。通常这是通过两个单独的字段完成的,但有些平台支持a datetimeoffset 可以存储在单个字段中的类型。
  • 将时间戳记存储为数值时,请使用 Unix时间  - 从那以后的整秒数 1970-01-01T00:00:00Z (不包括闰秒)。如果需要更高的精度,请使用毫秒。此值应始终基于UTC,不进行任何时区调整。
  • 如果以后可能需要修改时间戳,请包含原始时区ID,以便确定偏移量是否与记录的原始值相比有所变化。
  • 在调度未来事件时,通常优先选择本地时间而不是UTC,因为偏移量通常会发生变化。 见答案,和 博客文章
  • 存储整个日期(例如生日和纪念日)时,请勿转换为UTC或任何其他时区。
    • 如果可能,请存储不包含一天中某个时间的仅限日期的数据类型。
    • 如果此类型不可用,请务必在解释值时始终忽略时间。如果您无法确定时间将被忽略,请选择中午12:00,而不是午夜00:00作为当天更安全的代表时间。
  • 请记住,时区偏移量并不总是整数小时数(例如,印度标准时间为UTC + 05:30,尼泊尔使用UTC + 05:45)。
  • 如果使用Java,请使用 java.time 对于Java 8,或使用 乔达时间 适用于Java 7或更低版​​本。
  • 如果使用 。净,考虑使用 野田时间
  • 如果使用没有Noda Time的.NET,请考虑这一点 DateTimeOffset 往往是比较好的选择 DateTime
  • 如果使用Perl,请使用 约会时间
  • 如果使用Python,请使用 pytz 要么 dateutil
  • 如果使用JavaScript,请使用 moment.js 随着 那一刻,时区 延期。
  • 如果使用PHP> 5.2,请使用提供的本机时区转换 DateTime,和 DateTimeZone 类。使用时要小心 DateTimeZone::listAbbreviations()  - 看到答案。要使PHP保持最新的Olson数据,请定期安装 timezonedb PECL包; 看到答案
  • 如果使用C ++,请务必使用正确实现的库 IANA时区数据库。这些包括 cctzICU,和 Howard Hinnant的“tz”图书馆
    • 不使用 促进 用于时区转换。而 它的API 声称支持标准IANA(又名“zoneinfo”)标识符 粗略地映射它们 对于POSIX风格的数据,不考虑每个区域可能具有的丰富变化历史。 (此外,该文件已不再用于维护。)
  • 大多数业务规则使用民用时间,而不是UTC或GMT。因此,计划在应用应用程序逻辑之前将UTC时间戳转换为本地时区。
  • 请记住,时区和偏移不是固定的,可能会改变。例如,历史上美国和英国使用相同的日期来“前进”和“后退”。然而,在2007年,美国改变了时钟变换的日期。这意味着,在一年中的48周,伦敦时间和纽约时间之间的差异是5小时和4周(春季3,秋季1),这是4小时。在涉及多个区域的任何计算中都要注意这样的项目。
  • 考虑时间类型(实际事件时间,广播时间,相对时间,历史时间,重复时间)您需要存储哪些元素(时间戳,时区偏移和时区名称)以便正确检索 - 请参阅“时间类型” 这个答案
  • 保持您的操作系统,数据库和应用程序tzdata文件在它们与世界其他地方之间保持同步。
  • 在服务器上,将硬件时钟和OS时钟设置为UTC而不是本地时区。
  • 无论前一个要点如何, 服务器端代码,包括网站,应该 决不 期望服务器的本地时区特别是任何东西。 看到答案
  • 更喜欢在应用程序代码中逐个使用时区,而不是通过配置文件设置或默认值全局使用。
  • 使用 NTP 所有服务器上的服务。
  • 如果使用 FAT32请记住,时间戳存储在本地时间,而不是UTC。
  • 在处理周期性事件(例如每周电视节目)时,请记住时间随着夏令时的变化而变化,并且在不同时区会有所不同。
  • 始终将日期时间值查询为 下限包含,上限独占 (>=<)。

别:

  • 不要混淆“时区”,例如 America/New_York 带有“时区偏移”,例如 -05:00。他们是两个不同的东西。看到 时区标签维基
  • 不要使用JavaScript Date 在旧的Web浏览器中执行日期和时间计算的对象,如ECMAScript 5.1及更低版本 设计缺陷 可能会错误地使用夏令时。 (这已在ECMAScript 6/2015中修复)。
  • 永远不要相信客户的时钟。这可能是不正确的。
  • 不要告诉人们“始终使用UTC”。这种广泛的建议是对本文档前面所述的几种有效方案的短视。而是对您正在使用的数据使用适当的时间参考。 (时间戳 可以使用UTC,但未来的时间安排和仅限日期的值不应该。)

测试:

  • 测试时,请确保您测试西部,东部,北部和西部的国家/地区 南部的 半球(事实上在全球的每个四分之一,所以4个地区),DST正在进行中而不是(给出8),以及一个不使用DST的国家(另外4个覆盖所有地区,共计12个)。
  • 测试DST的过渡,即当你在夏季时,从冬季选择一个时间值。
  • 使用DST测试边界情况,例如UTC + 12的时区,产生本地时间 夏季UTC + 13,冬季甚至是UTC + 13
  • 测试所有第三方库和应用程序,并确保它们正确处理时区数据。
  • 至少测试半小时的时区。

参考:

其他:

  • 大厅让你的代表结束美国夏令时的憎恶。我们总能希望......
  • 大堂 地球标准时间

803



使用GMT并没有真正解决问题,你还需要弄明白 什么 是适用的delta,这就是所有复杂性所在。在用于不同区域(具有不同夏令时切换时间表)的用户或显示历史/未来数据的系统中使用的系统中,增量可能很复杂。 - ckarras
如果所有应用程序需要做的是显示当前本地时间,那就是这样。但是,如果我们在澳大利亚有一台服务器需要在2006年4月2日在加拿大显示交易的日期/时间(这是加拿大夏令时切换规则发生变化之前的最后一年) - 您是否有OS调用?可以做DateTime(“2006/04/02 14:35Z”)。ToLocalTime()。WithDaylightSavingRules(“Canada”,2006)? - ckarras
是的,在Windows中存在这样的OS调用--SystemTimeToTzSpecificLocation。 msdn.microsoft.com/en-us/library/ms724949(VS.85).aspx - Michael Madsen
@tchrist也许如果你坚持不懈就可以。 - Peter Ajtai
请记住将未来日期(甚至是明天)转换为UTC 总是 失去一些东西例如,您使用了一些已知的偏移来进行转换,但由于日期是将来的,因此设置这些规则的组总是有可能更改这些规则。此外,几乎不可能将某些未来日期转换为UTC,因为直到那一年才知道夏令时(有些国家每年设定日期,而不是提前10年或基于任何设定规则)。 - eselk


我不确定我可以添加到上面的答案,但这里有几点我:

时代的类型

您应该考虑四种不同的时间:

  1. 活动时间:例如,国际体育赛事发生的时间,或加冕/死亡/等等。这取决于事件的时区而不是观众的时区。
  2. 电视时间:例如,特定的电视节目在当地时间晚上9点播出。在考虑在您的网站上发布结果(比如说“美国偶像”)时很重要
  3. 相对时间:例如:这个问题在21小时内有一个开放的赏金。这很容易显示
  4. 重复时间:例如:即使DST发生变化,电视节目也会在每个星期一晚上9点播出。

还有历史/交替时间。这些都很烦人,因为它们可能无法映射回标准时间。例如:朱利安日期,日期根据土星的农历,克林贡日历。

以UTC格式存储开始/结束时间戳的效果很好。对于1,您需要与事件一起存储的事件时区名称+偏移量。对于2,您需要为每个区域存储一个本地时间标识符和为每个查看器存储的本地时区名称+偏移量(如果您处于紧缩状态,则可以从IP中获取此值)。对于3,以UTC秒存储,不需要时区。 4是1或2的特殊情况,取决于它是全局事件还是本地事件,但您还需要存储一个 创建于 时间戳,以便您可以判断在创建此事件之前或之后是否更改了时区定义。如果您需要显示历史数据,这是必要的。

存放时间

  • 始终以UTC格式存储时间
  • 转换为显示的本地时间(本地由查看数据的用户定义)
  • 存储时区时,需要名称,时间戳和偏移量。这是必需的,因为政府有时会改变其时区的含义(例如:美国政府改变了DST日期),并且您的应用程序需要优雅地处理事情......例如:DST规则之前和之后LOST剧集的确切时间戳改变。

偏移和名称

以上的一个例子是:

足球世界杯决赛比赛   发生在南非(UTC + 2 - SAST)   于2010年7月11日19:00 UTC。

有了这些信息,我们可以历史地确定2010年WCS决赛的确切时间,即使南非时区定义发生变化,  能够在查询数据库时向本地时区的查看者显示该信息。

系统时间

您还需要使您的操作系统,数据库和应用程序tzdata文件保持同步,彼此之间以及与世界其他地方同步,并在升级时进行广泛测试。您依赖的第三方应用程序未正确处理TZ更改并非闻所未闻。

确保硬件时钟设置为UTC,如果您在世界各地运行服务器,请确保其操作系统也配置为使用UTC。当您需要从多个时区的服务器复制每小时轮换的apache日志文件时,这一点就变得很明显了。按文件名对它们进行排序仅在所有文件都使用相同的时区命名时才有效。这也意味着当您从一个框转到另一个框并且需要比较时间戳时,您不必在头脑中进行日期数学运算。

此外,在所有框上运行ntpd。

客户端

永远不要相信从客户端计算机获得的时间戳有效。例如,Date:HTTP标头或javascript Date.getTime() 呼叫。当用作不透明标识符时,或者在同一客户端上的单个会话期间进行日期数学运算时,这些都很好,但不要尝试将这些值与服务器上的内容交叉引用。您的客户端不运行NTP,并且可能不一定有可用于其BIOS时钟的电池。

琐事

最后,政府有时会做很奇怪的事情:

荷兰的标准时间是   正好是19分钟和32.13秒   1909年至2005年1月,法律提前通过UTC   通过1937-06-30。这个时区   无法准确表示使用   HH:MM格式。

好的,我想我已经完成了。


288



这个答案有一些很好的观点,我特别想指出这一部分“重复时间:例如:电视节目在每个星期一晚上9点,即使DST发生变化”在数据库中以UTC存储时间然后转换显示有助于处理处理时区的许多棘手问题。然而,处理跨越DST障碍的“反复发生的事件”变得更加困难。 - Jared Hales
为琐事+1。保持内容的有趣性使这项任务更加愉快,这可能会提高生产力:) - Chris
这个答案中有很多好东西,但有几个问题。 1)许多时区缩写都不明确。 看这里  2)不 总是 你想以UTC格式存储吗?大部分时间 - 是的,但背景很重要。用你的话来说, 电视时间 不会以UTC格式存储。此外,有时它的非法或反对政策不存储当地时间 - 这就是事情 DateTimeOffset (或同等的)派上用场。否则,好好写。 - Matt Johnson
荷兰的琐事看起来很合法。 ietf.org/rfc/rfc3339.txt 第5.8节。中途认为有人在拉我们的腿。 - ruffin
政府做奇怪事情的另一个例子: timeanddate.com/news/time/turkey-scraps-dst-2016.html - Ad Infinitum


这是一个重要且令人惊讶的棘手问题。事实上,没有完全令人满意的持久时间标准。例如,SQL标准和ISO格式(ISO 8601)显然是不够的。

从概念的角度来看,人们通常会处理两种类型的时间 - 数据,并且很容易区分它们(上述标准没有):“物理时间“和”民用时间”。

“物理”时刻是物理学处理的连续通用时间线中的一个点(当然,忽略相对论)。例如,这个概念可以用UTC充分编码 - 持久化(如果你可以忽略闰秒)。

“民用”时间是遵循民用规范的日期时间规范:此处的时间点由一组日期时间字段(Y,M,D,H,MM,S,FS)加上TZ(时区规范)完全指定(实际上也是“日历”;但我们假设我们将讨论限制在格里高利历)。时区和日历共同允许(原则上)从一个表示映射到另一个表示。但是,民用和物理时间瞬间是不同类型的大小,它们应该在概念上保持分离和区别对待(类比:字节数组和字符串)。

这个问题令人困惑,因为我们可以互换地谈论这些类型的事件,因为民事时代受政治变化的影响。对于未来的事件,问题(以及区分这些概念的必要性)变得更加明显。示例(取自我的讨论 这里

约翰在他的日历中记录了日期时某些事件的提醒 2019-Jul-27, 10:30:00,TZ =Chile/Santiago,(已抵消GMT-4, 因此它对应于UTC 2019-Jul-27 14:30:00)。但是有一天 在未来,该国决定将TZ抵消额改为GMT-5。

现在,当这一天到来时...那个提醒应该触发

一个) 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)的基础。


81



我在API中没有看到的另一个要点是,即使给出了时间和时区,对于例如时间和时区至少有两个合理的含义。 “13:00:00EDT”。它可能是指当地时间下午1点发生的事情,据信是东部夏令时,或者是东部时间到达13:00时发生的事情,据信是发生在观察东部夏令时的地方。如果有一个时区,时区信息可能有误(例如数码相机文件)...... - supercat
...知道它们是否反映了准确的当地时间或全球时间对于确定如何纠正它们可能很重要。 - supercat
显然约翰想要A)因为人们在8:30去上班,无论目前的DST或时区是什么。如果他们进入DST,他们将在8:30开始工作,当他们退出DST时,他们将在8:30开始工作。如果该国决定转移到另一个时区,他们将在8:30开始工作 - Gianluca Ghettini


明确关注点的架构分离 - 准确了解哪个层与用户交互,并且必须更改规范表示(UTC)的日期时间。 非UTC日期时间是演示文稿 (跟随用户当地时区), UTC时间是模型 (对于后端和中间层仍然是唯一的)。

也, 决定你的实际观众是什么,你不需要服务的是什么以及你在哪里画线。不要触摸异国情调的日历,除非您确实拥有重要的客户,然后考虑仅针对该区域的单独的面向用户的服务器。

如果您可以获取并维护用户的位置,请使用位置进行系统的日期时间转换(例如.NET文化或SQL表),但如果日期时间对您的用户至关重要,则为最终用户提供选择替代的方法。

如果有历史审计义务 涉及(就像2年前9月Jo在AZ支付账单一样) 然后保持UTC和当地时间 记录(您的转换表将随着时间的推移而变化)。

为批量生成的数据定义时间参照时区  - 像文件,网络服务等。说东海岸公司在CA有数据中心 - 你需要询问并知道他们使用什么作为标准,而不是假设其中一个。

不要相信嵌入在日期时间和文本表示中的时区偏移 不接受解析并遵循它们。 而是始终要求必须明确定义时区和/或参考区域。您可以轻松地通过PST偏移接收时间,但时间实际上是EST,因为这是客户端的参考时间,而记录只是在PST中的服务器上导出。


53





你需要了解奥尔森 tz数据库,可从 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,互联网号码分配机构,并在首页上有一个链接 '时区数据库'。讨论邮件列表现在是 tz@iana.org;公告清单是 tz-announce@iana.org


38



Olson数据库包含 历史的 数据也是如此,这使得它对处理DST特别有用。例如,它知道对应于2000年3月31日在美国的UTC值不是DST,而是对应于2008年3月31日在美国的UTC值。 - Josh Kelley
Unicode Consortium维护Olson tome区域ID和Windows时区ID之间的映射: unicode.org/repos/cldr-tmp/trunk/diff/supplemental/... - Josh Kelley
更新了Unicode Consortium页面的链接: unicode.org/repos/cldr-tmp/trunk/diff/supplemental/... - Span
哪里可以找到有关解析Olson文件的信息?我想将它编译成db表,但无法弄清楚我应该如何读取文件 - shealtiel
@gidireich:主要信息是与数据一起找到的,并不容易理解。它的主要文档是在 zic.8 手册页(或 zic.8.txt)。你需要的 tzcode2011i.tar.gz 文件而不是文件 tzdata2011i.tar.gz 获取此信息的文件。 - Jonathan Leffler


一般来说, 包括本地时间偏移(包括DST偏移) 在存储的时间戳中:如果您以后想要在其原始时区(和DST设置)中显示时间戳,则仅使用UTC是不够的。

请记住,偏移量并不总是整数小时(例如,印度标准时间是UTC + 05:30)。

例如,合适的格式是元组(unix时间,以分钟为单位的偏移量)或 ISO 8601


24



对于显示,偏移是完美的。如果要进行更改,则偏移量仍然不足。您还必须知道时区,因为新值可能需要不同的偏移量。 - Matt Johnson


越过“计算机时间”和“人们时间”的界限是一场噩梦。主要的一点是,对于时区和夏令时的规则没有任何标准。各国可以随时改变其时区和DST规则,而且他们也可以。

一些国家,以色列,巴西每年决定何时进行夏令时,因此无法提前知道(如果)DST何时生效。其他人对DST何时生效有固定(ish)规则。其他国家不使用夏令时。

时区不一定与格林尼治标准时间完全不同。尼泊尔是+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个)。


22



关于以色列 - 这是正确的。虽然DST更改的时间不是Julian日历中的特定日期 - 但是有一条基于希伯来日历的规则(2014年有变化)。无论如何,一般的想法是正确的 - 这些事情可能会偶尔发生变化 - Yaron U.
此外,AST =大西洋标准时间。至于“最好的建议”,它可以作为一个起点,但你需要阅读本页的其余部分,并说一点祷告,你可能会在一段时间内做到正确。 - DavidWalley


对于PHP:

PHP> 5.2中的DateTimeZone类已经基于其他人提到的Olson DB,因此如果您在PHP中而不是在DB中进行时区转换,则可以免除使用(难以理解的)Olson文件。

但是,PHP没有像Olson DB那样频繁更新,因此仅使用PHP时区转换可能会留下过时的DST信息,并影响数据的正确性。虽然预计不会经常发生这种情况,但可能会发生这种情况  如果您在全球拥有庞大的用户群,就会发生

要解决上述问题,请使用 timezonedb pecl包,这个功能是更新PHP的时区数据。按照更新的频率安装此软件包。 (我不确定此软件包更新是否完全遵循Olson更新,但它似乎更新的频率至少非常接近Olson更新。)


18





如果您的设计可以容纳它, 一起避免本地时间转换! 

我知道有些人可能听起来很疯狂,但想想用户体验:用户处理的时间近似,相对日期(今天,昨天,下周一)比绝对日期(2010.09.17,周五9月17日)更快。当你考虑更多时,时区(和DST)的准确性越接近日期就越重要 now()因此,如果您可以以相对格式表示日期/日期时间+/- 1或2周,则其余日期可以是UTC,对95%的用户来说无关紧要。

这样,您可以以UTC格式存储所有日期,并以UTC格式进行相对比较,只显示相对日期阈值之外的用户UTC日期。

这也可以应用于用户输入(但通常以更有限的方式)。从只有{昨天,今天,明天,下周一,下周四}的下拉菜单中选择对于用户来说比日期选择器更简单,更容易。采摘日期是形式填充最痛苦的组成部分。当然,这对所有情况都不适用,但你可以看到它只需要一点巧妙的设计就能让它变得非常强大。


15



+1有趣的想法。 UTC可以是一件美丽的事; o) - Jon Onstott