2134

我希望使这个问题及其答案成为处理夏令时的权威指南,特别是处理实际的转换。

如果你有什么要补充的,请做

许多系统都依赖于保持准确的时间,问题在于由于夏令时导致的时间变化——向前或向后移动时钟。

例如,在接单系统中具有取决于订单时间的业务规则——如果时钟发生变化,则规则可能不那么清晰。订单的时间应该如何坚持?当然有无数种场景——这只是一个说明性的场景。

  • 你是如何处理夏令时问题的?
  • 哪些假设是您的解决方案的一部分?(在这里寻找上下文)

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

  • 你试过什么没用?
  • 为什么它不起作用?

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

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

4

30 回答 30

1028

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

做:

  • 每当您指的是确切的时间点时,请根据不受夏令时影响的统一标准坚持时间。(GMT 和 UTC 在这方面是等价的,但最好使用 UTC 一词。请注意,UTC 也称为ZuluZ时间。)
  • 相反,如果您选择使用本地时间值保留(过去)时间,请包括此特定时间与 UTC 的本地时间偏移量(此偏移量可能在全年发生变化),以便以后可以明确地解释时间戳。
  • 在某些情况下,您可能需要存储UTC 时间和等效的本地时间通常这是使用两个单独的字段来完成的,但有些平台支持一种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.time功能都在ThreeTen-Backport库中向后移植到 Java 6 和 7。
  • 在ThreeTenABP库中进一步适用于早期 Android (< 26) 。
  • 这些项目正式取代了古老的Joda-Time,现在处于维护模式。Joda-Time、ThreeTen-Backport、ThreeTen-Extrajava.time类和JSR 310由同一个人Stephen Colebourne领导。
  • 如果使用.NET,请考虑使用Noda Time
  • 如果在没有 Noda Time 的情况下使用 .NET,则认为这DateTimeOffset通常是比 .NET 更好的选择DateTime
  • 如果使用 Perl,请使用DateTime
  • 如果使用 Python 3.9 或更高版本,请使用内置的zoneinfo来处理时区。否则,使用dateutilarrow。通常可以避免使用较旧的pytz库。
  • 如果使用 JavaScript,请避免使用较旧的moment.jsmoment-timezone库,因为它们不再被积极维护。有关更多详细信息,请参阅Moment.js 项目状态。相反,请考虑Luxondate- fns 、day.jsjs-joda
  • 如果使用 PHP > 5.2,请使用DateTime, 和DateTimeZone类提供的本地时区转换。使用时要小心DateTimeZone::listAbbreviations()——见答案。为了让 PHP 保持最新的 Olson 数据,请定期安装timezonedb PECL 包;见答案
  • 如果使用 C++,请务必使用正确实现IANA 时区数据库的库。其中包括cctzICUHoward Hinnant 的“tz”库。在 C++20 中,后者被采用到标准<chrono>库中。
    • 不要使用Boost进行时区转换。虽然它的 API声称支持标准 IANA(又名“zoneinfo”)标识符,但它粗略地将它们映射到 POSIX 样式的数据,而不考虑每个区域可能具有的丰富更改历史。(此外,该文件已停止维护。)
  • 如果使用 Rust,请使用chrono
  • 大多数业务规则使用民用时间,而不是 UTC 或 GMT。因此,计划在应用应用程序逻辑之前将 UTC 时间戳转换为本地时区。
  • 请记住,时区和偏移量不是固定的,可能会发生变化。例如,从历史上看,美国和英国使用相同的日期来“前进”和“后退”。然而,在 2007 年,美国更改了时钟更改的日期。现在这意味着一年中的 48 周伦敦时间和纽约时间之间的差异是 5 小时,而 4 周(春季 3 周,秋季 1 周)是 4 小时。在涉及多个区域的任何计算中,请注意此类项目。
  • 考虑时间类型(实际事件时间、广播时间、相对时间、历史时间、重复时间)您需要存储哪些元素(时间戳、时区偏移和时区名称)以进行正确检索 - 请参阅“时间类型”中这个答案
  • 使您的操作系统、数据库和应用程序 tzdata 文件在它们自己和世界其他地方之间保持同步。
  • 在服务器上,将硬件时钟和操作系统时钟设置为 UTC 而不是本地时区。
  • 不管前面的要点如何,服务器端代码,包括网站,都不应该期望服务器的本地时区是特定的。 见答案
  • 更喜欢在应用程序代码中根据具体情况使用时区,而不是通过配置文件设置或默认值全局使用。
  • 在所有服务器上使用NTP服务。
  • 如果使用FAT32,请记住时间戳存储在本地时间,而不是 UTC。
  • 在处理重复事件(例如每周电视节目)时,请记住时间会随着 DST 的变化而变化,并且会因时区而异。
  • 始终将日期时间值查询为包含下限、上限不包含( >=, <)。

