夏令时和时区最佳实践
我希望能够把这个问题及其答案作为处理夏令时的权威性指南,尤其是对于处理实际变更的问题。
如果你有什么补充,请做
许多系统都依赖于保持准确的时间,问题在于由于夏令时而改变时间 – 向前或向后移动时钟。
例如,一个订单取得系统的业务规则取决于订单的时间 – 如果时钟改变,规则可能不那么清晰。 订单的时间应该如何持续下去? 当然有无数的场景 – 这只是一个例证。
- 你怎么处理夏令时问题?
- 什么假设是你的解决scheme的一部分? (在这里寻找上下文)
重要的是,如果不是更重要的话:
- 你尝试了什么不起作用?
- 为什么不行?
我会对编程,操作系统,数据持久性和其他相关问题感兴趣。
一般的答案是好的,但我也想看看细节,特别是如果他们只在一个平台上。
答案和其他数据摘要:(请加上你的)
做:
- 每当你指的是一个确切的时刻,坚持时间根据一个统一的标准,不受夏时制的影响。 (GMT和UTC在这方面是等价的,但是最好使用UTC这个术语。注意UTC也被称为Zulu或Z时间。
- 如果您select使用本地时间值来保存时间,请包括UTC的本地时间偏移,以便稍后可以明确解释时间戳。
- 在某些情况下,您可能需要同时存储UTC时间和当地时间。 通常这是通过两个单独的字段来完成的,但是一些平台支持可以存储在单个字段中的
datetimeoffset
types。 - 将时间戳存储为数字值时,请使用Unix时间 – 这是自
1970-01-01T00:00:00Z
(不包括闰秒)以来的整秒数。 如果您需要更高的精确度,请改用毫秒。 此值应始终基于UTC,而不进行任何时区调整。 - 如果以后需要修改时间戳,请包含原始时区ID,以便确定偏移量是否可能与logging的原始值发生了变化。
- 在安排未来事件时,通常优先使用本地时间,而不是UTC,因为偏移是常见的变化。 查看答案和博客post 。
- 存储完整date(例如生日和周年纪念日)时,不要转换为UTC或任何其他时区。
- 如果可能,请存储不包含一天中某个时间的仅限date的数据types。
- 如果这种types不可用,请务必在解释该值时始终忽略时间。 如果您不能保证时间会被忽略,请select12:00中午,而不是00:00午夜作为更安全的代表时间。
- 请记住,时区偏移不总是整数小时(例如,印度标准时间是UTC + 05:30,尼泊尔使用UTC + 05:45)。
- 如果使用Java,请使用java.time for Java 8,或者使用Joda Time for Java 7或更低版本。
- 如果使用.NET ,请考虑使用Noda Time 。
- 如果在不使用Noda Time的情况下使用.NET,则认为
DateTimeOffset
通常是比DateTime
更好的select。 - 如果使用Perl,请使用DateTime 。
- 如果使用Python,请使用pytz或dateutil 。
- 如果使用JavaScript,请使用带有时区扩展名的moment.js 。
- 如果使用PHP> 5.2,则使用由
DateTime
和DateTimeZone
类提供的本地时区转换。 使用DateTimeZone::listAbbreviations()
时要小心 – 请参阅答案 。 要保持PHP与最新的Olson数据,定期安装timezonedb PECL包; 见答案 。 - 如果使用C ++,请确保使用正确实现IANA时区数据库的库 。 包括cctz , ICU和Howard Hinnant的“tz”图书馆 。
- 不要使用Boost进行时区转换。 虽然其API声称支持标准IANA(又称“zoneinfo”)标识符,但它粗略地将其映射到POSIX风格的数据,而不考虑每个区域可能具有的丰富的更改历史logging。 (此外,该文件已经不在维护中。)
- 大多数业务规则使用民用时间,而不是UTC或GMT。 因此,计划在应用应用程序逻辑之前将UTC时间戳转换为本地时区。
- 请记住,时区和偏移不是固定的,可能会改变。 例如,历史上美国和英国使用相同的date来“前进”和“后退”。 然而,在2007年,美国改变了时钟改变的date。 这意味着,一年中的48周,伦敦时间和纽约时间之间的差别是5小时和4周(春季3次,秋季1次)是4小时。 在任何涉及多个区域的计算中都要注意这样的项目。
- 考虑时间types(实际事件时间,广播时间,相对时间,历史时间,周期时间)需要存储哪些元素(时间戳,时区偏移和时区名称)以便正确检索 – 请参阅“时间types” 这个答案 。
- 保持你的操作系统,数据库和应用程序数据文件在他们自己和世界其他地方之间保持同步。
- 在服务器上,将硬件时钟和操作系统时钟设置为UTC而不是本地时区。
- 不pipe以前的要点是什么,包括网站在内的服务器端代码不应该期望服务器的本地时区是特别的东西。 见答案 。
- 喜欢在应用程序代码中根据具体情况使用时区,而不是通过configuration文件设置或默认设置全局。
- 在所有服务器上使用NTP服务。
- 如果使用FAT32 ,请记住时间戳存储在本地时间,而不是UTC。
- 在处理周期性事件(例如,每周电视节目)时,请记住,时间随着DST的变化而变化,并且在不同时区之间会有所不同。
- 总是查询date – 时间值作为下限包括,上限排他 (
>=
,<
)。
别:
- 请勿将“
America/New_York
”等“时区”与“时区偏移”(例如-05:00
混淆。 他们是两回事。 查看时区标记wiki 。 - 不要使用JavaScript的
Date
对象在较早的Web浏览器中执行date和时间计算,因为ECMAScript 5.1及更低版本具有可能错误地使用夏令时的devise缺陷 。 (这在ECMAScript 6/2015中得到了修复)。 - 永远不要相信客户的时钟。 这可能是不正确的。
- 不要告诉人们“总是使用UTC”。 这个广泛的build议是本文前面描述的几个有效scheme的短视。 相反,使用适当的时间参考您正在使用的数据。 ( 时间戳可以使用UTC,但是将来的时间安排和仅限date的值不应该)。
testing:
- testing时,确保testing西,东,北,南半球的国家(事实上在全球每个季度都有4个地区),DST正在进行中,而不是(8次),还有一个国家不使用DST(另外4个覆盖所有地区,共计12个)。
- DST的testing过渡,即当你在夏天的时候,从冬天select一个时间值。
- 使用DSTtesting边界情况(例如UTC + 12的时区),使当地时间在夏季为UTC + 13,甚至在冬季为UTC + 13
- testing所有第三方库和应用程序,并确保它们正确处理时区数据。
- 至lesstesting半小时的时区。
参考:
- Stack Overflow上详细的
timezone
标签wiki页面 - 奥尔森数据库,又名Tz_database
- IETF起草维护Olson数据库的程序
- 时区和DST的来源
- ISO格式(ISO 8601)
- Olson数据库和Windows时区ID之间的映射,来自Unicode联合会
- 维基百科上的时区页面
- StackOverflow问题标记为
dst
- StackOverflow问题标记的
timezone
- 处理DST – Microsoft DateTime最佳实践
- networking时间协议维基百科
其他:
- 大厅你的代表结束可憎的是DST。 我们总是希望…
- 大堂的地球标准时间
我不知道我可以添加到上面的答案,但这里有几点从我:
时代的types
有四个不同的时间你应该考虑:
- 活动时间:例如发生国际体育赛事或加冕/死亡等。 这取决于事件的时区,而不是观众的时区。
- 电视时间:例如,当地时间晚上9点在世界各地播放特定的电视节目。 在您的网站上考虑发布结果(比如美国偶像)时很重要
- 相对时间:例如:这个问题在21小时内有一个开放的赏金结束。 这很容易显示
- 周期时间:例如:电视节目每周一晚上9点,即使夏令时发生变化。
也有历史/备用时间。 这些都很烦人,因为它们不能映射回标准时间。 例如:朱利安date,根据土星的农历date,克林贡日历。
以UTC存储开始/结束时间戳运行良好。 对于1,您需要一个与事件一起存储的事件时区名称+偏移量。 对于2,您需要存储每个区域的本地时间标识符以及为每个查看器存储的本地时区名称+偏移量(如果您处于紧缩状态,可以从IP中导出)。 对于3,以UTC秒存储并且不需要时区。 4是1或2的特殊情况,取决于它是全局事件还是本地事件,但是您还需要存储创build的时间戳记,以便您可以确定在创build事件之前或之后是否更改了时区定义。 如果您需要显示历史数据,这是必要的。
储存时间
- 始终以UTC存储时间
- 转换为显示的本地时间(本地由用户查看数据定义)
- 存储时区时,需要名称,时间戳和偏移量。 这是必需的,因为政府有时会改变其时区的含义(例如:美国政府更改DSTdate),并且您的应用程序需要适当地处理事情…例如:DOST规则前后显示的确切时间戳改变。
偏移和名称
上面的例子是:
世界杯足球赛总决赛在2010年7月11日19:00 UTC在南非(UTC + 2 – SAST)举行。
有了这些信息,即使南非时区定义发生变化,我们也可以在历史上确定2010年WCS总决赛的确切时间, 并且能够在查询数据库时向当地时区的观众显示。
系统时间
您还需要保持您的操作系统,数据库和应用程序tzdata文件同步,彼此同步,并与世界其他地方保持同步,并在升级时进行广泛的testing。 您所依赖的第三方应用程序没有正确处理TZ更改并不是闻所未闻。
确保硬件时钟设置为UTC,如果您在世界各地运行服务器,请确保将其操作系统configuration为使用UTC。 当你需要从多个时区的服务器复制按小时轮播的apache日志文件时,这一点就变得很明显了。 按文件名sorting仅适用于所有文件都使用相同时区命名的情况。 这也意味着,当你从一个盒子跳到另一个盒子时需要比较时间戳,所以你不必在头脑中进行datemath计算。
另外,在所有的盒子上运行ntpd。
客户端
永远不要相信从客户端机器获得的时间戳是有效的。 例如,Date:HTTP标头,或者一个javascript Date.getTime()
调用。 当用作不透明标识符时,或者在同一客户端的单个会话期间进行datemath计算时,这些都没有问题,但是不要试图将这些值与服务器上的某些值进行交叉引用。 您的客户端不运行NTP,可能不一定有一个工作电池的BIOS时钟。
琐事
最后,政府有时会做很奇怪的事情:
荷兰的标准时间从1909年5月1日到1937年6月30日,比法定时间早了19分32.13秒。 这个时区不能用HH:MM格式完全表示。
好吧,我想我已经完成了。
这是一个非常重要和令人惊讶的难题。 事实是,持续时间没有完全令人满意的标准。 例如,SQL标准和ISO格式(ISO 8601)显然是不够的。
从概念的angular度来看,通常涉及到两种时间date数据,并且便于区分它们(上述标准没有):“ 实际时间 ”和“ 民间时间 ”。
时间的“物理”瞬间是物理学处理的持续普遍时间表中的一点(当然,忽略相对性)。 例如,这个概念可以被充分地编码 – 以UTC保存(如果你可以忽略闰秒)。
“民间”时间是遵循民事规范的date时间规范:这里的时间点由一组date时间字段(Y,M,D,H,MM,S,FS)加上TZ(时区规范) (实际上也是一个“日历”,但假设我们将讨论限定在公历上)。 时区和日历共同允许(原则上)从一个表示映射到另一个表示。 但是,公民和物理时间是根本不同types的大小,它们应该保持在概念上的分离和不同的处理(类比:字节和stringarrays)。
这个问题是令人困惑的,因为我们可以互换地谈论这些types的事件,而且由于公民时代受到政治变化的影响。 问题(以及区分这些概念的必要性)对未来的事件更为明显。 例子(取自我在这里的讨论。
约翰logging日历时间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
给我2019-Jul-27, 10:30:00 TZ=Chile/Santiago
”。
他的意思是“公民date时间”(“我的城市时钟10:30说)”吗? 在这种情况下,A)是正确的答案。
还是他的意思是一个“物理时间瞬间”,这是我们宇宙连续时间线上的一个点,比如说“下一次日食发生时”。 在这种情况下,答案B)是正确的。
一些date/时间API可以很好地区分这个区别:其中包括Jodatime ,它是下一个(第三个!)Java DateTime API(JSR 310)的基础。
明确的架构分离的关注 – 要确切地知道哪个层与用户交互,并且必须改变/从标准表示(UTC)的date时间。 非UTCdate时间是演示文稿 (在用户本地时区之后), UTC时间是模型 (对于后端和中间层保持唯一)。
另外, 决定你的实际听众是什么,你不需要服务什么,你在哪里画线 。 除非您确实拥有重要的客户,否则请勿触摸异国情调的日历,然后再考虑单独面向该区域的面向用户的服务器。
如果您可以获取和维护用户的位置,请使用位置进行系统date – 时间转换(如.NET文化或SQL表格),但是如果date时间对于用户至关重要,则为最终用户select覆盖提供了一种方法。
如果有涉及的历史审计义务 (例如准确地说明9月份乔AZ在2年前支付了账单), 那么保持UTC和当地时间作为logging(您的转换表将在一段时间内改变)。
定义大批量数据 (如文件,Web服务等) 的时间参考时区 。假设东海岸公司在加利福尼亚州有数据中心 – 您需要询问并了解他们作为标准使用的内容,而不是假设其中一个。
不要相信embedded在date时间的文本表示中的时区偏移量,也不要接受parsing并遵循它们。 相反,总是要求明确定义时区和/或参考区域 。 您可以很容易地获得PST抵消的时间,但时间实际上是EST,因为这是客户端的参考时间,logging只是在PST服务器上输出。
您需要了解Olson tz数据库 ,该数据库可从ftp://elsie.nci.nih.gov/pub http://iana.org/time-zones/获取; 。 它每年更新多次,以处理世界各国在冬季和夏季(标准时间和夏令时)之间切换的时间(以及是否)经常发生的最后一刻的变化。 2009年最后一个版本是2009年; 2010年是2010年, 2011年是2011年; 在2012年5月底,发布是2012年c。 请注意,在两个单独的归档(tzcode20xxy.tar.gz和tzdata20xxy.tar.gz)中有一组代码来pipe理数据和实际的时区数据本身。 代码和数据都在公共领域。
这是时区名称的来源,例如America / Los_Angeles(以及诸如US / Pacific的同义词)。
如果您需要跟踪不同的区域,那么您需要Olson数据库。 正如其他人所build议的那样,您还希望以固定的格式存储数据 – UTC通常是所选的数据 – 以及生成数据的时区logging。 您可能需要区分当时与UTC的偏移量和时区名称; 这可以在以后有所作为。 另外,知道现在是2010年3月28日23:47:00-07:00(美国/太平洋)可能会或可能不会帮助您解释2010-11-15T12:30的值 – 这大概是在PST太平洋标准时间),而不是PDT(太平洋夏令时)。
标准的C库接口对这类东西不是很有帮助。
奥尔森的数据已经发生了变化,部分原因是因为AD奥尔森将很快退休,部分原因是因为对版权侵权维护者提出了(现在被驳回的)诉讼。 时区数据库现在由IANA (互联网号码分配机构)主持pipe理,首页上有一个“时区数据库”链接 。 讨论邮件列performance在是tz@iana.org
; 公告名单是tz-announce@iana.org
。
通常,在存储的时间戳中包含本地时间偏移量(包括DST偏移量) :如果稍后要在其原始时区(和DST设置)中显示时间戳,那么UTC本身是不够的。
请记住,偏移不总是整数小时(例如印度标准时间是UTC + 05:30)。
例如,合适的格式是元组(unix时间,以分钟为单位的偏移量)或ISO 8601 。
跨越“电脑时间”和“人们时间”的界限是一场噩梦。 主要的一点是时区和夏令时规则没有标准。 各国可以随时更改他们的时区和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
最好的build议是尽可能地远离当地时间,并尽可能地坚持到UTC。 只能在最后时刻转换到当地时间。
在进行testing时,确保您testing了西半球和东半球的国家,同时进行了DST,而不是使用DST的国家(共6个)。
对于PHP:
PHP> 5.2中的DateTimeZone类已经基于其他人提到的Olson DB,所以如果您在PHP中而不是在DB中进行时区转换,那么您可以免除使用(难以理解的)Olson文件。
但是,PHP不会像Olson DB那样频繁更新,所以只使用PHP时区转换可能会使您的DST信息过期,并影响数据的正确性。 虽然这不会经常发生,但如果您在全球范围内拥有庞大的用户群,则可能会发生这种情况。
为了解决上述问题,请使用timezonedb pecl包 ,这个function是更新PHP的时区数据。 安装此软件包的频率与更新频率相同。 (我不确定这个软件包更新是否完全遵循Olson更新,但似乎是以至less非常接近Olson更新的频率进行更新。)
如果你的devise可以容纳它, 避免本地时间一起转换!
我知道有些人可能听起来很疯狂,但是想一想用户体验:用户一眼就能看到相对date(今天,昨天,下周一)比绝对date(2010.09.17,星期五17日)快。 当你考虑更多的时候,timezones(和DST)的精确度越接近date now()
,所以如果你可以以+/- 1或2周的相对格式表示date/date时间,其余的date可以是UTC,95%的用户不会太重要。
通过这种方式,您可以使用UTC存储所有date,并以UTC为单位进行相对比较,并简单地在相对date阈值之外显示用户的UTCdate。
这也可以应用于用户input(但通常以更有限的方式)。 从下拉菜单中select{Yesterday,Today,Tomorrow,Next Monday,Next Thursday}对于用户来说比dateselect器更简单和容易。 dateselect器是表格填充最引起疼痛的部分。 当然这并不适用于所有的情况,但是你可以看到它只需要一点聪明的devise就可以使它非常强大。
虽然我还没有尝试过,但我会发现一个时区调整的方法如下:
-
使用UTC存储所有内容。
-
使用三列创build表
TZOffsets
:RegionClassId,StartDateTime和OffsetMinutes(int,以分钟为单位)。
在表中,存储当地时间改变的date和时间的列表,以及多less。 表格中的地区数量和date的数量取决于您需要支持的世界范围的date和地区。 把它看作是“历史”的date,尽pipedate应该包括未来的一些实际的限制。
当您需要计算任何UTC时间的本地时间时,只需执行以下操作:
SELECT DATEADD('m', SUM(OffsetMinutes), @inputdatetime) AS LocalDateTime FROM TZOffsets WHERE StartDateTime <= @inputdatetime AND RegionClassId = @RegionClassId;
您可能希望将此表caching在您的应用程序中,并使用LINQ或一些类似的方法来执行查询,而不是击中数据库。
这个数据可以从公共领域tz数据库中提取。
这种方法的优点和脚注:
- 代码中没有规则,您可以随时调整新区域或date范围的偏移量。
- 您不必支持每个date或地区的范围,您可以根据需要添加它们。
- Regions don't have to correspond directly to geopolitical boundaries, and to avoid duplication of rows (for instance, most states in the US handle DST the same way), you can have broad RegionClass entries that link in another table to more traditional lists of states, countries, etc.
- For situations like the US where the start and end date of DST has changed over the past few years, this is pretty easy to deal with.
- Since the StartDateTime field can store a time as well, the 2:00 AM standard change-over time is handled easily.
- Not everywhere in the world uses a 1-hour DST. This handles those cases easily.
- The data table is cross-platform and could be a separate open-source project that could be used by developers who use nearly any database platform or programming language.
- This can be used for offsets that have nothing to do with time zones. For instance, the 1-second adjustments that happen from time to time to adjust for the Earth's rotation, historical adjustments to and within the Gregorian calendar, etc.
- Since this is in a database table, standard report queries, etc. can take advantage of the data without a trip through business logic code.
- This handles time zone offsets as well if you want it to, and can even account for special historical cases where a region is assigned to another time zone. All you need is an initial date that assigns a time zone offset to each region with a minimal start date. This would require creating at least one region for each time zone, but would allow you to ask interesting questions like: "What is the difference in local time between Yuma, Arizona and Seattle, Washington on February 2, 1989 at 5:00am?" (Just subtract one SUM() from the other).
Now, the only disadvantage of this approach or any other is that conversions from local time to GMT are not perfect, since any DST change that has a negative offset to the clock repeats a given local time. No easy way to deal with that one, I'm afraid, which is one reason storing local times is bad news in the first place.
I have hit this on two types of systems, “shift planning systems (eg factory workers)” and “gas depend management systems)…
23 and 25 hour long days are a pain to cope with, so are 8hr shifts that take 7hr or 9hr. The problem is you will find that each customers, or even department of the customer have different rules they have created (often without documenting) on what they do in these special cases.
Some questions are best not asked of the customer's until after they have paid for your “off the shelf” software. It is very rare to find a customer that thinks about this type of issue up front when buying software.
I think in all cases you should record time in UTC and convert to/from local time before storing the date/time. However even know which take a given time is in can be hard with Daylight saving and time zones.
For the web, the rules aren't that complicated…
- Server-side, use UTC
- Client-side, use Olson
- Reason: UTC-offsets are not daylight savings-safe (eg New York is EST (UTC – 5 Hours) part of the year, EDT (UTC – 4 Hours) rest of the year).
- For client-side time zone determination, you have two options:
- 1) Have user set zone (Safer)
- Resources: Web-ready Olson tz HTML Dropdown and JSON
- 2) Auto-detect zone
- Resource: jsTimezoneDetect
- 1) Have user set zone (Safer)
The rest is just UTC/local conversion using your server-side datetime libraries. Good to go…
I recently had a problem in a web application where on an Ajax post-back the datetime coming back to my server-side code was different from the datetime served out.
It most likely had to do with my JavaScript code on the client that built up the date for posting back to the client as string, because JavaScript was adjusting for time zone and daylight savings, and in some browsers the calculation for when to apply daylight savings seemed to be different than in others.
In the end I opted to remove date and time calculations on the client entirely, and posted back to my server on an integer key which then got translated to date time on the server, to allow for consistent transformations.
My learning from this: Do not use JavaScript date and time calculations in web applications unless you ABSOLUTELY have to.
Keep your servers set to UTC, and make sure they all are configured for ntp or the equivalent.
UTC avoids daylight savings time issues, and out-of-sync servers can cause unpredictable results that take a while to diagnose.
Be careful when dealing with timestamps stored in the FAT32 filesystem – it is always persisted in local time coordinates (which include DST – see msdn article ). Got burned on that one.
One other thing, make sure the servers have the up to date daylight savings patch applied.
We had a situation last year where our times were consistently out by one hour for a three-week period for North American users, even though we were using a UTC based system.
It turns out in the end it was the servers. They just needed an up-to-date patch applied ( Windows Server 2003 ).
When it comes to applications that run on a server , including web sites and other back-end services, the time zone setting of the server should be ignored by the application.
The common advice is to set the server's time zone to UTC. This is indeed a good best practice, but it's there as a band-aid for applications that do not follow other best practices. For example, a service might be writing to log files with local timestamps instead of UTC-based timestamps, thus creating ambiguities during the daylight saving time fall-back transition. Setting the server's time zone to UTC will fix that application. However the real fix would be for the application to log using UTC to begin with.
Server-side code, including web sites, should never expect the local time zone of the server to be anything in particular.
In some languages, the local time zone can easily creep in to application code. For example, the DateTime.ToUniversalTime
method in .NET will convert from the local time zone to UTC, and the DateTime.Now
property returns the current time in the local time zone. Also, the Date
constructor in JavaScript uses the computer's local time zone. There are many other examples like this. It is important to practice defensive programming, avoiding any code that uses the computer's local time zone setting.
Reserve using the local time zone for client-side code, such as desktop applications, mobile applications, and client-side JavaScript.
Business rules should always work on civil time (unless there's legislation that says otherwise). Be aware that civil time is a mess, but it's what people use so it's what is important.
Internally, keep timestamps in something like civil-time-seconds-from-epoch. The epoch doesn't matter particularly (I favour the Unix epoch ) but it does make things easier than the alternative. Pretend that leap-seconds don't exist unless you're doing something that really needs them (eg, satellite tracking). The mapping between timestamps and displayed time is the only point where DST rules should be applied; the rules change frequently (on a global level, several times a year; blame politicians) so you should make sure that you do not hard-code the mapping. Olson's TZ database is invaluable.
PHP's DateTimeZone::listAbbreviations()
output
This PHP method returns an associative array containing some 'major' timezones (like CEST), which on their own contain more specific 'geographic' timezones (like Europe/Amsterdam).
If you're using these timezones and their offset/DST information, it's extremely important to realize the following:
It seems like all different offset/DST configurations (including historical configurations) of each timezone are included!
For example, Europe/Amsterdam can be found six times in the output of this function. Two occurrences (offset 1172/4772) are for the Amsterdam time used until 1937; two (1200/4800) are for the time that was used between 1937 and 1940; and two (3600/4800) are for the time used since 1940.
Therefore, you cannot rely on the offset/DST information returned by this function as being currently correct/in use!
If you want to know the current offset/DST of a certain timezone, you'll have to do something like this:
<?php $now = new DateTime(null, new DateTimeZone('Europe/Amsterdam')); echo $now->getOffset(); ?>
If you happen to maintain database systems that are running with DST active, check carefully whether they need to be shut down during the transition in fall. Mandy DBS (or other systems as well) don't like passing the same point in (local) time twice, which is exactly what happens when you turn back the clock in fall. SAP has solved this with a (IMHO really neat) workaround – instead of turning back the clock, they just let the internal clock run at half the usual speed for two hours…
Are you using the .NET framework ? If so, let me introduce you to the DateTimeOffset type, added with .NET 3.5.
This structure holds both a DateTime
and an Offset ( TimeSpan
), which specifies the difference between the DateTimeOffset
instance's date and time and Coordinated Universal Time (UTC).
-
The
DateTimeOffset.Now
static method will return aDateTimeOffset
instance consisting of the current (local) time, and the local offset (as defined in the operating system's regional info). -
The
DateTimeOffset.UtcNow
static method will return aDateTimeOffset
instance consisting of the current time in UTC (as if you were in Greenwich).
Other helpful types are the TimeZone and TimeZoneInfo classes.
For those struggling with this on .NET , see if using DateTimeOffset
and/or TimeZoneInfo
are worth your while.
If you want to use IANA/Olson time zones , or find the built in types are insufficient for your needs, check out Noda Time , which offers a much smarter date and time API for .NET.
Here is my experience:-
(Does not require any third-party library)
- On server-side, store times in UTC format so that all date/time values in database are in a single standard regardless of location of users, servers, timezones or DST.
- On the UI layer or in emails sent out to user, you need to show times according to user. For that matter, you need to have user's timezone offset so that you can add this offset to your database's UTC value which will result in user's local time. You can either take user's timezone offset when they are signing up or you can auto-detect them in web and mobile platforms. For websites, JavaScript's function getTimezoneOffset() method is a standard since version 1.0 and compatible with all browsers. (Ref: http://www.w3schools.com/jsref/jsref_getTimezoneOffset.asp )
Actually, kernel32.dll does not export SystemTimeToTzSpecificLocation. It does however export the following two: SystemTimeToTzSpecificLocalTime and TzSpecificLocalTimeToSystemTime…
Just wanted to point out two things that seem inaccurate or at least confusing:
Always persist time according to a unified standard that is not affected by daylight savings. GMT and UTC have been mentioned by different people, though UTC seems to be mentioned most often.
For (almost) all practical computing purposes, UTC is, in fact, GMT. Unless you see a timestamps with a fractional second, you're dealing with GMT which makes this distinction redundant.
Include the local time offset as is (including DST offset) when storing timestamps.
A timestamp is always represented in GMT and thus has no offset.
Tom Scott's video about timezones on YouTube on the Computerphile channel has also a nice and entertaining description of the topic. 例子包括:
- Samoa (an island in the Pacific Ocean) shifting its time zone forward by 24 hours to make trading easier with Australia and New Zealand,
- West Bank , where 2 populations of people are following different time zones,
- 18th century changes from the Julian calendar to the Gregorian calendar (which happened in the 20th century in Russia).
Never rely only on constructors like
new DateTime(int year, int month, int day, int hour, int minute, TimeZone timezone)
They can throw exceptions when a certain date time does not exist due to DST. Instead, build your own methods for creating such dates. In them, catch any exceptions that occur due to DST, and adjust the time is needed with the transition offset. DST may occur on different dates and at different hours (even at midnight for Brazil) according to the timezone.
Just one example to prove that handling time is the huge mess described, and that you can never be complacent. In several spots on this page leap-seconds have been ignored.
Several years ago, the Android operating system used GPS satellites to get a UTC time reference, but ignored the fact that GPS satellites do not use leap-seconds. No one noticed until there was confusion on New Year's Eve, when the Apple phone users and Android phone users did their count-downs about 15 seconds apart.
I think it has since been fixed, but you never know when these 'minor details' will come back to haunt you.
In dealing with databases (in particular MySQL , but this applies to most databases), I found it hard to store UTC.
- Databases usually work with server datetime by default (that is, CURRENT_TIMESTAMP).
- You may not be able to change the server timezone.
- Even if you are able to change the timezone, you may have third-party code that expects server timezone to be local.
I found it easier to just store server datetime in the database, then let the database convert the stored datetime back to UTC (that is, UNIX_TIMESTAMP()) in the SQL statements. After that you can use the datetime as UTC in your code.
If you have 100% control over the server and all code, it's probably better to change server timezone to UTC.