javascript关于“时间”的一次探索

最近使用 sequelize 过程中发现一个“奇怪”的问题,将某个时间插入到表中后,通过 sequelize 查询出来的时间和通过 mysql 命令行工具查询出来的时间不一样。非常困惑,于是研究了下,下面是学习成果。

基本概念

我们先来介绍一些可能当年在地理课上学习过的基本概念。

说起来,时间真是一个神奇的东西。以前人们通过观察太阳的位置来决定时间(比如:使用日晷),这就使得不同经纬度的地区时间是不一样的。后来人们进一步规定以子午线为中心,向东西两侧延伸,每 15 度划分一个时区,刚好是 24 个时区。然后因为一天有 24 小时,地球自转一圈是 360 度,360 度 / 24 小时 = 15 度/小时,所以每差一个时区,时间就差一个小时。

最开始的标准时间(子午线中心处的时间)是英国伦敦的皇家格林威治天文台的标准时间(因为它刚好在本初子午线经过的地方),这就是我们常说的 GMT(Greenwich Mean Time)。然后其他各个时区根据标准时间确定自己的时间,往东的时区时间晚(表示为 GMT+hh:mm)、往西的时区时间早(表示为 GMT-hh:mm)。比如,中国标准时间是东八区,我们的时间就总是比 GMT 时间晚 8 小时,他们在凌晨 1 点,我们已经是早晨 9 点了。

但是 GMT 其实是根据地球自转、公转计算的(太阳每天经过英国伦敦皇家格林威治天文台的时间为中午 12 点),不是非常准确,于是后面提出了根据原子钟计算的标准时间 UTC(Coordinated Universal Time)。

一般情况下,GMT 和 UTC 可以互换,但是实际上,GMT 是一个时区,而 UTC 是一个时间标准。

可以在这里看到所有的时区:https://www.timeanddate.com/time/map/

所以,当我们“展示”某个时间时,明确时区就变得非常重要了。不然你只说现在是 2016-01-11 19:30:00,然后不告诉我时区,我其实是没法准确知道时间的(当然,我可以认为这个时间是我所在时区的当地时间)。如果你说现在是 2016-01-11 19:30:00 GMT+0800,那我就知道这个时间是东八区的时间了。如果我在东八区,那时间就是 19:30,如果我在 GMT 时区,那时间就是 11:30(减掉 8 小时)。

JavaScript 中的“时间”

我们现在来介绍下 JavaScript 中的“时间”,包括:Date、Date.parse、Date.UTC、Date.now。

注:下面的代码示例可以在 node shell 里面运行,如果你运行的时候结果和下面的不一致,那可能咱们不在一个时区:)

Date 构造器

构造时间的方法有下面几种:

new Date(); // 当前时间 new Date(value); // 自 1970-01-01 00:00:00 UTC 经过的毫秒数 new Date(dateString); // 时间字符串 new Date(year, month[, day[, hour[, minutes[, seconds[, milliseconds]]]]]);

需要注意的是:构造出的日期用来显示时,会被转换为本地时间(调用 toString 方法):

> new Date() Mon Jan 11 2016 20:15:18 GMT+0800 (CST)

打印出我写这篇文章时的本地时间。后面的 GMT+0800 表示是“东八区”,CST 表示是“中国标准时间(China Standard Time)”。

有一个很“诡异”的地方是如果我们直接使用 Date,而不是 new Date,得到的将会是字符串,而不是 Date 类型的对象:

> typeof Date() 'string' > typeof new Date() 'object'

时间字符串

我们先说最复杂的时间字符串形式。它实际上支持两种格式:一种是 RFC-2822 的标准;另一种是 ISO 8601 的标准。我们主要介绍后一种。

ISO 8601

ISO 8601的标准格式是:YYYY-MM-DDTHH:mm:ss.sssZ,分别表示:

YYYY:年份,0000 ~ 9999

MM:月份,01 ~ 12

DD:日,01 ~ 31

T:分隔日期和时间

HH:小时,00 ~ 24

mm:分钟,00 ~ 59

ss:秒,00 ~ 59

.sss:毫秒

Z:时区,可以是:Z(UFC)、+HH:mm、-HH:mm

这里我们主要来说下 T、以及 Z。

T

T 也可以用空格表示,但是这两种表示有点不一样,T 其实表示 UTC,而空格会被认为是本地时区(前提是不通过 Z 指定时区)。这里的表述是错误的,T 仅仅是分隔日期和时间的符号,没有其他含义。所以下面的例子其实结果是一样的。

> new Date('1970-01-01 00:00:00') Thu Jan 01 1970 00:00:00 GMT+0800 (CST) > new Date('1970-01-01T00:00:00') Thu Jan 01 1970 00:00:00 GMT+0800 (CST)

这里补充一点需要注意的,时间字符串这种形式有一个特殊的逻辑:如果你不提供“时间”(也就是 T 分隔后的内容),得到的其实是 UTC 时间。比如:

> new Date('1970-01-01') Thu Jan 01 1970 08:00:00 GMT+0800 (CST) > new Date('1970-01-01T00:00') Thu Jan 01 1970 00:00:00 GMT+0800 (CST)

Z

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/bd9fae7e4c237d7bac1657205ddef35c.html