不:

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

测试:

  • 测试时,确保您测试的是西半球、东半球、北半球和南半球的国家/地区实际上是全球每个季度,所以 4 个区域),DST 正在进行和未进行(给出 8),以及一个国家不使用 DST(另外 4 个覆盖所有区域,总共 12 个)。
  • 测试夏令时的转换,即当您当前处于夏令时时,从冬季中选择一个时间值。
  • 使用 DST 测试边界情况,例如 UTC+12 的时区,使本地时间在夏季为 UTC+13,甚至在冬季为 UTC+13 的地方
  • 测试所有第三方库和应用程序,确保它们正确处理时区数据。
  • 至少测试半小时的时区。

参考:

其他:

  • 游说您的代表结束 DST 的可憎行为。我们总能希望...
  • 地球标准时间大厅
于 2010-03-28T13:01:44.273 回答
336

I'm not sure what I can add to the answers above, but here are a few points from me:

Types of times

There are four different times you should consider:

  1. Event time: eg, the time when an international sporting event happens, or a coronation/death/etc. This is dependent on the timezone of the event and not of the viewer.
  2. Television time: eg, a particular TV show is broadcast at 9pm local time all around the world. Important when thinking about publishing the results (of say American Idol) on your website
  3. Relative time: eg: This question has an open bounty closing in 21 hours. This is easy to display
  4. Recurring time: eg: A TV show is on every Monday at 9pm, even when DST changes.

There is also Historic/alternate time. These are annoying because they may not map back to standard time. Eg: Julian dates, dates according to a Lunar calendar on Saturn, The Klingon calendar.

Storing start/end timestamps in UTC works well. For 1, you need an event timezone name + offset stored along with the event. For 2, you need a local time identifier stored with each region and a local timezone name + offset stored for every viewer (it's possible to derive this from the IP if you're in a crunch). For 3, store in UTC seconds and no need for timezones. 4 is a special case of 1 or 2 depending on whether it's a global or a local event, but you also need to store a created at timestamp so you can tell if a timezone definition changed before or after this event was created. This is necessary if you need to show historic data.

Storing times

  • Always store time in UTC
  • Convert to local time on display (local being defined by the user looking at the data)
  • When storing a timezone, you need the name, timestamp and the offset. This is required because governments sometimes change the meanings of their timezones (eg: the US govt changed DST dates), and your application needs to handle things gracefully... eg: The exact timestamp when episodes of LOST showed both before and after DST rules changed.

Offsets and names

An example of the above would be:

The soccer world cup finals game happened in South Africa (UTC+2--SAST) on July 11, 2010 at 19:00 UTC.

With this information, we can historically determine the exact time when the 2010 WCS finals took place even if the South African timezone definition changes, and be able to display that to viewers in their local timezone at the time when they query the database.

System Time

You also need to keep your OS, database and application tzdata files in sync, both with each other, and with the rest of the world, and test extensively when you upgrade. It's not unheard of that a third party app that you depend on did not handle a TZ change correctly.

