提问人: 提问时间:3/28/2010 最后编辑:50 revs, 18 users 51%Oded 更新时间:1/29/2022 访问量:487638
夏令时和时区最佳做法 [已关闭]
Daylight saving time and time zone best practices [closed]
问:
我希望使这个问题及其答案成为处理夏令时的权威指南,特别是处理实际转换的指南。
如果您有什么要补充的,请做
许多系统都依赖于保持准确的时间,问题在于由于夏令时而导致的时间变化 - 向前或向后移动时钟。
例如,在接受订单的系统中,有依赖于订单时间的业务规则 - 如果时钟发生变化,规则可能就不那么清晰了。订单时间应该如何维持?当然,有无数的场景 - 这个只是一个说明性的场景。
- 你是如何处理夏令时问题的?
- 解决方案中有哪些假设?(在此处查找上下文)
同样重要,如果不是更重要的话:
- 你尝试了什么没有用?
- 为什么它不起作用?
我对编程、操作系统、数据持久性和该问题的其他相关方面感兴趣。
一般答案很好,但我也想看看细节,特别是如果它们只在一个平台上可用。
答:
商业规则应该始终适用于民事时间(除非有立法另有规定)。请注意,民事时间是一团糟,但这是人们使用的,所以它才是重要的。
在内部,将时间戳保留在类似 civil-time-seconds-from-epoch 的范围内。纪元并不重要(我更喜欢 Unix 纪元),但它确实比替代方案更容易。假装闰秒不存在,除非你正在做一些真正需要它们的事情(例如,卫星跟踪)。时间戳和显示时间之间的映射是应应用 DST 规则的唯一点;规则经常更改(在全球范围内,每年几次;责怪政客),因此您应该确保不要对映射进行硬编码。Olson 的 TZ 数据库非常宝贵。
评论
答案和其他数据摘要:(请添加您的答案)
狐狸
- 每当您提到确切的时间时,请根据不受夏令时影响的统一标准保留时间。(GMT 和 UTC 在这方面是等效的,但最好使用术语 UTC。请注意,UTC 也称为祖鲁时间或 Z 时间。
- 相反,如果您选择使用本地时间值来保留(过去的)时间,请包括此特定时间相对于 UTC 的本地时间偏移量(此偏移量可能会在一年中发生变化),以便以后可以明确解释时间戳。
- 在某些情况下,您可能需要同时存储 UTC 时间和等效的本地时间。这通常是通过两个单独的字段完成的,但某些平台支持可以将这两个字段存储在单个字段中的类型。
datetimeoffset
- 将时间戳存储为数值时,请使用 Unix 时间 - 这是自那以后的整秒数(不包括闰秒)。如果需要更高的精度,请改用毫秒。此值应始终基于 UTC,无需进行任何时区调整。
1970-01-01T00:00:00Z
- 如果以后可能需要修改时间戳,请包括原始时区 ID,以便确定偏移量是否可能与记录的原始值相比发生了变化。
- 在计划未来事件时,通常首选本地时间而不是 UTC,因为偏移量通常会发生变化。请参阅答案和博客文章。
- 存储整个日期(例如生日和周年纪念日)时,请勿转换为 UTC 或任何其他时区。
- 如果可能,请以不包含一天中的时间的仅日期数据类型进行存储。
- 如果此类类型不可用,请确保在解释值时始终忽略时间。如果您无法确定一天中的时间将被忽略,请选择中午 12:00 而不是午夜 00:00 作为当天更安全的代表时间。
- 请记住,时区偏移量并不总是整数小时数(例如,印度标准时间为 UTC+05:30,尼泊尔使用 UTC+05:45)。
- 如果使用 Java,请将 java.time 用于 Java 8 及更高版本。
- 大部分 java.time 功能都向后移植到 Java 6 和 Java 7 的 ThreeTen-Backport 库中。
- 在ThreeTenABP库中进一步适配了早期的Android(<26)。
- 这些项目正式取代了现在处于维护模式的古老Joda-Time。Joda-Time、ThreeTen-Backport、ThreeTen-Extra、java.time 类和 JSR 310 都是由同一个人 Stephen Colebourne 领导的。
- 如果使用 .NET,请考虑使用 Noda Time。
- 如果使用没有 Noda Time 的 .NET,请考虑这通常是比 更好的选择。
DateTimeOffset
DateTime
- 如果使用 Perl,请使用 DateTime。
- 如果使用 Python 3.9 或更高版本,请使用内置的 zoneinfo 来处理时区。否则,请使用 dateutil 或 arrow。通常可以避免使用较旧的 pytz 库。
- 如果使用 JavaScript,请避免使用较旧的 moment.js 或 moment-timezone 库,因为它们不再积极维护。有关更多详细信息,请参阅 Moment.js 项目状态。相反,请考虑 Luxon、date-fns、day.js 或 js-joda。
- 如果使用 PHP > 5.2,请使用 和 classes 提供的本机时区转换。使用时要小心 - 见答案。要使 PHP 保持最新的 Olson 数据,请定期安装 timezonedb PECL 软件包;请参阅答案。
DateTime
DateTimeZone
DateTimeZone::listAbbreviations()
- 如果使用 C++,请确保使用正确实现 IANA 时区数据库的库。其中包括 cctz、ICU 和 Howard Hinnant 的“tz”库。在 C++20 中,后者被采用到标准库中。
<chrono>
- 如果使用 Rust,请使用 chrono。
- 大多数业务规则使用民事时间,而不是 UTC 或 GMT。因此,在应用应用程序逻辑之前,请计划将 UTC 时间戳转换为本地时区。
- 请记住,时区和偏移量不是固定的,可能会发生变化。例如,从历史上看,美国和英国使用相同的日期来“向前”和“向后退”。然而,在 2007 年,美国更改了时钟更改的日期。这意味着一年中有 48 周的伦敦时间与纽约时间相差 5 小时,而 4 周(春季 3 周,秋季 1 周)为 4 小时。在涉及多个区域的任何计算中,请注意此类项目。
- 考虑时间类型(实际事件时间、广播时间、相对时间、历史时间、重复时间)以及需要存储哪些元素(时间戳、时区偏移量和时区名称)才能正确检索 - 请参阅本答案中的“时间类型”。
- 使您的操作系统、数据库和应用程序 tzdata 文件与世界其他地方保持同步。
- 在服务器上,将硬件时钟和操作系统时钟设置为 UTC 而不是本地时区。
- 无论前面的项目符号是什么,服务器端代码(包括网站)都不应期望服务器的本地时区是任何特定的东西。请参阅答案。
- 更喜欢在应用程序代码中根据具体情况使用时区,而不是通过配置文件设置或默认值进行全局使用。
- 在所有服务器上使用 NTP 服务。
- 如果使用 FAT32,请记住时间戳以本地时间存储,而不是 UTC。
- 在处理重复发生的事件(例如每周电视节目)时,请记住,时间会随着 DST 而变化,并且会因时区而异。
- 始终将日期时间值查询为下限(包含)、上限(包含)和上限(包含) 。
>=
<
不要:
- 不要将“时区”(如)与“时区偏移量”(如 .它们是两回事。请参阅时区标签 wiki。
America/New_York
-05:00
- 不要在较旧的 Web 浏览器中使用 JavaScript 的对象执行日期和时间计算,因为 ECMAScript 5.1 及更低版本存在设计缺陷,可能会错误地使用夏令时。(此问题已在 ECMAScript 6 / 2015 中修复)。
Date
- 永远不要相信客户的时钟。这很可能是不正确的。
- 不要告诉人们“在任何地方都使用UTC”。这种广为流传的建议与本文档前面描述的几个有效方案相比是短视的。相反,请对正在处理的数据使用适当的时间参考。(时间戳可以使用 UTC,但不应使用将来时间计划和仅日期值。
测试:
- 测试时,请确保测试西半球、东半球、北半球和南半球的国家/地区(实际上在全球每个季度,因此有 4 个地区),同时 DST 正在进行中和未进行(给出 8 个),以及一个不使用 DST 的国家/地区(另外 4 个覆盖所有地区,总共 12 个)。
- 测试 DST 的转换,即当您当前处于夏令时时,从冬令时选择一个时间值。
- 使用 DST 测试边界情况,例如时区为 UTC+12,使本地时间在夏季为 UTC+13,甚至在冬季使本地时间为 UTC+13
- 测试所有第三方库和应用程序,并确保它们正确处理时区数据。
- 至少测试半小时时区。
参考:
- Stack Overflow 上的详细
时区
标签 wiki 页面 - 奥尔森数据库,又名Tz_database
- IETF 维护 Olson 数据库的程序草案
- 时区和 DST 的来源
- ISO 格式 (ISO 8601)
- Olson 数据库和 Windows 时区 ID 之间的映射,来自 Unicode Consortium
- 维基百科上的时区页面
- 标记为
dst
的 StackOverflow 问题 - StackOverflow 问题标记
时区
- 处理 DST - Microsoft DateTime 最佳做法
- 维基百科上的网络时间协议
其他:
- 游说您的代表结束 DST 的可憎行为。我们总是希望...
- 地球标准时间大厅
评论
我最近在一个 Web 应用程序中遇到了一个问题,在 Ajax 回发时,返回到我的服务器端代码的日期时间与提供的日期时间不同。
这很可能与我在客户端上的 JavaScript 代码有关,该代码建立了以字符串形式发布回客户端的日期,因为 JavaScript 正在调整时区和夏令时,并且在某些浏览器中,何时应用夏令时的计算似乎与其他浏览器不同。
最后,我选择完全删除客户端上的日期和时间计算,并以整数键回发到我的服务器,然后将其转换为服务器上的日期时间,以允许一致的转换。
我从中学到的: 不要在 Web 应用程序中使用 JavaScript 日期和时间计算,除非您绝对必须这样做。
评论
将服务器设置为 UTC,并确保它们都配置为 ntp 或等效项。
UTC 避免了夏令时问题,不同步的服务器可能会导致不可预知的结果,需要一段时间才能诊断出来。
我已经在两种类型的系统上遇到了这个问题,“轮班计划系统(例如工厂工人)”和“燃气依赖管理系统)”......
每天 23 小时和 25 小时的工作很痛苦,8 小时轮班需要 7 小时或 9 小时。问题在于,您会发现每个客户,甚至客户的部门,对于他们在这些特殊情况下所做的事情,都有不同的规则(通常没有记录)。
有些问题最好不要问客户,直到他们为您的“现成”软件付费后。很难找到在购买软件时预先考虑此类问题的客户。
我认为在所有情况下,您都应该以 UTC 记录时间并在存储日期/时间之前转换为本地时间。但是,即使知道给定时间的时间在夏令时和时区中也很难。
评论
通常,在存储的时间戳中包含本地时间偏移量(包括 DST 偏移量):如果以后要在其原始时区(和 DST 设置)中显示时间戳,仅使用 UTC 是不够的。
请记住,偏移量并不总是整数小时数(例如,印度标准时间为 UTC+05:30)。
例如,合适的格式是元组(unix 时间,偏移量(以分钟为单位)或 ISO 8601。
评论
虽然我没有尝试过,但我认为令人信服的时区调整方法如下:
将所有内容存储在 UTC 中。
创建一个包含三列的表:RegionClassId、StartDateTime 和 OffsetMinutes(int,以分钟为单位)。
TZOffsets
在表格中,存储本地时间更改的日期和时间列表,以及更改的日期和时间。表中的区域数和日期数取决于您需要支持的日期范围和世界区域。把它想象成“历史”日期,即使日期应该包括一些实际限制的未来。
当您需要计算任何UTC时间的本地时间时,只需执行以下操作:
SELECT DATEADD('m', SUM(OffsetMinutes), @inputdatetime) AS LocalDateTime
FROM TZOffsets
WHERE StartDateTime <= @inputdatetime
AND RegionClassId = @RegionClassId;
您可能希望在应用中缓存此表,并使用 LINQ 或一些类似的方法来执行查询,而不是访问数据库。
这些数据可以从公共领域的 tz 数据库中提炼出来。
这种方法的优点和脚注:
- 代码中没有规则,您可以轻松地调整新区域或日期范围的偏移量。
- 您不必支持每个日期或区域范围,您可以根据需要添加它们。
- 区域不必直接对应于地缘政治边界,并且为了避免行重复(例如,美国大多数州都以相同的方式处理 DST),您可以具有广泛的 RegionClass 条目,这些条目在另一个表中链接到更传统的州、国家/地区等列表。
- 对于像美国这样的情况,夏令时的开始和结束日期在过去几年中发生了变化,这很容易处理。
- 由于 StartDateTime 字段也可以存储时间,因此可以轻松处理凌晨 2:00 的标准转换时间。
- 并非世界上所有地方都使用 1 小时的夏令时。这很容易处理这些情况。
- 数据表是跨平台的,可以是一个单独的开源项目,可供使用几乎任何数据库平台或编程语言的开发人员使用。
- 这可用于与时区无关的偏移量。例如,不时发生的 1 秒调整以调整地球的自转、对公历和公历内的历史调整等。
- 由于这是在数据库表中,因此标准报表查询等可以利用数据,而无需通过业务逻辑代码。
- 如果您愿意,这也可以处理时区偏移量,甚至可以考虑将一个区域分配给另一个时区的特殊历史情况。您只需要一个初始日期,该日期为每个区域分配一个时区偏移量,并具有最短的开始日期。这需要为每个时区创建至少一个区域,但允许您提出有趣的问题,例如:“1989 年 2 月 2 日凌晨 5:00,亚利桑那州尤马和华盛顿州西雅图的当地时间有什么差异?(只需从另一个 SUM() 中减去一个 SUM()。
现在,这种方法或任何其他方法的唯一缺点是从本地时间到 GMT 的转换并不完美,因为任何对时钟具有负偏移量的 DST 更改都会重复给定的本地时间。恐怕没有简单的方法可以解决这个问题,这也是存储当地时间首先是坏消息的原因之一。
评论
这是一个重要且令人惊讶的棘手问题。事实是,对于持久性时间,没有完全令人满意的标准。例如,SQL标准和ISO格式(ISO 8601)显然是不够的。
从概念的角度来看,通常处理两种类型的时间-日期数据,区分它们很方便(上述标准没有):“物理时间”和“民用时间”。
“物理”瞬间是物理学所处理的连续宇宙时间线中的一个点(当然,忽略相对论)。例如,这个概念可以在UTC中充分编码(如果可以忽略闰秒)。
“民事”时间是遵循民事规范的日期时间规范:这里的时间点完全由一组日期时间字段(Y,M,D,H,MM,S,FS)加上TZ(时区规范)(实际上也是一个“日历”;但假设我们将讨论限制在公历上)。时区和日历共同允许(原则上)从一种表示映射到另一种表示。但是,民用时间瞬间和物理时间瞬间从根本上是不同类型的量级,它们应该在概念上分开并区别对待(类比:字节数组和字符串)。
这个问题令人困惑,因为我们交替谈论这些类型的事件,并且因为公民时代会受到政治变化的影响。对于未来的事件,这个问题(以及区分这些概念的必要性)变得更加明显。示例(摘自我在这里的讨论。
John 在他的日历中记录了某个事件的提醒,日期时间 TZ=,(偏移量为 GMT-4,
因此它对应于 UTC )。但总有一天
未来,该国决定将 TZ 偏移量更改为 GMT-5。2019-Jul-27, 10:30:00
Chile/Santiago
2019-Jul-27 14:30:00
现在,当这一天到来时......如果该提醒在
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)。
评论
如果您碰巧维护了在 DST 处于活动状态的情况下运行的数据库系统,请仔细检查它们在秋季过渡期间是否需要关闭。Mandy DBS(或其他系统)不喜欢两次通过(本地)时间的同一点,这正是您在秋天倒转时钟时发生的情况。SAP已经通过一个(恕我直言,真的很整洁)的解决方法解决了这个问题 - 他们没有让时钟倒流,他们只是让内部时钟以通常速度的一半运行两个小时......
您需要了解 Olson tz 数据库,该数据库可从 ftp://elsie.nci.nih.gov/pub http://iana.org/time-zones/ 获得。它每年更新多次,以应对世界不同国家/地区何时(以及是否)在冬季和夏季(标准和夏令时)之间切换的最后一刻变化。2009 年,最后一个版本是 2009 年代;2010年是2010年;2011年是2011年;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 库接口对这类东西没有太大的帮助。
奥尔森的数据已经移动,部分原因是 A D Olson 即将退休,部分原因是有针对维护者侵犯版权的诉讼(现已被驳回)。时区数据库现在由互联网号码分配机构 IANA 主持管理,首页上有一个指向“时区数据库”的链接。讨论邮件列表现在是;公告列表为 。[email protected]
[email protected]
评论
zic.8
zic.8.txt
tzcode2011i.tar.gz
tzdata2011i.tar.gz
跨越“计算机时间”和“人时间”的界限是一场噩梦。最主要的是,管理时区和夏令时的规则没有某种标准。国家/地区可以随时更改其时区和 DST 规则,而且确实如此。
一些国家,例如以色列、巴西,每年都会决定何时进行夏令时,因此无法提前知道夏令时何时生效。其他人对 DST 何时生效有固定的规则。其他国家/地区并不全部使用 DST。
时区不必与格林威治标准时间相差整整一小时。尼泊尔是+5.45。甚至有 +13 的时区。这意味着:
SUN 23:00 in Howland Island (-12)
MON 11:00 GMT
TUE 00:00 in Tonga (+13)
都是相同的时间,但 3 个不同的日子!
关于时区的缩写,以及它们在夏令时中如何变化,也没有明确的标准,所以你最终会得到这样的东西:
AST Arab Standard Time UTC+03
AST Arabian Standard Time UTC+04
AST Arabic Standard Time UTC+03
最好的建议是尽可能远离当地时间,并尽可能坚持使用UTC。仅在最后一刻转换为当地时间。
测试时,请确保测试西半球和东半球的国家/地区,同时进行 DST 和未进行 DST,以及不使用 DST 的国家/地区(总共 6 个)。
评论
我不确定我可以在上面的答案中补充什么,但这里有几点:
时间类型
您应该考虑四个不同的时间:
- 活动时间:例如,国际体育赛事发生的时间,或加冕/死亡/等。这取决于事件的时区,而不是查看者的时区。
- 电视时间:例如,一个特定的电视节目在当地时间晚上 9 点在世界各地播出。在考虑在您的网站上发布结果(例如美国偶像)时很重要
- 相对时间:例如:这个问题在 21 小时内关闭公开赏金。这很容易显示
- 重复时间:例如:即使夏令时发生变化,每周一晚上 9 点也会播放电视节目。
还有历史/交替时间。这些很烦人,因为它们可能不会映射回标准时间。例如:儒略历,根据土星上的农历,克林贡历的日期。
在 UTC 中存储开始/结束时间戳效果很好。对于 1,您需要一个事件时区名称 + 与事件一起存储的偏移量。对于 2,您需要与每个区域一起存储一个本地时间标识符,并为每个查看器存储一个本地时区名称 + 偏移量(如果您处于紧缩状态,可以从 IP 中派生此标识符)。对于 3,以 UTC 秒存储,无需时区。4 是 1 或 2 的特例,具体取决于它是全局事件还是本地事件,但您还需要存储创建时间戳,以便判断时区定义是在创建此事件之前还是之后更改的。如果需要显示历史数据,这是必需的。
存储时间
- 始终以 UTC 存储时间
- 转换为显示的本地时间(本地时间由查看数据的用户定义)
- 存储时区时,您需要名称、时间戳和偏移量。这是必需的,因为政府有时会更改其时区的含义(例如:美国政府更改了 DST 日期),并且您的应用程序需要优雅地处理事情......例如:LOST 剧集在 DST 规则更改之前和之后显示的确切时间戳。
偏移量和名称
上述示例如下:
足球世界杯决赛 发生在南非 (UTC+2--SAST) 于 2010年7月11日 19:00 UTC。
有了这些信息,即使南非时区定义发生变化,我们也可以从历史上确定 2010 年 WCS 决赛的确切时间,并且能够在观众查询数据库时向他们当地时区的观众显示该时间。
系统时间
您还需要保持操作系统、数据库和应用程序 tzdata 文件彼此之间以及与世界其他地方同步,并在升级时进行广泛的测试。您依赖的第三方应用程序没有正确处理 TZ 更改并非闻所未闻。
确保硬件时钟设置为 UTC,如果您在世界各地运行服务器,请确保其操作系统也配置为使用 UTC。当您需要从多个时区的服务器复制每小时轮换的 apache 日志文件时,这一点就变得很明显了。仅当所有文件都以相同的时区命名时,按文件名对它们进行排序才有效。这也意味着,当您从一个盒子到另一个盒子时,您不必在脑海中进行日期数学运算,并且需要比较时间戳。
此外,在所有盒子上运行 ntpd。
客户
永远不要相信您从客户端计算机获取的时间戳是有效的。例如,Date: HTTP 标头或 javascript 调用。当用作不透明标识符时,或者在同一客户端上的单个会话期间执行日期数学运算时,这些值很好,但不要尝试将这些值与服务器上的内容进行交叉引用。您的客户端不运行 NTP,并且不一定为其 BIOS 时钟提供工作电池。Date.getTime()
琐事
最后,政府有时会做一些非常奇怪的事情:
荷兰的标准时间是 正好是 19 分 32.13 秒 从 1909-05-01 开始,根据法律提前于 UTC 通过 1937-06-30。此时区 不能精确地表示 HH:MM 格式。
好的,我想我已经完成了。
评论
DateTimeOffset
明确关注点的架构分离 - 确切地知道哪个层与用户交互,并且必须更改规范表示 (UTC) 的日期时间。非 UTC 日期时间是表示(遵循用户本地时区),UTC 时间是模型(对于后端和中间层保持唯一)。
此外,决定你的实际受众是什么,你不必服务什么,以及你在哪里划清界限。不要碰异国情调的日历,除非你真的有重要的客户,然后考虑只为该地区使用单独的面向用户的服务器。
如果可以获取和维护用户的位置,请使用位置进行系统日期时间转换(例如 .NET 区域性或 SQL 表),但如果日期时间对用户至关重要,则为最终用户提供一种选择替代的方法。
如果涉及历史审计义务(例如准确告知亚利桑那州的 Jo 在 2 年前的 9 月支付账单的时间),请同时保留 UTC 和当地时间作为记录(您的转换表会随着时间的推移而更改)。
为批量数据(如文件、Web 服务等)定义时间参考时区。 假设东海岸公司在加利福尼亚州拥有数据中心 - 您需要询问并知道他们使用什么作为标准,而不是假设一个或另一个。
不要相信嵌入在日期时间的文本表示形式的时区偏移量,也不要接受解析和遵循它们。相反,始终要求必须明确定义时区和/或参考区。您可以轻松接收带有 PST 偏移量的时间,但时间实际上是 EST,因为这是客户端的参考时间,并且记录只是在 PST 中的服务器上导出。
还有一件事,确保服务器应用了最新的夏令时补丁。
去年,我们遇到了这样的情况,即即使我们使用的是基于UTC的系统,我们的时间在三周内一直比北美用户多出一小时。
事实证明,最终是服务器。他们只需要应用最新的修补程序 (Windows Server 2003)。
在处理存储在 FAT32 文件系统中的时间戳时要小心 - 它始终保留在本地时间坐标中(包括 DST - 请参阅 msdn 文章)。被烧伤了。
评论
如果您的设计可以适应它,请避免同时进行本地时间转换!
我知道对某些人来说这听起来可能很疯狂,但想想用户体验:用户处理接近的相对日期(今天、昨天、下周一)的速度比绝对日期(2010.09.17,9 月 17 日星期五)快。当你仔细想想,时区(和夏令时)的准确性越接近日期越重要,所以如果你能用相对格式表示 +/- 1 或 2 周的日期/日期时间,其余的日期可以是 UTC,这对 95% 的用户来说不会太重要。now()
这样,您可以将所有日期存储在 UTC 中,并在 UTC 中进行相对比较,只需在相对日期阈值之外向用户显示 UTC 日期。
这也适用于用户输入(但通常以更有限的方式)。对于用户来说,从只有 { Yesterday, Today, Tomorrow, Next Monday, Next Thursday } 的下拉列表中进行选择比日期选择器要简单得多。日期选择器是表单填写中最令人痛苦的组成部分之一。当然,这并不适用于所有情况,但您可以看到,只需要一点巧妙的设计就可以使其非常强大。
对于 PHP:
PHP > 5.2 中的 DateTimeZone 类已经基于其他人提到的 Olson DB,因此如果您在 PHP 中而不是在 DB 中进行时区转换,则无需使用(难以理解的)Olson 文件。
但是,PHP 的更新频率不如 Olson DB,因此仅使用 PHP 时区转换可能会给您留下过时的 DST 信息并影响数据的正确性。虽然这种情况预计不会经常发生,但它可能会发生,并且如果您在全球拥有大量用户群,就会发生这种情况。
为了解决上述问题,请使用 timezonedb pecl 包。它的功能是更新PHP的时区数据。安装此软件包的频率与更新频率相同。(我不确定这个包的更新是否完全遵循 Olson 更新,但它的更新频率似乎至少非常接近 Olson 更新的频率。
实际上,kernel32.dll 不会导出 SystemTimeToTzSpecificLocation。但是,它确实导出以下两个:SystemTimeToTzSpecificLocalTime 和 TzSpecificLocalTimeToSystemTime...
只是想指出两件事似乎不准确或至少令人困惑:
始终按照统一标准坚持时间,而不是 受夏令时影响。GMT 和 UTC 已被 不同的人,尽管UTC似乎最常被提及。
对于(几乎)所有实际计算目的,UTC实际上是GMT。除非您看到带有小数秒的时间戳,否则您正在处理 GMT,这使得这种区分变得多余。
在以下情况下按原样包括本地时间偏移量(包括 DST 偏移量) 存储时间戳。
时间戳始终以 GMT 表示,因此没有偏移量。
评论
在处理数据库(特别是MySQL,但这适用于大多数数据库)时,我发现很难存储UTC。
- 默认情况下,数据库通常使用服务器日期时间(即 CURRENT_TIMESTAMP)。
- 您可能无法更改服务器时区。
- 即使您可以更改时区,也可能有第三方代码要求服务器时区为本地时区。
我发现将服务器日期时间存储在数据库中,然后让数据库在 SQL 语句中将存储的日期时间转换回 UTC(即 UNIX_TIMESTAMP())更容易。之后,您可以在代码中使用日期时间作为 UTC。
如果您对服务器和所有代码有 100% 的控制权,则最好将服务器时区更改为 UTC。
评论
您使用的是 .NET Framework 吗? 如果是这样,请允许我向您介绍随 .NET 3.5 添加的 DateTimeOffset 类型。
此结构同时包含 a 和 Offset (),后者指定实例的日期和时间与协调世界时 (UTC) 之间的差值。DateTime
TimeSpan
DateTimeOffset
static 方法将返回一个由当前(本地)时间和本地偏移量组成的实例 (如操作系统的区域信息中所定义)。
DateTimeOffset.Now
DateTimeOffset
static 方法将返回一个由当前时间组成的实例(以 UTC 为单位 如果你在格林威治)。
DateTimeOffset.UtcNow
DateTimeOffset
其他有用的类型是 TimeZone 和 TimeZoneInfo 类。
评论
永远不要只依赖像这样的构造函数
new DateTime(int year, int month, int day, int hour, int minute, TimeZone timezone)
当由于 DST 而不存在特定日期时间时,它们可能会引发异常。相反,请构建自己的方法来创建此类日期。在其中,捕获由于 DST 而发生的任何异常,并使用转换偏移量调整所需的时间。根据时区的不同,夏令时可能会在不同的日期和不同的时间(甚至在巴西的午夜)发生。
对于网络来说,规则并不复杂......
- 服务器端,使用 UTC
- 客户端,使用 Olson
- 原因:UTC 偏移量不是夏令时安全的(例如,纽约是一年中的部分时间(UTC - 5 小时),EDT(UTC - 4 小时)是一年中的其余时间)。
- 对于客户端时区确定,您有两个选项:
- 1)让用户设置区域(更安全)
- 2) 自动检测区
剩下的就是使用服务器端日期时间库进行UTC/本地转换。好去...
评论
对于那些在 .NET 上苦苦挣扎的人,看看是否值得您花时间使用和/或。DateTimeOffset
TimeZoneInfo
如果您想使用 IANA/Olson 时区,或者发现内置类型不足以满足您的需求,请查看 Noda Time,它为 .NET 提供了更智能的日期和时间 API。
PHP 的 DateTimeZone::listAbbreviations()
输出
此 PHP 方法返回一个包含一些“主要”时区(如 CEST)的关联数组,这些时区本身包含更具体的“地理”时区(如欧洲/阿姆斯特丹)。
如果您使用的是这些时区及其偏移量/DST 信息,则了解以下几点非常重要:
似乎包括每个时区的所有不同偏移量/DST 配置(包括历史配置)!
例如,Europe/Amsterdam 可以在此函数的输出中找到 6 次。两次出现(偏移量 1172/4772)是阿姆斯特丹时间,直到 1937 年;两个 (1200/4800) 用于 1937 年至 1940 年间使用的时间;两个(3600/4800)是自1940年以来使用的时间。
因此,您不能依赖此函数返回的偏移量/DST 信息当前正确/正在使用!
如果你想知道某个时区的当前偏移量/夏令时,你必须做这样的事情:
<?php
$now = new DateTime(null, new DateTimeZone('Europe/Amsterdam'));
echo $now->getOffset();
?>
这是我的经验:-
(不需要任何第三方库)
- 在服务器端,以 UTC 格式存储时间,以便数据库中的所有日期/时间值都采用单一标准,而不管用户、服务器、时区或 DST 的位置如何。
- 在 UI 层或发送给用户的电子邮件中,您需要根据用户显示时间。就此而言,您需要具有用户的时区偏移量,以便可以将此偏移量添加到数据库的 UTC 值中,从而获得用户的本地时间。您可以在用户注册时获取他们的时区偏移量,也可以在 Web 和移动平台中自动检测他们。对于网站,JavaScript 的函数 getTimezoneOffset() 方法是自 1.0 版本以来的标准,并且与所有浏览器兼容。(参考: http://www.w3schools.com/jsref/jsref_getTimezoneOffset.asp)
评论
getTimezoneOffset
当涉及到在服务器上运行的应用程序(包括网站和其他后端服务)时,应用程序应忽略服务器的时区设置。
常见的建议是将服务器的时区设置为 UTC。这确实是一个很好的最佳实践,但对于不遵循其他最佳实践的应用程序来说,它是一种创可贴。例如,服务可能使用本地时间戳而不是基于 UTC 的时间戳写入日志文件,从而在夏令时回退转换期间产生歧义。将服务器的时区设置为 UTC 将修复该应用程序。但是,真正的解决方法是让应用程序首先使用 UTC 进行记录。
服务器端代码(包括网站)永远不应该期望服务器的本地时区是任何特定的时区。
在某些语言中,本地时区很容易渗透到应用程序代码中。例如,.NET 中的方法将从本地时区转换为 UTC,并且该属性返回本地时区的当前时间。此外,JavaScript 中的构造函数使用计算机的本地时区。像这样的例子还有很多。练习防御性编程很重要,避免使用计算机本地时区设置的任何代码。DateTime.ToUniversalTime
DateTime.Now
Date
使用本地时区为客户端代码(如桌面应用程序、移动应用程序和客户端 JavaScript)保留。
汤姆·斯科特 (Tom Scott) 在 Computerphile 频道的 YouTube 上关于时区的视频也对该主题进行了很好且有趣的描述。 示例包括:
- 萨摩亚(太平洋上的一个岛屿)将其时区向前移动了 24 小时,以方便与澳大利亚和新西兰的贸易,
- 约旦河西岸,那里有 2 个人口遵循不同的时区,
- 18世纪从儒略历到公历的变化(发生在20世纪的俄罗斯)。
举个例子来证明处理时间是所描述的巨大混乱,你永远不能自满。在此页面上的几个地方,闰秒被忽略了。
几年前,Android 操作系统使用 GPS 卫星来获取 UTC 时间参考,但忽略了 GPS 卫星不使用闰秒的事实。直到除夕夜出现混乱,苹果手机用户和Android手机用户相隔约15秒进行倒计时,才有人注意到。
我认为它已经修复了,但你永远不知道这些“小细节”什么时候会回来困扰你。
- 永远不要在没有 UTC 偏移量(或对时区的引用)的情况下存储本地时间——如何不这样做的示例包括 FAT32 和 C 语言。
struct tm
- 了解时区是以下因素的组合
- 一组 UTC 偏移量(例如,冬季为 +0100,夏季为 +0200)
- 切换发生时间的规则(可能会随着时间的推移而改变:例如,在 1990 年代,欧盟将切换协调为 3 月和 10 月的最后一个星期日,标准时间 02:00/夏令时 03:00;以前成员国之间有所不同)。
¹ 某些实现确实存储了 UTC 偏移量,但这尚未成为标准。struct tm
评论
GETDATE()
DateTime.Now
DateTime.UtcNow
GETUTCDATE()