在JavaScript中使用new Date
或Date.parse
时,我不能只传递任意日期格式。根据格式的不同,我得到的日期与我想要的日期不同,甚至Invalid Date
而不是日期对象。某些日期格式可在一个浏览器中使用,但在其他浏览器中则无效。那么我应该使用哪种日期时间格式呢?
其他问题:
-
所有浏览器都支持相同的格式吗?Mozilla Firefox、Google Chrome、Microsoft Internet Explorer、Microsoft Edge 和 Apple Safari 如何处理日期时间字符串?Node.js呢?
-
它是否考虑了本地日期格式? 例如,如果我住在瑞士,日期格式是30.07.2018,我可以使用
new Date('30.07.2018')
吗? -
是否考虑了当地时区?
-
如何从日期对象获取日期时间字符串?
-
如何检测无效的日期时间字符串?
-
像 Moment.js 这样的日期库如何处理日期字符串?
如果你没有注意到,我回答了我自己的问题(为什么?
要点
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)。 而是将 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(1 月)到 12(12 月)的月份。DD
是从 01 到 31 的月份中的某一天。T
字面意思出现在字符串中,以指示 time 元素的开头。HH
是自午夜以来经过的完整小时数,从 00 到 24 作为两个十进制数字。:
(冒号)在字符串中实际出现两次。mm
是自小时开始以来的完整分钟数,从 00 到 59 的两位十进制数字。ss
是自分钟开始以来的完整秒数,作为从 00 到 59 的两个十进制数字。.
(点)字面上出现在字符串中。sss
是自秒开始以来的三个十进制数字的完整毫秒数。Z
是指定为"Z"(对于 UTC)或"+"或"-"后跟时间表达式HH:mm
该规范还提到,如果"字符串不符合[指定的]格式,则函数可能会回退到任何特定于实现的启发式或特定于实现的日期格式",这可能会导致不同浏览器中的日期不同。
ECMAScript 不考虑任何用户的本地日期时间格式,这意味着您不能使用国家或地区特定的日期时间格式。
简短日期(和时间)表格
该规范还包括较短的格式,如下所示。
此格式包括仅日期表单:
YYYY
YYYY-MM
YYYY-MM-DD
它还包括"日期时间"表单,这些表单由上述仅日期表单之一组成,紧跟以下时间表单之一,并附加可选的时区偏移量:
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年份中的日期。 延长的年限可以是正数,也可以是负数。
日期接口
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 中的检测"无效日期"Date 实例中找到。
例子
有效的日期时间格式
以下日期时间格式根据规范都是有效的,并且应该适用于每个浏览器、Node.js或其他支持 ES2016 或更高版本的实现。
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
浏览器支持
今天,每个现代和相当旧的浏览器都支持2009年ES5规范引入的日期时间格式。 但是,即使在今天(Status 2018),对于没有时区的日期时间字符串,也有不同的实现(请参阅下面的"缺少时区偏移量")。如果需要支持较旧的浏览器或使用没有时区的字符串,则不应使用日期时间字符串。 而是传递自 1970 年 1 月 1 日 00:00:00 UTC 以来的毫秒数 或两个或多个参数,表示Date
构造函数的不同日期部分。
缺少时区偏移量
ES5.1错误地指出缺少时区偏移量的值“Z”
这与ISO 8601相矛盾。 此错误在 ES6 (ES2015) 中得到修复,并在 ES2016 中得到扩展(请参阅下面的"对 ECMAScript 规范的更改")。 从 ES2016 开始,没有时区的日期时间字符串被解析为本地时间,而仅日期字符串被解析为 UTC。
根据这个答案,一些实现从未实现过 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.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 年指定,因此您不应依赖它们在不同的实现(尤其是旧浏览器)中同样工作。
节点.js
Node.js运行在V8 JavaScript引擎上,该引擎也用于Google Chrome。 因此,有关日期时间字符串格式的相同规范适用。但是,由于代码在后端运行,因此用户本地时间不会影响时区,而只会影响服务器上的设置。 托管 Node.js 应用程序的大多数平台即服务 (PaaS) 提供程序都使用 UTC 作为其默认时区。
日期时间库
瞬间.js
Moment.js 是一个非常流行的库,用于帮助处理 JavaScript 中的日期,它还支持比 ECMAScript 指定的更多格式。 此外,Moment.js 还支持基于字符串和任意格式创建日期对象。
卢克森
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 对象 [...],则以下所有表达式都应在该实现 [...] 中生成相同的数值:
- [...]
Date.parse(x.toString())
Date.parse(x.toGMTString())
但是,Date.prototype.toString()
和Date.prototype.toGMTString()
的返回值都被指定为"依赖于实现"。
那么我应该使用哪种日期时间格式呢?
一般建议根本不使用内置解析器,因为它不可靠,因此"应该"的答案是"无"。请参阅为什么 Date.parse 给出不正确的结果?
但是,正如str所说,您可能会使用ECMA-262中指定的时区格式:YYYY-MM-DDTHH:mm:ss.sssZ
或YYYY-MM-DDTHH:mm:ss.sss±HH:mm
,不要信任任何其他格式。
所有浏览器都支持相同的格式吗?
不。
Mozilla Firefox、Google Chrome、Microsoft Internet Explorer、Microsoft Edge 和 Apple Safari 如何处理日期时间字符串?
不同。ECMA-262 中的格式以外的任何内容都依赖于实现,并且在解析 ECMA-262 格式时存在错误。
Node.js呢?
可能又不同了,见上文。
它是否考虑了本地日期格式? 例如,如果我住在瑞士,日期格式是 30.07.2018,我可以使用 new Date('30.07.2018')吗?
或。由于它不是标准格式,因此解析依赖于实现,所以也许,也许不是。
是否考虑了当地时区?
它使用主机时区偏移量,其中字符串被解析为本地,并生成使用本地时间显示的字符串。否则,它使用 UTC(内部时间值为 UTC)。
如何从日期对象获取日期时间字符串?
Date.parse.toString,或者参阅在哪里可以找到有关在 JavaScript 中格式化日期的文档?
如何检测无效的日期时间字符串?
这里的前 3 个答案之一应该回答这个问题。
像 Moment.js 这样的日期库如何处理日期字符串?
他们根据默认或提供的格式解析它们。阅读源代码(例如 fecha.js 是一个简单的解析器和格式化程序,具有编写良好、易于遵循的代码)。
解析器并不难编写,但尝试猜测输入格式(就像内置解析器倾向于这样做的那样)在实现中令人担忧、不可靠且不一致。因此,解析器应要求提供格式,除非输入字符串采用解析器的默认格式。
附言
在ECMAScript 2019(目前处于草案中)中,实现必须支持的解析和格式化字符串格式发生了一些变化,但我认为避免内置解析器的一般建议将持续一段时间。