Make sure hardware clocks are set to UTC, and if you're running servers around the world, make sure their OSes are configured to use UTC as well. This becomes apparent when you need to copy hourly rotated apache log files from servers in multiple timezones. Sorting them by filename only works if all files are named with the same timezone. It also means that you don't have to do date math in your head when you ssh from one box to another and need to compare timestamps.

Also, run ntpd on all boxes.

Clients

Never trust the timestamp you get from a client machine as valid. For example, the Date: HTTP headers, or a javascript Date.getTime() call. These are fine when used as opaque identifiers, or when doing date math during a single session on the same client, but don't try to cross-reference these values with something you have on the server. Your clients don't run NTP, and may not necessarily have a working battery for their BIOS clock.

Trivia

Finally, governments will sometimes do very weird things:

Standard time in the Netherlands was exactly 19 minutes and 32.13 seconds ahead of UTC by law from 1909-05-01 through 1937-06-30. This time zone cannot be represented exactly using the HH:MM format.

Ok, I think I'm done.

于 2010-07-16T22:40:29.303 回答
91

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

从概念上看,通常处理两种时间-日期数据,便于区分(上述标准没有):“物理时间”和“民用时间”。

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

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

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

John 在他的日历中记录了 datetime 某个事件的提醒 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) 的基础。

于 2010-03-28T15:17:33.667 回答
63

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

另外,决定你的实际受众是什么,你不需要服务什么以及你在哪里划清界限。除非您确实在那里有重要客户,否则不要触摸异国情调的日历,然后考虑仅针对该地区使用单独的面向用户的服务器。

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

如果涉及历史审计义务(例如准确告诉 AZ 的 Jo 2 年前 9 月何时支付账单),请保留 UTC 和本地时间作为记录(您的转换表会随着时间的推移而改变)。

为大量传入的数据(如文件、Web 服务等)定义时间参考时区。假设东海岸公司在 CA 有数据中心 - 您需要询问并知道他们使用什么作为标准,而不是假设其中一个。

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

于 2010-08-04T10:52:41.817 回答
41

您需要了解 Olson 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(以及同义词,例如 US/Pacific)。

如果您需要跟踪不同的区域,那么您需要 Olson 数据库。正如其他人建议的那样,您还希望以固定格式存储数据(通常选择 UTC)以及生成数据的时区记录。您可能想要区分当时与 UTC 的偏移量和时区名称;这可以在以后有所作为。此外,知道当前是 2010-03-28T23:47:00-07:00 (美国/太平洋)可能会或可能不会帮助您解释值 2010-11-15T12:30 ——这可能在 PST 中指定(太平洋标准时间)而不是 PDT(太平洋夏令时间)。

标准 C 库接口对这类东西没有太大帮助。


Olson 数据已经移动,部分原因是 AD Olson 将很快退休,部分原因是有一项(现已驳回)针对维护者侵犯版权的法律诉讼。时区数据库现在由互联网号码分配机构IANA管理,首页上有一个指向“时区数据库”的链接。讨论邮件列表现在是tz@iana.org;公告列表是tz-announce@iana.org

于 2010-03-29T06:50:15.260 回答
27

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

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

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

于 2010-03-28T14:59:06.357 回答
24

跨越“计算机时间”和“人的时间”的界限是一场噩梦。主要的一点是,管理时区和夏令时的规则没有任何标准。国家/地区可以随时更改其时区和 DST 规则,而且确实如此。

以色列、巴西等一些国家每年都决定何时实行夏令时,因此无法提前知道(是否)夏令时生效。其他人有关于 DST 何时生效的固定(ish)规则。其他国家根本不使用 DST。

时区不必与 GMT 相差整小时。尼泊尔是+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 个)。

于 2010-03-29T14:02:26.577 回答
22

对于 PHP:

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

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

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

于 2011-09-07T18:10:16.453 回答
20

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

