2016-08-31

聊聊 JavaScript Date 对象

时间的发现

日常生活中,各种形式的时间字符到处都是。

时间观念的产生,时间单位、计时工具的发明,给人类带来的变化实在一言难尽。

今天就来谈谈日期那些事儿。一起来看看 JavaScript 中的日期对象 Date。

Date 对象

和其他对象如 Math、RegExp 等一样,Date 对象是 JavaScript 语言中的内建(build-in)对象。在工作中,Date 对象有着许多重要的应用。

创建一个 Date 实例很简单,下面简单回顾下常用的方法。

获取当前时间

注意,JavaScript 中的当前时间与操作系统相关。因此,在重要的 Web 应用中,应该会避免使用该时间,更可靠的方式是操作前先请求服务器获取时间,或者将工作直接交给服务端。

生成时间

上面只是最常见的几种形式。实际应用中会发现,Date 对象非常强大,能够解析多种格式的字符串。本文暂且略过不表。

值得一提的是,上面 这种形式应当尽量避免。如果没有记错,iOS 系统环境中,这种格式会报错,遇到该格式的字符串时,应该一律先进行替换操作:

获取或修改更多时间细节

Date 对象提供了一系列 方法供我们使用。方法名也很语义化。在此略过。

眼花缭乱的时间字符串

在工作中,常常会接触各种不同格式的时间字符串。除了那些格式整齐,地球人几乎都能读懂的之外,还有一些不那么为普通人所了解的格式。

与此同时,细心的同学可能注意到,在控制台中输入一个 Date 变量的引用,按下 的那一刻,会有一大堆属性、方法提示。除去 这一类方法之外,还有一堆 形式的方法。相信多数同学用得不多。

往前数月,我也不太关注这些东西。但后来某次,后端返回的数据总是随机地有一些数据是 这种格式的。当时不明白其含义,只能自作聪明地拿正则表达式来匹配,作为容错方案。

接下来,自己的一个小爬虫工具想要支持 rss 解析。拿到的不少日期数据是 这种格式的。

由此我开始试着去了解这些看上去奇奇怪怪的日期格式。

接下来,主要通过 JavaScript 中的 系列方法,了解这些时间字符串。

一堆 方法

首先,让我们写个简单的脚本,看看 Date 对象到底有哪些 方法。

注意,这些方法是无法通过 循环取到的,也就是说,默认是

使用 方法可以查看这些细节。

为例,一起看下:

结果如下:

既然不能通过 遍历,那还有没有其他办法呢?

有的。 这个方法可以帮我们拿到对象自身属性的 key 值。

(写到这里,虽然看上去很啰嗦,但我总觉得有必要把细节记下来。万一下次又记不清了呢。)

接着,就可以愉快地取到所有的 方法名了。

一共有 9 个,如下:

看名称大概也能知道,这 9 个方法可以分为三组。下面按组来细看。

toString 系列

接下来,我们所有的实验,统一使用一个 Date 实例。需要说明的,我所使用的是 Chrome 52,所有实验都是在控制台中进行的。

一并展示出所有结果吧,就是这么简单粗暴。

toString

注意到, 的结果是一样的。这不是偶然,它们实际上是等价的。这涉及到 JavaScript 中的隐式类型转换

根据 ECMA-262 标准, 执行的结果,是实现相关的(implementation-dependent)。

为例,看看标准中的是如何说的:

该方法返回一个 String 类型的值。该值是实现相关的,但其目的是以一种简便、便于阅读的形式,展示 Date 实例在当前时区内的“日期”部分。 —— ECMA-262 7ᵗʰ Edition

再来看看结果中的 是什么鬼。所谓 ,是英文 “Greenwich Mean Time” 的缩写,完整翻译过来就是“格林尼治平时”,也就是通常所说的“格林尼治时间”,即位于英国伦敦郊区的皇家格林尼治天文台的标准时间。详细信息可以查看维基百科

至于“+0800”,则是时区的概念了。这意味着,当前时间与标准时区相差 8 小时。

