要点
JavaScript 正式支持简化 ISO 8601 扩展格式。格式如下:YYYY-MM-DDTHH:mm:ss.sssZ。字母T 是日期/时间分隔符,Z 是指定为Z(对于UTC)或+ 或- 后跟时间表达式HH:mm 的时区偏移量。
该格式的某些部分(例如时间)可以省略。
请注意,年份必须至少有四位数字,月/日/小时/分钟/秒必须正好有两位数字,毫秒必须 em> 正好是三位数。例如,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。
重要:
根据规范,所有现代和相当旧的浏览器和实现都支持 full-length 日期时间格式。
然而,在处理没有时区的日期(时间)字符串方面存在差异(有关详细信息,请参阅下面的“缺少时区偏移量”)。您应该不使用没有时区的日期时间字符串(状态 2018)。
而是将 unix timestamp in milliseconds or separate arguments for different parts of the date 传递给 Date 构造函数。
大多数浏览器也支持一些其他格式,但它们没有被指定,因此在所有浏览器中的工作方式都不同。
如果有的话,您应该只使用上面解释的日期时间字符串格式。
其他所有格式都可能在其他浏览器甚至同一浏览器的其他版本中中断。
如果您遇到Invalid Date 而不是日期对象,您很可能使用了无效的日期时间字符串。
现在有更多细节。
日期时间字符串格式
ECMAScript(JavaScript 语言实现的规范)从一开始就支持new Date (specification) 和Date.parse (specification) 中的日期字符串。
但是,第一个版本实际上并没有指定日期时间格式。
这在 2009 年发生了变化,当时 ES5 引入了日期时间格式规范。
基础知识
ECMAScript 将Date Time String Format 指定为ISO 8601 Extended Format 的简化。格式如下: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 的时区偏移量
规范还 mentions 指出,如果“字符串不符合 [指定] 格式,则函数可能会退回到任何特定于实现的启发式或特定于实现的日期格式”,这可能会导致不同浏览器中的日期不同.
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 的日期时间格式还指定了extended years,它们是六位数的年份值。
这种扩展年份字符串格式的示例类似于 +287396-10-12T08:59:00.992Z,它表示公元 287396 年的日期。
延长的年份可以是正数也可以是负数。
日期 API
ECMAScript 指定了广泛的date object properties。
给定一个有效的日期对象,您可以使用Date.prototype.toISOString() 来获取一个有效的日期时间字符串。
请注意,时区始终为 UTC。
new Date().toISOString() // "2018-08-05T20:19:50.905Z"
还可以使用以下函数检测日期对象是否有效或Invalid Date。
function isValidDate(d) {
return d instanceof Date && !isNaN(d);
}
来源和更多信息可以在Detecting an “invalid date” Date instance in 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
浏览器支持
今天,every modern and reasonably old browser supports the date time format 在 2009 年随 ES5 规范引入。
但是,即使在今天(2018 年状态),对于没有时区的日期时间字符串也有不同的实现(请参阅下面的“缺少时区偏移量”)。
如果您需要支持旧版浏览器或使用不带时区的字符串,则不应使用日期时间字符串。
相反,传递一个number of milliseconds since January 1, 1970, 00:00:00 UTC
或 two or more arguments representing the different date parts 到 Date 构造函数。
缺少时区偏移
ES5.1 incorrectly 声明缺席时区偏移的值为“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.toUTCString() 相比,工作日和日月份颠倒后有一个逗号,。
由于这些格式仅在 2018 年指定,因此您不应依赖它们在不同的实现(尤其是旧版浏览器)中同样工作。
Node.js
Node.js 在 V8 JavaScript 引擎上运行,该引擎也用于 Google Chrome。
所以适用于日期时间字符串格式的相同规范。
由于代码在后端运行,因此用户本地时间不会影响时区,而只会影响服务器上的设置。
大多数托管 Node.js 应用程序的平台即服务 (PaaS) 提供商都使用 UTC 作为其默认时区。
日期时间库
Moment.js
Moment.js 是一个非常流行的库,可以帮助处理 JavaScript 中的日期,它还比 ECMAScript 指定的 supports more formats 还要好。
此外,Moment.js 还支持creating date objects based on a string and a arbitrary format。
卢克森
Luxon 支持 ISO 8601、HTTP、RFC2822、SQL 和任意格式的parsing。但只能对不同的日期时间格式使用不同的函数。
ECMAScript 规范的变化
ECMAScript 规范中有关日期时间字符串格式的显着变化列表。
ES2018的变化
介绍Date.prototype.toString() 和Date.prototype.toUTCString() 返回的日期格式规范。
ES2017的变化
没有显着变化。
ES2016的变化
如果不存在时区偏移,则日期时间被解释为本地时间。
当不存在时区偏移时,仅日期形式被解释为 UTC 时间,而日期时间形式被解释为本地时间。
ES6 (ES2015)的变化
缺席时区偏移的值为“Z”。
如果不存在时区偏移,则日期时间被解释为本地时间。
来自Corrections and Clarifications in ECMAScript 2015 with Possible Compatibility Impact:
如果不存在时区偏移,则使用本地时区。 5.1 版错误地指出缺少的时区应解释为"z"。
有关该更改的更多详细信息,请参阅Date Time String Format: default time zone difference from ES5 not web-compatible。
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 对象 [...],那么以下所有表达式都应在该实现中产生相同的数值 [...]:
- [...]
Date.parse(x.toString())
Date.parse(x.toGMTString())
但是,Date.prototype.toString() 和 Date.prototype.toGMTString() 的返回值都被指定为“依赖于实现”。