我知道对某些人来说这听起来很疯狂,但想想 UX:用户一目了然地处理较近的相对日期(今天、昨天、下周一)比绝对日期(2010.09.17,星期五 9 月 17 日)快。而且当您考虑更多时,时区(和 DST)的准确性越接近 date 越重要now(),因此如果您可以以相对格式表示日期/日期时间 +/- 1 或 2 周,其余的日期可以是 UTC,这对 95% 的用户来说并不重要。

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

这也可以应用于用户输入(但通常以更有限的方式)。从只有{昨天、今天、明天、下周一、下周四}的下拉列表中选择对用户来说比日期选择器简单得多。日期选择器是表单填写中最容易引起痛苦的组件之一。当然,这并不适用于所有情况,但您可以看到只需要一点巧妙的设计就可以使其非常强大。

于 2010-09-17T06:24:45.367 回答
16

虽然我还没有尝试过,但我认为引人注目的时区调整方法如下:

  1. 以 UTC 存储所有内容。

  2. 创建一个TZOffsets包含三列的表:RegionClassId、StartDateTime 和 OffsetMinutes(int,以分钟为单位)。

在表中,存储本地时间更改的日期和时间列表以及更改幅度。表中的地区数量和日期数量取决于您需要支持的日期范围和世界范围。将其视为“历史”日期,即使日期应包括未来的某些实际限制。

当您需要计算任何 UTC 时间的本地时间时,只需执行以下操作:

SELECT DATEADD('m', SUM(OffsetMinutes), @inputdatetime) AS LocalDateTime
FROM   TZOffsets
WHERE  StartDateTime <= @inputdatetime
       AND RegionClassId = @RegionClassId;

您可能希望在您的应用程序中缓存此表并使用 LINQ 或其他类似方法来执行查询,而不是访问数据库。

这些数据可以从公共领域 tz 数据库中提取。

这种方法的优点和脚注:

  1. 代码中没有任何规则,您可以轻松调整新区域或日期范围的偏移量。
  2. 您不必支持每个日期或地区范围,您可以根据需要添加它们。
  3. 区域不必直接对应于地缘政治边界,并且为了避免行重复(例如,美国的大多数州以相同的方式处理 DST),您可以在另一个表中将广泛的 RegionClass 条目链接到更传统的列表州、国家等
  4. 对于像美国这样的情况,过去几年 DST 的开始和结束日期发生了变化,这很容易处理。
  5. 由于 StartDateTime 字段也可以存储时间,因此可以轻松处理凌晨 2:00 的标准转换时间。
  6. 并非世界上任何地方都使用 1 小时 DST。这很容易处理这些情况。
  7. 数据表是跨平台的,可以是一个单独的开源项目,可供使用几乎任何数据库平台或编程语言的开发人员使用。
  8. 这可用于与时区无关的偏移量。例如,不时发生的1秒调整以适应地球自转,对公历和公历内部的历史调整等。
  9. 由于这是在数据库表中,标准报表查询等可以利用数据而无需通过业务逻辑代码。
  10. 如果您愿意,它也可以处理时区偏移,甚至可以考虑将区域分配给另一个时区的特殊历史情况。您所需要的只是一个初始日期,该日期为每个区域分配一个时区偏移量,并具有最短的开始日期。这需要为每个时区创建至少一个区域,但您可以提出一些有趣的问题,例如:“1989 年 2 月 2 日凌晨 5 点,亚利桑那州尤马和华盛顿西雅图之间的当地时间有什么不同?” (只需从另一个中减去一个 SUM())。

现在,这种方法或任何其他方法的唯一缺点是本地时间到 GMT 的转换并不完美,因为任何对时钟具有负偏移的 DST 更改都会重复给定的本地时间。恐怕没有简单的方法来处理这个问题,这是存储当地时间首先是坏消息的一个原因。

于 2010-03-28T15:08:15.673 回答
16

我最近在一个 Web 应用程序中遇到了一个问题,在 Ajax 回发中,返回到我的服务器端代码的日期时间与提供的日期时间不同。

这很可能与我在客户端上的 JavaScript 代码有关,该代码构建了作为字符串回发到客户端的日期,因为 JavaScript 正在调整时区和夏令时,并且在某些浏览器中计算何时应用夏令时似乎与其他人不同。

