跨时区应用时间处理方案

时间在程序中应用广泛,几乎所有业务都会与时间产生关联。虽然每种程序开发语言都自带处理日期和时间的相关函数,但很多开发者只是把日期和时间简单存入数据库中,并未对存入过程产生任何深入思考,一旦涉及到跨时区的日期和时间的处理时,大多数开发者根本就不明白如何正确地处理日期和时间。

1. 什么是时区

关于时区的定义,我们先看维基百科的定义:

时区,是地球上的区域使用同一个时间定义。以前,人们通过观察太阳的位置(时角)决定时间,这就使得不同经度的地方的时间有所不同(地方时)。1863年,首次使用时区的概念。通过设立一个区域的标准时间部分地解决了这个问题。

世界各国位于地球不同位置上,因此不同国家,特别是东西跨度大的国家日出、日落时间必定有所偏差。这些偏差就是所谓的时差。

简单来说,因为地球上不同地方的人们看到太阳的时间不一样,这就造成了,如果全球都用一个时间表示,那么会出现“中国的日出在 06:00 am,美国日出在 06:00pm”这种严重违背人类认知的情况出现。

为了解决这个问题,人们通过划分区域,把地球上的区域划分成 24 块,每一块代表一个时区,从而保证某些统一行为出现的时间是一致的(比如日出),而不会出现“你的上午是白天,我的上午是夜晚”的情况。

通过划分区域产生的时间也叫做“本地时间”。这个很好理解,因为这个时间是通过本地的时区计算出来的,只能代表这个地区的时间概念。

当然,各地的“本地时间”不可能独自孤立,它们需要另外一个时间来进行彼此关联,这个时间就是“绝对时间”。绝对时间是一个参考量,它是用来联系各个时区本地时间的,这个绝对时间参考标准,主要有 GMT(格林尼治时间) 和 UTC时间(协调世界时)

同时,人们把英国格林尼治天文台旧址规定为零时区(UTC),从这里开始往东/西跨越时区,本地时间分别加/减 1 小时。所以东 8 区的早上 8 点整就可以表示为“2019-03-22 08:00 +08:00”。这个时间跟东 1 区的欧洲本地时间“2019-03-22 01:00 +01:00”是一样的。

2. 什么是时间戳

前面我们提到了,各时区的时间协调需要一个基准,称为 UTC,但是这毕竟只是个名词,怎么量化它?这里就要引入一个新的名词:UNIX 时间。

UNIX 时间是 UNIX 或类 UNIX 系统使用的时间表示方式:从 UTC 1970 年 1 月 1 日 0 时 0 分 0 秒到现在的总秒数。

关于 UNIX 时间存在的一些问题我们不做过多展开,有兴趣自行了解,这里只要记住,它是表示 UTC 时区距离 1970-01-01 00:00:00 的总秒数即可。

3. 正确处理方案

这里就要提到一个设计原则:“数据的存储与显示分离”。

基于这个原则,我们只要在存入数据库之前将时间都统一成相同时区时间(通常是服务器时间),那么在显示的时候就可以根据用户设置的时区格式化为正确的字符串。

这里的统一是应用层面全部统一,包括用户输入,都应该是带时区的本地时间,经过应用层转换成服务器本地时间。

数据的存储和显示相分离是非常基本的设计原则,却常常被大多数开发人员忽略。

4. 总结

基于统一的时区存储,保证了即使是不同时区的用户接入,也能保证相同的绝对时间。至于实际存储是 UNIX 时间还是 UTC 的 datetime,这个完全根据业务决定。