基础知识
JavaScript 官方支持 ISO 8601 扩展格式的简化版。格式如下:YYYY-MM-DDTHH:mm:ss.sssZ
。字母 T
是日期时间分隔符,Z
是时区偏移量,指定为 Z
(表示 UTC)或者是 +
或 -
后跟一个时间表达式 HH:mm
。
该格式的某些部分(例如时间)可以省略。
请注意,年份必须至少有四位数字,月/日/小时/分钟/秒钟必须恰好有两位数字,毫秒必须恰好有三位数字。例如,99-1-1
不是有效的日期字符串。
以下是一些有效的日期(时间)字符串示例:
2018-12-30
2018-12-30T20:59
2018-12-30T20:59:00
2018-12-30T20:59:00.000Z
2018-12-30T20:59:00.000+01:00
2018-12-30T20:59:00.000-01:00
当您省略时间区偏移时,日期时间将被解释为用户本地时间。
当您完全省略时间时,日期将被解释为UTC。
重要提示:
所有现代和相当旧的浏览器和实现都支持规范中的完整日期时间格式。
然而,在处理没有时间区的日期(时间)字符串时存在差异(有关详细信息,请参见下面的“缺少时间区偏移”)。
您不应该使用没有时间区的日期时间字符串(状态2018)。
相反,传递{{link1:毫秒的Unix时间戳或不同日期部分的单独参数}}到
Date
构造函数中。
大多数浏览器还支持一些其他格式,但它们没有被指定,因此在所有浏览器中都不能以相同的方式工作。
如果可能的话,您只应该使用上述解释的日期时间字符串格式。
其他任何格式可能会在其他浏览器甚至同一浏览器的其他版本中出现问题。
如果您遇到
Invalid Date
而不是日期对象,则很可能使用了无效的日期时间字符串。
现在稍微详细说明一下。
日期时间字符串格式
ECMAScript(JavaScript语言实现的规范)从一开始就支持new Date
(规范)和Date.parse
(规范)中的日期字符串。
然而,最初的版本并没有指定日期时间格式。
这在2009年ES5被引入并规范了一个日期时间格式时发生了改变。
基础知识
ECMAScript规定
日期时间字符串格式为
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"(代表UTC)或者是"+"或"-"后面跟着时间表达式HH:mm
该规范还 提到,如果“字符串不符合[指定]格式,则函数可能会回退到任何特定于实现的启发式或特定于实现的日期格式”,这可能导致不同浏览器中的不同日期。
ECMAScript 不考虑任何用户本地日期时间格式,这意味着您不能使用特定于国家或地区的日期时间格式。
短日期(和时间)表单
该规范还包括以下简短格式。
此格式包括仅日期表单:
它还包括“日期时间”形式,由上述仅日期形式之一紧接着下列时间形式之一组成,并附加一个可选的时区偏移量:
THH:mm
THH:mm:ss
THH:mm:ss.sss
回退值
如果
MM
或
DD
字段不存在,则使用
"01"
作为值。如果
HH
、
mm
或
ss
字段不存在,则使用
"00"
作为值,缺少
sss
字段的值为
"000"
。当时间偏移量不存在时,仅包含日期的形式被解释为UTC时间,日期和时间的形式被解释为本地时间。
请参阅下面的“缺少时区偏移”的更多信息,了解浏览器支持的不足之处。
超出范围的值
在格式字符串中的非法值(超出范围以及语法错误)意味着该格式字符串不是此格式的有效实例。
例如,
new Date('2018-01-32')
和
new Date('2018-02-29')
将导致
Invalid Date
。
扩展年份
ECMAScript的日期时间格式还指定了扩展年份,这是六位数字的年份值。
这种扩展年份字符串格式的例子如下:+287396-10-12T08:59:00.992Z
表示公元 287396 年的一个日期。
扩展年份可以是正数也可以是负数。
日期API
ECMAScript指定了各种日期对象属性。
给定一个有效的日期对象,您可以使用 Date.prototype.toISOString()
来获取有效的日期时间字符串。
请注意,时区始终为UTC。
new Date().toISOString() // "2018-08-05T20:19:50.905Z"
使用以下函数也可以检测日期对象是否有效或为Invalid Date
。
function isValidDate(d) {
return d instanceof Date && !isNaN(d);
}
源代码和更多信息可以在JavaScript中检测“无效日期”实例中找到。
示例
有效的日期时间格式
以下日期时间格式都符合规范,应该适用于支持ES2016或更高版本的所有浏览器、Node.js或其他实现。
2018
2018-01
2018-01-01
2018-01-01T00:00
2018-01-01T00:00:00
2018-01-01T00:00:00.000
2018-01-01T00:00:00.000Z
2018-01-01T00:00:00.000+01:00
2018-01-01T00:00:00.000-01:00
+002018-01-01T00:00:00.000+01:00
无效的日期时间格式
请注意,以下示例根据规范是无效的。
但这并不意味着没有浏览器或其他实现将它们解释为日期。请不要使用以下任何日期时间格式,因为它们是非标准的,并且可能在某些浏览器或浏览器版本中失败。
2018-1-1 // month and date must be two digits
2018-01-01T0:0:0.0 // hour/minute/second must be two digits, millisecond must be three digits
2018-01-01 00:00 // whitespace must be "T" instead
2018-01-01T00 // shortest time part must have format HH:mm
2018-01-01T00:00:00.000+01 // time zone must have format HH:mm
浏览器支持
如今,每个现代化和相当旧的浏览器都支持ES5规范在2009年引入的日期时间格式。
然而,即使在今天(状态为2018),没有时区的日期时间字符串有不同的实现(请参见下面的“缺少时区偏移量”)。
如果您需要支持旧版浏览器或使用没有时区的字符串,则不应使用日期时间字符串。
相反,传递自1970年1月1日00:00:00 UTC以来的毫秒数或表示不同日期部分的两个或更多参数到Date
构造函数中。
缺少时区偏移量
ES5.1 错误地 声明缺失时区偏移量的值为 “Z”
,这与 ISO 8601 相矛盾。
该错误已在 ES6(ES2015) 中得到修正,并在 ES2016 中得到扩展(请参见下文中的“ECMAScript 规范的更改”)。
从 ES2016 开始,没有时区的日期时间字符串将被解析为本地时间,而仅有日期的字符串将被解析为 UTC。
根据
this answer,一些实现从未实现 ES5.1 中指定的行为。其中之一似乎是 Mozilla Firefox。其他符合 ES2016(及更高版本)规范的浏览器包括 Google Chrome 65+、Microsoft Internet Explorer 11 和 Microsoft Edge。当前版本的 Apple Safari(11.1.2)
不符合规范,因为它错误地将没有时区的日期时间字符串(例如
2018-01-01T00:00
)解析为 UTC 而不是本地时间。
旧版日期时间格式
ES5 在 2009 年引入了日期时间字符串的规范。在此之前,没有所有浏览器都支持的指定格式。因此,每个浏览器供应商都添加了对不同格式的支持,这些支持通常在不同的浏览器(和版本)中无法正常工作。有关古代历史的一个小例子,请参见
date-formats。
大多数浏览器仍然支持这些旧版格式,以保持对旧网站的向后兼容性。但是,依赖这些非标准格式并不安全,因为它们可能是不一致的或随时被删除。
Date.prototype.toString()
和 Date.prototype.toUTCString()
ES2018 首次指定了 Date.prototype.toString()
和 Date.prototype.toUTCString()
返回的日期格式。
即使在此之前,ECMA 规范也要求 Date
构造函数和 Date.parse
正确解析这些方法返回的格式(尽管在 2018 年之前没有指定格式)。
Date.prototype.toString()
的示例返回值可能如下所示:
Sun Feb 03 2019 14:27:49 GMT+0100 (Central European Standard Time)
请注意,括号内的时区名称是可选项,确切的名称取决于实现。
Date.prototype.toUTCString()
返回一个与
Date.prototype.toString()
类似的日期格式,但具有零时区偏移量。一个示例格式可能如下所示:
Sun, 03 Feb 2019 13:27:49 GMT
请注意,在星期几后面有一个逗号“
,
”,并且日期和月份与
Date.prototype.toString()
相比是颠倒的。
由于这些格式仅在2018年被指定,因此您不应该依赖它们在不同实现中的平等工作(特别是旧版浏览器)。
Node.js
Node.js 运行在 V8 JavaScript 引擎上,该引擎也用于 Google Chrome。因此
日期时间字符串格式的规范相同。由于代码在后端运行,用户本地时间不会影响时区,只有服务器设置才会影响。大多数平台即服务(PaaS)提供商默认使用 UTC 作为它们托管的 Node.js 应用程序的时区。
日期时间库
Moment.js
Moment.js是一个非常流行的JavaScript日期处理库,它支持比ECMAScript规定更多的格式。此外,Moment.js还支持基于字符串和任意格式创建日期对象。
Luxon
Luxon支持ISO 8601、HTTP、RFC2822、SQL和任意格式的解析。但是需要使用不同的函数来处理不同的日期时间格式。
ECMAScript规范的更改
关于日期时间字符串格式方面,以下是ECMAScript规范的一些重要更改。
ES2018中的更改
介绍了由Date.prototype.toString()
和Date.prototype.toUTCString()
返回的日期格式规范。
ES2017中的更改
没有显着的更改。
ES2016中的更改
如果时区偏移量不存在,则将日期时间解释为本地时间。
当时区偏移量不存在时,只有日期的形式被解释为UTC时间,日期时间的形式被解释为本地时间。
ES6 (ES2015)的变化
缺失时区偏移量的值为"Z"
。
如果缺少时间偏移量,则将日期时间解释为本地时间。
来自ECMAScript 2015中的更正和澄清,可能会影响兼容性:
如果不存在时间偏移量,则使用本地时区。第5.1版错误地说明缺少时间区域应该被解释为"z"
。
有关此更改的更多详细信息,请参见日期时间字符串格式:默认时区差异从ES5到Web不兼容。
ES5.1的变化
如果MM
或DD
字段不存在,则使用“01”
作为值。 如果HH
,mm
或ss
字段不存在,则使用“00”
作为值,并且缺少sss
字段的值为“000”
。 缺少时区偏移量的值为“Z”
。
ES5的变化
首次引入日期时间字符串格式到ECMAScript规范中。
ECMAScript定义了一种基于ISO 8601扩展格式简化的日期时间字符串交换格式。该格式如下:
YYYY-MM-DDTHH:mm:ss.sssZ
。
还引入了
Date.prototype.toISOString()
,它返回指定格式的日期时间字符串。
ES3中的更改:
弃用
Date.prototype.toGMTString()
并将其替换为
Date.parse(x.toUTCString())
,在提到这些方法返回的格式必须由
Date.parse
实现正确解析的部分中。请注意,
Date.parse(x.toUTCString())
返回的格式是“实现相关的”。
ES2中没有显著变化。
初始规范: ES1
ES1引入了日期时间字符串,用于new Date(value)
和Date.parse(value)
。然而,它并没有指定实际的日期(时间)格式,甚至指出:
[...] Date.parse
生成的值取决于实现[...]
该规范还提到:
如果x
是任何日期对象[...],则以下所有表达式在该实现中应产生相同的数字值[...]:
- [...]
Date.parse(x.toString())
Date.parse(x.toGMTString())
然而,Date.prototype.toString()
和Date.prototype.toGMTString()
的返回值被指定为“依赖于实现”。
Object.prototype.toString.call(d) == '[object Date]' && !isNaN(d)
。好的,它有65个字符,而不是30个,但你只需要写一次。;-) - RobG