最后,我选择完全删除客户端上的日期和时间计算,并在一个整数键上发回我的服务器,然后将其转换为服务器上的日期时间,以实现一致的转换。

我从中学到的:除非绝对必须,否则不要在 Web 应用程序中使用 JavaScript 日期和时间计算。

于 2010-03-28T13:07:46.503 回答
14

我已经在两种类型的系统上实现了这一点,“轮班计划系统(例如工厂工人)”和“气体依赖管理系统)......</p>

每天工作 23 和 25 小时是一件很痛苦的事情,8 小时轮班也需要 7 小时或 9 小时。问题是你会发现每个客户,甚至客户的部门都有他们在这些特殊情况下所做的事情创建的不同规则(通常没有记录)。

有些问题最好在客户为您的“现成”软件付费之前不要问他们。很少有客户在购买软件时预先考虑到此类问题。

我认为在所有情况下,您都应该以 UTC 记录时间,并在存储日期/时间之前转换为本地时间。然而,使用夏令时和时区,即使知道给定的时间是多少也很难。

于 2010-03-28T14:36:00.803 回答
13

对于网络来说,规则并没有那么复杂......

  • 服务器端,使用 UTC
  • 客户端,使用Olson
    • 原因:UTC 偏移量不是夏令时安全的(例如,纽约是一年中东部标准时间(UTC - 5 小时)的一部分,而美国东部时间(UTC - 4 小时)是一年中的其余时间)。
    • 对于客户端时区确定,您有两种选择:

其余的只是使用服务器端日期时间库的 UTC/本地转换。好去...

于 2012-08-14T15:25:47.933 回答
13

对于在服务器上运行的应用程序,包括网站和其他后端服务,应用程序应该忽略服务器的时区设置。

常见的建议是将服务器的时区设置为 UTC。这确实是一个很好的最佳实践,但对于不遵循其他最佳实践的应用程序来说,它是一种创可贴。例如,服务可能正在使用本地时间戳而不是基于 UTC 的时间戳写入日志文件,从而在夏令时回退过渡期间产生歧义。将服务器的时区设置为 UTC 将修复应用程序。然而,真正的解决办法是让应用程序一开始就使用 UTC 进行日志记录。

服务器端代码,包括网站,不应该期望服务器的本地时区是特定的。

在某些语言中,本地时区很容易渗入应用程序代码。例如,DateTime.ToUniversalTime.NET 中的方法将本地时区转换UTC,该DateTime.Now属性返回本地时区的当前时间。此外,DateJavaScript 中的构造函数使用计算机的本地时区。像这样的例子还有很多。练习防御性编程很重要,避免使用任何使用计算机本地时区设置的代码。

使用本地时区为客户端代码保留,例如桌面应用程序、移动应用程序和客户端 JavaScript。

于 2015-06-17T16:27:35.283 回答
11

将您的服务器设置为 UTC,并确保它们都配置为 ntp 或等效的。

UTC 避免了夏令时问题,不同步的服务器可能导致无法预测的结果,需要一段时间才能诊断。

于 2010-03-28T14:32:08.537 回答
9

处理存储在 FAT32 文件系统中的时间戳时要小心 - 它始终保存在本地时间坐标中(包括 DST - 请参阅msdn 文章)。被那个烫伤了。

于 2010-08-04T17:29:37.050 回答
7

另一件事是,确保服务器应用了最新的夏令时补丁。

去年我们遇到过这样一种情况,即使我们使用的是基于 UTC 的系统,北美用户在三周内的时间都持续减少一小时。

最终结果是服务器。他们只需要应用最新的补丁(Windows Server 2003)。

于 2010-08-04T11:08:29.740 回答
6

PHP 的DateTimeZone::listAbbreviations()输出

这个 PHP 方法返回一个包含一些“主要”时区(如 CEST)的关联数组,这些时区本身包含更具体的“地理”时区(如欧洲/阿姆斯特丹)。

