TL;DR
new Date
和 Date.parse
在格式化某些日期字符串的时候,时区具有不确定性,最好用 moment.js 这类工具去处理。
不确定的日期字符串
事情的起源是客户跟我说网页上的某个日期总是比实际日期少一天。经过一步步 debug 后发现问题在此:
js
// 客户时区为美国东部时区夏令时 new Date("2015-03-31") // Mon Mar 30 2015 20:00:00 GMT-0400 (EDT)
明明写的 31 号,为什么生成的对象是 30 号的?因为 new Date
把它解析为 2015-03-31 00:00:00 ,时区为 UTC 。美国东部时区是减 4 小时的,于是就变成了前一天 20:00:00 。
那么 new Date
传入的时间字符串有没有规律可循呢?
混乱的规律
new Date
和 Date.parse
使用的是同样的解析规律,只是一个返回 Date object 另一个返回毫秒数。为了方便查看结果,以下例子只用 new Date
。但请记住它们遵循一样的规律。
new Date
可以传入一个日期字符串来生成对象的,官方规定日期字符串需要符合 RFC2822 或者 ISO8601 的格式。拿上面的日期举个例子,前者可以写成 "Mar 31 2015" 后者可以写成 "2015-03-31" 。
如果日期字符串不符合这两种标准,new Date
对结果概不负责……
不过就算符合标准了,结果还是有点不同的。看几个例子:
js
new Date("Mar 31 2015") // Tue Mar 31 2015 00:00:00 GMT-0400 (EDT) new Date("2015-03-31") // Mon Mar 30 2015 20:00:00 GMT-0400 (EDT)
RFC2822 的格式如果不带时区,new Date
会当做本地时区处理,而 ISO8601 格式则会当做 UTC 时区处理。
是有点绕人,但只要记住这个规律不就完了吗?骚年你太天真了…… 因为 ES6 草案为了简化这种情况,规定所有不带时区的字符串都默认为本地时区。注意这是草案,所以结果你懂的。
解决方案
一种解决方案是每次格式化日期都严格指定时区,以防止各种幺蛾子情况出现,比如:
js
new Date("2015-03-31T00:00:00-04:00") // Tue Mar 31 2015 00:00:00 GMT-0400 (EDT)
不过鉴于人都是懒惰的,这种情况交给工具做更靠谱,比如 moment.js 。
js
moment("2015-03-31").toDate()
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。