JavaScript 为我们提供了不是很好用的 Date 作为时间日期对象,除了接口设计怪异之外,还有一些隐藏很深的 bug,先看接口设计:
Date() 直接返回当前时间字符串,无参数。
new Date() 则是会根据参数来返回对应的时间对象,参数很有意思:
// 无参数,并返回当前时间。
new Date();
// 可接受一个数字参数,该参数表示与 1970 年 1 月 1 日 0 点之间的毫秒数。
new Date(value);
// 可接受一个字符串参数,参数形式类似于 Date.parse() 方法。
new Date(dateString);
// 可接受年月日时分秒参数,是本地时间。
new Date(year, month[, day[, hour[, minutes[, seconds[, milliseconds]]]]]);
先且不说这样的接口未免不够灵活(很难处理各种字符串格式的时间),最诡异的一点是其中使用 dateString 的那个构造方法,MDN 网站在这个函数下有下面一段注释:
注意这里用的是 strongly discouraged – 强烈不推荐使用,原先的我没有重视这句话,直到。。。付出惨痛的代价。
现象
我的一款每日打卡类的 App,因为有大量的时间运算,为了能接受各种时间,我做了一个方便的函数:
date2ymd(input) {
let day = (input instanceof Date) ? input : new Date(input)
return `${day.getFullYear()}-${day.getMonth() + 1}-${day.getDate()}`
}
上面的函数在某些 input 时没有问题,但当参数为类似’2018-10-1’这种格式,按照 MDN 的文档,就属于强烈不推荐使用的形式,但我没有留意,App 发布之后,不错,大部分用户没有反馈问题,直到某天,一个阿根廷的用户给我邮件反馈,说界面上的日历似乎错乱了,我试着更改自己的时区到对应的 - 3 时区,截图一看:
上图中彩色小方块的区域是年视图,本来应该和下面的日历一一对应,但在这个时区下,年视图完全错位了,肯定是哪里计算错误了,经过调试,我找到了上面这个函数,并发现了问题,我们用 chrome 的 console 演示一下:
看到了吗?从 9 号到 10 号,转换出的时间没有翻天,当然,我们可以为这种转换找到理由,JS 应该是把这种参数当成 UTC 时间了,我们看看 firefox:
Firefox 的表现可以理解,因为 UTC 和 -3:00 时区之间有个时间差,虽然这依然不是我想要的,但比起 chrome 的跳变要好接受一些,我们把输入时间换一个格式,再看下在 chrome 下的例子:
这种格式终于符合了正常逻辑,也就是短杠和斜杠分隔的时间处理上是不一致的,斜杠分隔的时间终于正确的按照了本地时间进行处理。
结论
正如 MDN 所说,不要使用单字符串参数的 Date 构造函数,即使你知道各种格式之间的区别,也不建议使用,毕竟记忆这些微妙,甚至不兼容的差别毫无意义。