如果您使用这些时区及其偏移量/夏令时信息,那么了解以下内容非常重要:

似乎每个时区的所有不同偏移量/DST 配置(包括历史配置)都包括在内!

例如,Europe/Amsterdam 可以在此函数的输出中找到六次。两次出现(偏移量 1172/4772)是用于 1937 年之前的阿姆斯特丹时间;两个 (1200/4800) 用于 1937 年至 1940 年间使用的时间;和两个 (3600/4800) 是自 1940 年以来使用的时间。

因此,您不能将此函数返回的偏移量/DST 信息视为当前正确/正在使用!

如果您想知道某个时区的当前偏移量/夏令时,您必须执行以下操作:

<?php
$now = new DateTime(null, new DateTimeZone('Europe/Amsterdam'));
echo $now->getOffset();
?>
于 2014-01-17T23:56:26.940 回答
5

如果您碰巧维护了在 DST 活动状态下运行的数据库系统,请仔细检查它们是否需要在秋季过渡期间关闭。Mandy DBS(或其他系统)不喜欢在(本地)时间两次通过同一点,这正是您在秋天倒转时钟时发生的情况。SAP 已经通过一种(恕我直言)解决方法解决了这个问题——他们没有让时钟倒转,而是让内部时钟以正常速度的一半运行两个小时......

于 2010-03-28T15:41:38.560 回答
4

商业规则应始终在民事时间有效(除非有立法另有规定)。请注意,民事时间是一团糟,但这是人们使用的,所以这很重要。

