资讯专栏INFORMATION COLUMN

聊聊 JavaScript 中的 Date 对象

adie / 3839人阅读

摘要:一起来看看中的日期对象。对象和其他对象如等一样,对象是语言中的内建对象。在工作中,对象有着许多重要的应用。这意味着,当前时间与标准时区相差小时。,全称是国际标准化组织,负责制定全世界工商业国际标准的国际标准。

原文地址: http://www.wemlion.com/2016/d...

时间的发现

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

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

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

Date 对象

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

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

获取当前时间

var now = new Date();

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

生成时间

// 2016-08-31 23:27:22
new Date(2016, 7, 31, 23, 27, 22);
new Date("2016-08-31 23:27:22");
new Date("2016/08/31 23:27:22");

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

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

var string = "2016-08-31";
var date = new Date(string.replace(/-/g, "/"));

获取或修改更多时间细节

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

眼花缭乱的时间字符串

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

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

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

接下来,自己的一个小爬虫工具想要支持 rss 解析。拿到的不少日期数据是 Thu Aug 25 2016 01:31:50 GMT+0800 (CST) 这种格式的。

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

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

一堆 to***String 方法

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

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

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

toString 为例,一起看下:

Object.getOwnPropertyDescriptor(Date.prototype, "toString");

结果如下:

{
    writable: true,
    enumerable: false,
    configurable: true,
    value: function toString(),
    __proto__: Object
}

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

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

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

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

let proto = Date.prototype;
let names = Object.getOwnPropertyNames(proto).filter((name) => /^to[a-zA-Z]*String/.test(name));
console.log(names);

一共有 9 个,如下:

[ "toString",
  "toDateString",
  "toTimeString",
  "toISOString",
  "toUTCString",
  "toGMTString",
  "toLocaleString",
  "toLocaleDateString",
  "toLocaleTimeString"
]

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

toString 系列

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

// 2016-09-02 10:49:22
var date = new Date(2016, 8, 2, 10, 49, 22);

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

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

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

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

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

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

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

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

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

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

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”。toISOString() 返回的就是这样的字符串。

T 只是一个字面量,标志着接下来的内容是时间(相对于前面的日期而言)。Z 标志着时差。直接使用 Z,意味着我们使用的是表示时间(UTC)。在我们的例子中,date.toISOString() 的结果是 2016-09-02T02:49:22.000Z,可以看到,和我们的实际时间 10:49:22 相差了 8 小时。

当然,Z 的位置上,还可以使用+HH:mm -HH:mm 的形式。这样就是直接指定与标准时间的时差了。例如,2016-09-02T02:49:22.000Z 作为标准时间,相当于北京时间的 10:49:22,换一种形式就是 2016-09-02T10:49:22.000+08:00

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

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

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

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

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

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

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 中,toUTCString() toGMTString() 这俩是一样的。

assertEqual(date.toGMTString, date,toUTCString);
日期提取工具

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

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

关键的正则表达式如下:

// 2012 年 2 月 28 日
re_zh  = /(d{4})s*[^x00-xff]s*(d{1,2})s*[^x00-xff]s*(d{1,2})s*[^x00-xff]/,

// 2012-02-28, 2012.02.28, 2012/02/28
re_ymd = /d{4}([/-.])d{1,2}(1)d{1,2}/,

// 02/28/2012 etc.
re_mdy = /d{1,2}([/-.])d{1,2}(1)d{4}/,

re_en  = new RegExp([
    // toUTCString(): "Tue, 30 Aug 2016 03:01:19 GMT"
    /(w{3}), (d{2}) (w{3}) (d{4}) ((d{2}):(d{2}):(d{2})) GMT/.source,

    // toString():  "Tue Aug 30 2016 11:02:45 GMT+0800 (中国标准时间)"
    /(w{3}) (w{3}) (d{2}) (d{4}) ((d{2}):(d{2}):(d{2})) GMT+d{4}/.source,

    // toISOString(): "2016-08-30T03:01:19.543Z"
    /(d{4})-(d{2})-(d{2})T((d{2}):(d{2}):(d{2})).(d{3})Z/.source,
    
    // toDateString(): "Tue Aug 30 2016"
    /(w{3}) (w{3}) (d{2}) (d{4})/.source
].join("|"), "m");

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

参考

http://www.timeanddate.com/

wikipedia

ECMAScript® 2016 Language Specification

http://wwp.greenwichmeantime.com/

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/80271.html

相关文章

  • 聊聊webWorker

    摘要:不过,这并不意味着语言本身就支持了多线程,对于语言本身它仍是运行在单线程上的,只是浏览器宿主环境提供的一个能力。主线程与子线程之间也可以交换二进制数据,比如等对象,也可以在线程之间发送。 先看几个例子 本例子是通过通过红点展示地球上的地震带,数据来自于地质探测局通过console.log看到数据运算所耗的时间不使用 webworker No web workers - all on ...

    luffyZh 评论0 收藏0
  • 聊聊Java的泛型及实现

    摘要:静态变量是被泛型类的所有实例所共享的。所以引用能完成泛型类型的检查。对于这个类型系统,有如下的一些规则相同类型参数的泛型类的关系取决于泛型类自身的继承体系结构。事实上,泛型类扩展都不合法。 前言 和C++以模板来实现静多态不同,Java基于运行时支持选择了泛型,两者的实现原理大相庭径。C++可以支持基本类型作为模板参数,Java却只能接受类作为泛型参数;Java可以在泛型类的方法中取得...

    lewif 评论0 收藏0
  • PHP中的日期时间处理利器-Carbon

    摘要:介绍是中很人性化的时间日期处理插件,拥有接近个。最大的特点就是灵活人性化。 Carbon介绍 Carbon是PHP中很人性化的时间日期处理插件,github拥有接近5000个 star。github地址为:https://github.com/briannesbi... Carbon基本用法 //1、基本应用 $now = Carbon::now(); ...

    edagarli 评论0 收藏0
  • 简单聊聊DOM

    摘要:讨论在如何去使用元素用于在页面中嵌入或引入脚本代码,该元素默认被定义在元素中页面内容该属性定义规定的文本类型,可以为类型也可以为类型等其他类型和属性类似,这个属性定义脚本使用的语言,该属性不是标准规范定义引用外部脚本的内嵌代码与内 讨论在HTML如何去使用JavaScript 元素用于在HTML页面中嵌入或引入JavaScript脚本代码,该元素默认被定义在元素中 HTML页面内容: ...

    MASAILA 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<