时间错乱
上周在工作中,发现了一个奇怪的问题:
页面表单上有一个选择出生日期的时间选择器,在 macOS 下选择了 1987-05-01 这个日期,保存到服务端之后,在另外一台 win7 系统的电脑中打开的时候服务端返回的数据回填到页面的数据在少了一天变成了 1987-04-30 ,而在 macOS 仍然是正常的,奇怪的是两个平台用的都是 chrome 浏览器。
但是当选择的日期为 2001-07-08 时,在 win7 电脑中却又是正常的,通过反复测试发现日期在 1987年-1992年 这个区间内的时间出现时间减少一天的情况比较频繁,非常诡异,查询资料发现这个问题很有可能与夏时制有关。
什么是夏时制
夏时制,另译夏令时间(英语:Summer time),又称日光节约时制、日光节约时间(英语:Daylight saving time),是一种为节约能源而人为规定地方时间的制度,在这一制度实行期间所采用的统一时间称为“夏令时间”。一般在天亮较早的夏季人为将时间调快一小时,可以使人早起早睡,减少照明量,以充分利用光照资源,从而节约照明用电。各个采纳夏时制的国家规定不同。
1986年至1991年,中华人民共和国在全国范围实行了六年夏时制,每年从4月中旬的第一个星期日凌晨2时整(北京时间)到9月中旬第一个星期日的凌晨2时整(北京夏令时)。除1986年因是实行夏时制的第一年,从5月4日开始到9月14日结束外,其它年份均按规定的时段施行。由于省电效果不抵需要适应时间的弊端,1992年4月5日后不再实行,改为夏季下午工作开始及结束时间比冬季推后半个小时。
通过百科中的资料我们得知 1986-05-04 到 1992-04-05 这个时间段中国实行了夏时制。我们之前出问题的日期确实正好都在这个时间段之内。
不同环境的表现
接下来我试着在 win7 与 macOS 两个系统的 chrome 中寻找他们对于 夏令时 处理上的区别。
在 macOS 中的 chrome 控制台打印 new Date(1987-05-01 00:00:00) 输出如下:

在 win7 中的 chrome 控制台打印 new Date(1987-05-01 00:00:00) 输出如下:

在这两个系统中的 chrome 输出的时间戳相差了 3600000 毫秒也就是一个小时。
macOS 下的 chrome 显示的日期的 GMT+0900(CDT) ,GMT 表示的是格林尼治时间,+0900(CDT) 表示的是 China Daylight Time (中国夏令时间),因为中国处于东八区,所以 中国标准时间 (CST) 是 +0800(CST),又因为夏令时提前一小时,所以变成了最终 +0900(CDT)。
win7 下的 chrome 貌似并没有对时间进行处理。
那么假设我在 macOS 提交表单,而且提交的是时间戳格式的,当我选择
1987-05-01 00:00:00 这个时间,转换成时间戳后变成 546793200000 传到服务端,然后在 win7 的chrome中接受服务端传递过来的时间戳 546793200000 并转换成Date格式:

这个时候时间就变成了 1987-04-30 23:00:00 ,就可以解释文章开头所产生的问题了。
如何解决
先试下如果不用时间戳,而用日期字符串传递话的话会不会有问题。
new Date() 传入的字符串需要满足 RFC-2822 标准或 ISO 8601标准
- ISO 8601 :
1987-05-01T00:00:00Z - RFC-2822 :
1987/05/01 00:00:00+0000
ISO 8601
这里的 Z 表示指定时间的时区为 UTC时区 也就是 零时区,如果要制定其他时区则可用 1987-05-01T00:00:00+0080 这样的格式。
ISO 8601 标准有兼容性问题,如 Safari 下:
1987-05-01T00:00:00+0080会报错Invalid Date1987-05-01T00:00:00Z依然为 本地时区
RFC-2822
RFC-2822 则基本兼容各大浏览器,可以把它当中靠谱的字符串来传递
Moment.js
Date 函数不仅只有上面的坑,而且功能很不完善,连个日期格式化都不支持。
Moment.js 封装了丰富的功能,并且帮我们各种坑,可以愉快地使用它。