要做:
datetimeoffset
类型。1970-01-01T00:00:00Z
(不包括闰秒)开始的整秒数。如果需要更高的精度,请改用毫秒。此值应始终基于UTC,不进行任何时区调整。DateTimeOffset
通常比DateTime
更好。DateTime
和DateTimeZone
类提供的本机时区转换。在使用DateTimeZone :: listAbbreviations()
时要小心 - 请参阅答案。要使PHP保持最新的Olson数据,请定期安装the timezonedb PECL软件包;请参阅答案。<chrono>
库。
> =
,<
)。不要:
America/New_York
)和“时区偏移量”(例如-05:00
)。它们是两个不同的概念。请参见时区标签 wiki。Date
对象进行日期和时间计算,因为 ECMAScript 5.1 及更低版本存在设计缺陷可能会错误地使用夏令时。(这在 ECMAScript 6 / 2015 中已修复)。测试:
参考:
timezone
标签wiki页面dst
的问题timezone
的问题其他:
我不确定在上面的答案中能添加什么,但是这里有几点需要注意:
需要考虑四种不同的时间:
还有历史/备用时间。 这些很烦人,因为它们可能无法映射回标准时间。 例如:朱利安日,土星上的阴历日期,克林贡历法。
在UTC中存储开始/结束时间戳非常有效。 对于1,您需要将事件时区名称+偏移量与事件一起存储。 对于2,您需要为每个区域存储本地时间标识符和每个观众存储本地时区名称+偏移量(如果您处于危机之中,则可以从IP中推导出这一点)。 对于3,以UTC秒为单位存储即可,无需时区。 4是1或2的特殊情况,具体取决于它是全球事件还是当地事件,但您还需要存储一个“创建于”时间戳,以便您可以判断时区定义是在此事件创建之前还是之后更改的。 如果需要显示历史数据,则必要。
以上内容的示例为:
2010年世界杯决赛在南非(UTC+2--SAST)于2010年7月11日19:00 UTC举行。
通过这些信息,我们可以历史性地确定2010年WCS决赛确切的时间,即使南非时区定义发生更改,并且能够在当用户查询数据库时将其显示为他们当地的时区。
您还需要确保操作系统、数据库和应用程序的tzdata文件保持同步,彼此之间以及与世界其他地方的时间保持一致,并在升级时进行广泛测试。不是没有先例,你依赖的第三方应用程序没有正确处理TZ更改。
确保硬件时钟设置为UTC,如果您在全球范围内运行服务器,请确保它们的操作系统也配置为使用UTC。当您需要从多个时区的服务器复制每小时轮换的Apache日志文件时,这变得明显起来。仅按文件名排序仅在所有文件都以相同的时区命名时才起作用。这还意味着在从一个框到另一个框进行ssh并需要比较时间戳时,您不必在脑海中进行日期数学运算。
此外,在所有盒子上运行ntpd。
Date.getTime()
调用。当用作不透明标识符或在同一客户端的单个会话期间进行日期计算时,这些内容是可以使用的,但是不要尝试将这些值与服务器上的某些内容交叉引用。您的客户端没有运行NTP,并且其BIOS时钟可能没有工作电池。最后,政府有时会做非常奇怪的事情:
荷兰的标准时间从1909-05-01到1937-06-30被法律明确规定为UTC正好快19分32.13秒。无法使用HH:MM格式准确表示此时区。
好了,我完成了。
DateTimeOffset
(或等价物)派上用场的地方。除此之外,写得很好。 - Matt Johnson-Pint这是一个重要且令人惊讶的难题。事实上,并没有完全令人满意的持久化时间标准。例如,SQL标准和ISO格式(ISO 8601)显然是不够的。
从概念上讲,人们通常处理两种类型的时间日期数据,并且方便地区分它们(上述标准没有):“物理时间”和“民用时间”。
“物理”时间瞬间是物理学所涉及的连续通用时间线上的点(当然忽略相对论)。例如,这个概念可以在UTC中被充分编码-持久化(如果您可以忽略闰秒)。
“民用”时间是遵循民用规范的日期时间规范:此处的某个时间点由一组日期时间字段(Y、M、D、H、MM、S、FS)加上TZ(时区规范)(实际上也是“日历”;但假设我们将讨论限制在格里高利日历上)来完全指定。 时区和日历联合允许(原则上)从一种表示到另一种表示的映射。 但是,民用和物理时间瞬间是根本不同类型的量,应在概念上分开并以不同方式处理(类比:字节数组和字符字符串)。
这个问题很令人困惑,因为我们可以互换地使用这些类型的事件,并且民用时间受政治变化的影响。 对于未来的事件,问题(和需要区分这些概念)变得更加明显。例如(摘自我的讨论here)。
John在他的日历中记录了一个提醒事件的日期时间:2019年7月27日,10:30:00
,TZ=Chile/Santiago
,(其偏移量为GMT-4,因此对应于UTC2019年7月27日14:30:00
)。 但是,在未来的某一天,该国决定将TZ偏移更改为GMT-5。
现在,当这一天到来时...... 提醒应该触发吗?
A) 2019年7月27日10:30:00智利/圣地亚哥时区
= UTC时间2019年7月27日15:30:00
?
还是
B) 2019年7月27日9:30:00智利/圣地亚哥时区
= UTC时间2019年7月27日14:30:00
?
除非知道约翰在他告诉日历“请在2019年7月27日10:30:00 TZ=Chile/Santiago
提醒我”时在概念上意味着什么,否则没有正确答案。
他是指“民用日期时间”(“当我所在城市的时钟显示为10:30时”)吗?如果是这样,那么A)就是正确答案。
还是他指的是“物理时间点”,我们宇宙时间的连续线上的一点,例如,“下一次日食发生时”。在这种情况下,答案B)是正确的。
一些日期/时间API正确地处理了这个区别:其中包括Jodatime,它是下一个(第三个!)Java DateTime API(JSR 310)的基础。
清晰地划分关注点架构 - 确定哪个层与用户交互,并且必须将日期时间更改为/从规范表示(UTC)。非UTC日期时间是演示文稿(遵循用户本地时区),UTC时间是模型(对于后端和中间层保持唯一)。
此外,决定您的实际受众、您不必服务的内容以及拉出线条的位置。除非您确实在那里有重要客户,否则不要涉及奇异的日历,然后考虑为该地区单独提供面向用户的服务器。
如果您可以获取并维护用户的位置,请使用位置进行系统化的日期时间转换(例如.NET culture或SQL表),但为最终用户提供选择覆盖的方式,如果日期时间对您的用户至关重要。
如果有历史审计义务(例如告知何时Jo在2年前的9月支付了账单),则同时保留UTC和本地时间以备记录(您的转换表将随着时间而变化)。
定义数据的时间参考时区,如文件、Web服务等批量进货 - 例如东海岸公司在加利福尼亚州有数据中心 - 您需要询问并知道他们使用的标准,而不是假设其中之一。
不要相信嵌入日期时间文本表示中的时区偏移量,也不要接受解析和遵循它们。 相反,始终请求明确定义时区和/或参考区域。您可以轻松获得具有PST偏移量的时间,但实际上该时间是EST,因为这是客户端的参考时间,而记录只是在位于PST的服务器上导出的。
C标准库接口对这种情况并不是非常有用。
由于A D Olson即将退休,以及维护人员因版权侵权而被起诉(现已被驳回),Olson数据已经移动。时区数据库现在由IANA(互联网数字分配机构)管理,并且在首页上有一个指向'时区数据库'的链接。讨论邮件列表现在是tz@iana.org
;公告列表为tz-announce@iana.org
。
zic.8
手册页(或zic.8.txt
)中找到。您需要获取tzcode2011i.tar.gz
文件,而不是或者同时也需要获取tzdata2011i.tar.gz
文件才能获得这些信息。 - Jonathan Leffler通常,在存储时间戳时包括本地时间偏移量(包括DST偏移量):如果您稍后想要在原始时区(和夏令时设置)中显示时间戳,则仅使用UTC是不够的。
请注意,时区偏移量并不总是整数小时(例如,印度标准时间为UTC+05:30)。
例如,合适的格式可以是一个元组(Unix时间,分钟偏移量)或ISO 8601。
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。只在最后一刻转换为本地时间。
在测试时,请确保测试西半球和东半球的国家,无论是否使用夏令时,以及不使用夏令时的国家(共6个)。
针对PHP:
PHP > 5.2 版本中的 DateTimeZone 类已经基于 Olson 数据库,这一点也被其他人提到过,因此如果您正在 PHP 中进行时区转换而不是在数据库中进行,那么您可以免去使用难以理解的 Olson 文件。
但是,PHP 的更新频率不如 Olson 数据库高,因此仅使用 PHP 的时区转换可能会让您的 DST(夏令时)信息过时,并影响数据的正确性。虽然这种情况不会经常发生,但如果您有全球大量用户,则可能发生,并且一定会发生。
为了解决上述问题,请使用 timezonedb pecl 包。它的功能是更新 PHP 的时区数据。尽可能经常地安装该包以保持最新。 (我不确定此包的更新是否完全遵循 Olson 的更新,但它似乎更新频率至少非常接近 Olson 更新的频率。)
now()
日期就越重要,因此如果你可以用相对格式表示日期/时间,对于+/- 1或2周的日期,其余日期可以是UTC,对95%的用户来说并不会太重要。虽然我没有尝试过,但我认为一个有吸引力的时区调整方法应该是:
将所有内容存储在UTC中。
创建一个名为TZOffsets
的表,其中包含三列:RegionClassId、StartDateTime和OffsetMinutes(int,以分钟为单位)。
在表中,存储一份日期和时间列表,列表中包括本地时间何时以及更改了多少。表中的区域数量和日期数量取决于您需要支持的日期范围和世界各地的区域。尽管这些日期应该包括未来某个实际限制的日期,但可以将其视为“历史”日期。
当您需要计算任何UTC时间的本地时间时,只需执行以下操作:
SELECT DATEADD('m', SUM(OffsetMinutes), @inputdatetime) AS LocalDateTime
FROM TZOffsets
WHERE StartDateTime <= @inputdatetime
AND RegionClassId = @RegionClassId;
GETDATE()
将会是 UTC 时间(DateTime.Now
也是)。而且服务器不会受到任何自动 DST 更改的影响。 - Oded