在内部,将时间戳保存在civil-time-seconds-from-epoch 之类的内容中。时代并不特别重要(我喜欢Unix 时代,但它确实比替代方案更容易。假设闰秒不存在,除非您正在做一些真正需要它们的事情(例如,卫星跟踪)。时间戳和显示时间之间的映射是唯一应该应用DST规则的点;规则经常变化(在全球范围内,一年几次;责怪政客)所以你应该确保你没有对映射进行硬编码。Olson 的TZ 数据库非常宝贵。

于 2010-03-28T11:47:20.303 回答
4

您使用的是.NET 框架吗?如果是这样,让我向您介绍 .NET 3.5 添加的DateTimeOffset类型。

此结构同时包含 aDateTime和 Offset ( ),它指定实例的日期和时间与协调世界时 (UTC)TimeSpan之间的差异。DateTimeOffset

  • DateTimeOffset.Now静态方法将返回一个由DateTimeOffset 当前(本地)时间和本地偏移量(在操作系统的区域信息中定义)组成的实例。

  • DateTimeOffset.UtcNow静态方法将返回一个 包含DateTimeOffsetUTC 当前时间的实例(就像您在格林威治一样)。

其他有用的类型是TimeZoneTimeZoneInfo类。

于 2011-12-29T12:32:09.390 回答
4

对于那些在.NET上苦苦挣扎的人,看看使用DateTimeOffset和/或TimeZoneInfo是否值得你花时间。

如果您想使用IANA/Olson 时区,或者发现内置类型不足以满足您的需求,请查看Noda Time,它为 .NET 提供了更智能的日期和时间 API。

于 2012-09-13T19:40:03.913 回答
2

这是我的经验:-

(不需要任何第三方库)

  • 在服务器端,以 UTC 格式存储时间,以便数据库中的所有日期/时间值都采用单一标准,而与用户、服务器、时区或 DST 的位置无关。
  • 在 UI 层或发送给用户的电子邮件中,您需要根据用户显示时间。就此而言,您需要拥有用户的时区偏移量,以便您可以将此偏移量添加到数据库的 UTC 值中,这将导致用户的本地时间。您可以在用户注册时获取用户的时区偏移量,也可以在 Web 和移动平台中自动检测它们。对于网站,JavaScript 的函数 getTimezoneOffset() 方法是自 1.0 版本以来的标准,并且与所有浏览器兼容。(参考:http ://www.w3schools.com/jsref/jsref_getTimezoneOffset.asp )
于 2015-03-30T13:12:12.213 回答
2

只是想指出两件看起来不准确或至少令人困惑的事情:

始终根据不受夏令时影响的统一标准持续时间。GMT 和 UTC 已被不同的人提及,但似乎最常提及 UTC。

对于(几乎)所有实际计算目的,UTC 实际上是 GMT。除非您看到带有小数秒的时间戳,否则您正在处理的是 GMT,这使得这种区别变得多余。

存储时间戳时包括本地时间偏移量(包括 DST 偏移量)。

时间戳始终以 GMT 表示,因此没有偏移。

于 2011-10-18T09:46:28.817 回答
2

Tom Scott 在 YouTube上 Computerphile 频道上关于时区的视频也对该主题进行了很好且有趣的描述。示例包括:

  • 萨摩亚(太平洋中的一个岛屿)将时区提前 24 小时,以便与澳大利亚和新西兰进行贸易,
  • 西岸,2 人口遵循不同的时区,
  • 18 世纪从儒略历变为公历 (发生在 20 世纪的俄罗斯)。
于 2016-02-19T11:21:58.337 回答
2

仅举一个例子来证明处理时间是所描述的巨大混乱,并且您永远不能自满。在本页的几个地方,闰秒被忽略了。

几年前,Android 操作系统使用 GPS 卫星获取 UTC 时间参考,但忽略了 GPS 卫星不使用闰秒这一事实。直到新年前夜出现混乱,当时苹果手机用户和安卓手机用户的倒计时时间间隔大约为 15 秒,没有人注意到这一点。

我想它已经被修复了,但你永远不知道这些“小细节”什么时候会再次困扰你。

于 2017-06-14T20:24:25.297 回答
1

实际上,kernel32.dll 不会导出 SystemTimeToTzSpecificLocation。但是它会导出以下两个:SystemTimeToTzSpecificLocalTime 和 TzSpecificLocalTimeToSystemTime...

于 2011-09-29T16:57:31.410 回答
1

永远不要只依赖像这样的构造函数

 new DateTime(int year, int month, int day, int hour, int minute, TimeZone timezone)

当由于 DST 导致某个日期时间不存在时,它们可能会引发异常。相反,构建您自己的方法来创建此类日期。在它们中,捕获由于 DST 而发生的任何异常,并使用转换偏移量调整所需的时间。根据时区,DST 可能发生在不同的日期和不同的时间(即使是巴西的午夜)。

于 2012-05-19T10:19:34.333 回答
1
  • 永远不要在没有 UTC 偏移量(或对时区的引用)的情况下存储本地时间——如何不struct tm存储的示例包括 FAT32 和 C 语言。¹
  • 了解时区是
    • 一组 UTC 偏移量(例如冬季 +0100,夏季 +0200)
    • 切换发生时间的规则(可能会随着时间的推移而改变:例如,在 1990 年代,欧盟将切换统一为 3 月和 10 月的最后一个星期日,标准时间 02:00/DST 03:00;以前这不同成员国之间)。

¹ 某些实现struct tm确实存储了 UTC 偏移量,但这还没有成为标准。

于 2018-02-02T21:24:46.677 回答
-4

在处理数据库(特别是MySQL,但这适用于大多数数据库)时,我发现很难存储 UTC。

  • 默认情况下,数据库通常使用服务器日期时间(即 CURRENT_TIMESTAMP)。
  • 您可能无法更改服务器时区。
  • 即使您能够更改时区,您也可能拥有期望服务器时区为本地的第三方代码。

我发现将服务器日期时间存储在数据库中更容易,然后让数据库在 SQL 语句中将存储的日期时间转换回 UTC(即 UNIX_TIMESTAMP())。之后,您可以在代码中使用日期时间作为 UTC。

如果您对服务器和所有代码有 100% 的控制权,最好将服务器时区更改为 UTC。

于 2011-12-13T11:09:33.077 回答