比如,此刻是北京时间 2016 年 9 月 2 日 13:07:22,也就是 ,那么此时,格林尼治时区的时间就应该是 05:07:22。不过,从世界时钟中此时英国伦敦的时间是 06:07:22。为何会有此差别?因为 9 月 2 号的时候,伦敦使用的是 BST 时区,即 British Summer Time,也就是众所周知的“夏令时”。

时区图

像 BST、CST 这些标志,就像上面表格中的“中国标准时间”一样。这些是用来说明时区的,通常用缩写表示,不过这并不是标准。CST 正好就是中国标准时间(China Standar Time)的缩写,可以参考 timeanddate 这个网站。

不过,上面仅仅是提到格林尼治时间。并不意味着真正用到了它。JavaScript 中实际使用到的,还是 UTC 时间。

toLocaleString 系列

执行结果如下图所示:

toLocaleString 系列

也很好理解。这三个方法,也是和实现相关的。[标准]中有一句很关键,“与宿主环境当前区域设置的约定保持一致”("corresponds to the conventions of the host environment's current locale.")。

标准时间系列

接着看 三个方法。按照惯例,先看结果:

toISOString() 与 toUTCString()、toGMTString()

这些年,想必各种商业广告已经帮我们普及了 ISO 的概念,八九岁的时候就知道,某某品牌的摩托车号称通过 ISO-2001 标准。ISO,全称是国际标准化组织(International Organization for Standardization),负责制定全世界工商业国际标准的国际标准。

JavaScript 标准定义的时间交换格式(interchange format),是基于 ISO 8601 扩展格式的简化版本,格式是 “YYYY-MM-DDTHH:mm:ss.sssZ”。 返回的就是这样的字符串。

只是一个字面量,标志着接下来的内容是时间(相对于前面的日期而言)。

标志着时差。直接使用 ,意味着我们使用的是标准时间(UTC)。

在我们的例子中, 的结果是 ,可以看到,和我们的实际时间 10:49:22 相差了 8 小时。

当然, 的位置上,还可以使用 的形式。这样就是直接指定与标准时间的时差了。

例如, 作为标准时间,相当于北京时间的 10:49:22,换一种形式就是

UTC 是“世界标准时间”的简称,又作“协调世界时” “世界协调时间”,英文是 Coordinated Universal Time。下面引用下维基百科中的说明:

协调世界时是世界上调节时钟和时间的主要时间标准,它与0度经线的平太阳时相差不超过1秒,并不遵守夏令时。协调世界时是最接近格林尼治标准时间(GMT)的几个替代时间系统之一…… UTC基于国际原子时,并通过不规则的加入闰秒来抵消地球自转变慢的影响。

我们注意到, 两者返回的字符串是一样的。实际上,这还是和具体实现有关。

还是引用维基百科中的一段,来看看 UTC 时间和格林尼治时间的不同。

理论上来说,格林尼治标准时间的正午是指当太阳横穿格林尼治子午线时(也就是在格林尼治上空最高点时)的时间。由于地球在它的椭圆轨道里的运动速度不均匀,这个时刻可能与实际的太阳时有误差,最大误差达16分钟。 由于地球每天的自转是有些不规则的,而且正在缓慢减速,因此格林尼治时间已经不再被作为标准时间使用。现在的标准时间,是由原子钟报时的协调世界时(UTC)。

ES 2016 中提到, 主要是用来满足旧代码兼容性的,新代码中推荐使用 。标准还提到这么一句:

The function object that is the initial value of Date.prototype.toGMTString is the same function object that is the initial value of Date.prototype.toUTCString.

也就是说,在 JavaScript 中, 这俩是一样的。

日期提取工具

从字符串中提取时间,已经有很多工具,还相当智能,英文不必说,中文的“前天”“五天前”“上周三”之类的不在话下。

临时应急,也仅仅是为了辨别一些常用的日期字符串,我也写了一个小工具,主要是提取日期。

关键的正则表达式如下:

详细代码,可见 Github。之后有时间,也会考虑加入更智能的识别功能。

参考