一些概念
Date是什么?
Date是JavaScript用来操作日期时间的对象,以常规函数调用它,会返回一个代表当前时间的字符串,而不是一个日期对象,即:
typeof Date() // "string"
typeof (new Date()) // "object"
除了文献[6]中提到的返回是否依赖参数之外,返回的类型也有差别,因此,可以更严谨地说明:Date()无论参数如何都只返回当前时间的字符串,而new Date()则会根据参数返回相应时间对象(打印出来的是toString方法执行的结果)。
JavaScript的时间戳又是什么?
JavaScript的时间戳定义为从格林威治时间1970年01月01日00时00分00秒
(北京时间1970年01月01日08时00分00秒
)起到现在的总毫秒数,对于每一个时刻而言,都有独一无二的时间戳与它对应。
时间戳,无关乎时区,对于同一时刻而言,不同时区虽然表现的时间不相同,但时间戳是相同的。
想象这样一个场景:当前是格林威治时间2018-07-20 00:00:00
,服务端位于中国(东八区,时间是2018-07-20 08:00:00
),前端位于洛杉矶(西七区,时间是2018-07-19 18:00:00
),而前端的页面想要显示出服务端的日期,如果两端之间通过时间戳1532044800000来传递,服务端是7月20日,而客户端显示的则是7月19日,这并不符合我们的预期,所以正确的应该传递服务端所在时区的日期“2018-07-20
”才能满足我们的需求。
UTC 与 GMT
UTC(Coordinated Universal Time,以UTC为缩写是英文与法文的折衷缩写)协调世界时间,是标准时间,以经过平均太阳时(以格林威治时间GMT为准),地轴运动修正后的新时标以及以“秒”为单位的国际原子时所综合精算而成的时间。不过因为时区的影响,没有国家会直接使用它作为当地时间的。
GMT(Greenwich Mean Time,以GMT为缩写)格林威治时间,实际上是一个时区(time zone),以时间来看的话,是英国伦敦郊区的皇家格林威治天文台的标准时间(零时区),由于零时区的特殊性常与UTC混用,因此在使用上GMT与UTC代表的也是相同的时间。
Date构造函数的参数们
Date构造函数的参数可以分为四类:
- 无参数
- (单参数)时间戳
- (单参数)时间字符串
- 至少三个参数以上,代表年、月、日、时、分、秒、毫秒
无参数、多参数和时间戳的情况比较简单,注意传参的类型是Number就行了,而字符串的方式则比较复杂,我们单独拿出来研究。
时间字符串格式标准共有两种:ISO 8601和RFC 2822。
ISO 8601标准[3]
ISO 8601标准格式“YYYY-MM-DDTHH:mm:ss.sssTZD
”,其中:
-
YYYY-MM-DD
是年月日,HH:mm:ss.sss
是时分秒毫秒; -
TZD
代表时区(Chrome默认为“Z”,Safari默认为当前时区),可以是“Z”即为标准时间UTC,或者是“±hh:mm”; -
T
是日期与时间的区分: - 如果
T
换成空格,Chrome中则与有T时表示的相同,Safari则作为“Invalid Date”;
- 如果
举个栗子,对比一下2018年7月20日零点在东八区(或默认时区)和GMT的不同:
new Date('2018-07-20T00:00:00+00:00') // Fri Jul 20 2018 08:00:00 GMT+0800 (中国标准时间) --> 标准时间的0点对应东八区的8点
new Date('2018-07-20T00:00:00+08:00') // Fri Jul 20 2018 00:00:00 GMT+0800 (中国标准时间) --> 与 new Date('2018-07-20T00:00:00')一致
new Date('2018-07-20 00:00:00') // Chrome中:Fri Jul 20 2018 00:00:00 GMT+0800 (中国标准时间)
new Date('2018-07-20 00:00:00') // Safari中:Invalid Date
RFC 2822标准[4]
RFC2822标准格式“day-of-week DD month-name YYYY HH:mm:ss ±hhmm
”,但DD与month-name交换的格式也是可以被识别的。
举个栗子,Chrome和Safari表示时间的格式就是这个标准,如:
new Date('Fri 20 Jul 2018 00:00:00 +0800') // Fri Jul 20 2018 00:00:00 GMT+0800 (中国标准时间)
另外,还有一种格式“YYYY/MM/DD
”,我没找到标准文档,因为文献[5]中提到与ISO8601日期格式在默认时区上有些不同,这里也单独列出来:
new Date('2018/07/21') // Sat Jul 21 2018 00:00:00 GMT+0800 (中国标准时间)
new Date('2018-07-21') // Sat Jul 21 2018 08:00:00 GMT+0800 (中国标准时间)
相同的情况,文献[2]也有说明:
given an ISO format such as “2014-03-07” it will assume a time zone of UTC (ES5 and ECMAScript 2015).
Date的getter们
有了本文前面的知识,Date的getter会更容易理解一些。
- toISOString:返回完整的ISO8601格式,如“2018-07-19T16:00:00.000Z”;
- toUTCString(toGMTString):以RFC2822格式返回标准时间的时间字符串,如“Thu, 19 Jul 2018 16:00:00 GMT”;
- toLocaleDateString:返回当前时区的日期部分,以“YYYY/MM/DD”这种格式,如“2018/7/20”;
- toLocaleTimeString:返回当前时区的时间部分,以“上午/下午 hh:mm:ss”格式,如“上午 12:00:00”(这里指的其实是0点);
- toLocaleString:返回当前时区的完整时间,如“2018/7/20 上午12:00:00”;
- toDateString:返回RFC2822格式中日期部分,如“Fri Jul 20 2018”;
- toTimeString:返回RFC2822格式中时间部分,如“00:00:00 GMT+0800 (中国标准时间)”
- toString:返回RFC2822格式的完整时间,如“Fri Jul 20 2018 00:00:00 GMT+0800 (中国标准时间)”;
- valueOf:返回Number格式的时间戳,如“1532016000000”;
总结
在时间的漩涡里挣扎了一个多星期,终于把文章写完了。这一切还是源于“JavaScript的时间戳又是什么?”里提到的场景,原本我天真的以为每个时区都有自己的时间戳,但实际上并非如此。会有这样的偏差也是概念不够清晰理解的缘故,当我意识到这点就开始找资料调研起Date对象。标准文档还是一样抽象难啃,更坑爹的是一个小小的Date对象都有这么多的标准和差异,感受真的如同文献5的标题“What A Mess!”。
最后列一下浏览器版本吧:
- Chrome版本:版本 67.0.3396.99(正式版本) (64 位)
- Safari版本:11.0.2 (13604.4.7.1.6)
参考文献
- 《The Difference Between GMT and UTC》https://www.timeanddate.com/t…
- https://developer.mozilla.org…
- ISO 8601 https://www.w3.org/TR/NOTE-da…
- RFC 2822 https://tools.ietf.org/html/r…
- 《JavaScript and Dates, What a Mess!》http://blog.dygraphs.com/2012…
- 《JS原生Date类型方法的一些冷知识》https://segmentfault.com/a/11…