JavaScript中有效的日期时间字符串是什么?

40

在JavaScript中使用new DateDate.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)如何处理日期字符串?

如果你没有注意到,我自己回答了这个问题(为什么?)。

2个回答

72

基础知识

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 不考虑任何用户本地日期时间格式,这意味着您不能使用特定于国家或地区的日期时间格式。

短日期(和时间)表单

该规范还包括以下简短格式。

此格式包括仅日期表单:

  • YYYY
  • YYYY-MM
  • YYYY-MM-DD

它还包括“日期时间”形式,由上述仅日期形式之一紧接着下列时间形式之一组成,并附加一个可选的时区偏移量:

  • THH:mm
  • THH:mm:ss
  • THH:mm:ss.sss

回退值

如果MMDD字段不存在,则使用"01"作为值。如果HHmmss字段不存在,则使用"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的变化

如果MMDD字段不存在,则使用“01”作为值。 如果HHmmss字段不存在,则使用“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()的返回值被指定为“依赖于实现”。


2
我猜两者都是真的:D - connexo
isValidDate 函数存在两个问题:instanceof 长期以来被认为是确定对象是否是特定构造函数的实例的一种较差的方式(请参阅 在 JavaScript 中检测“无效日期” Date 实例),并且该函数无法区分不是日期的对象和时间值为 NaN 的日期。 - RobG
@RobG 那个例子已经从您链接的问题中提取出来。您推荐使用什么?更长的版本 - str
@str - 是的,长版本,但可以更简洁:Object.prototype.toString.call(d) == '[object Date]' && !isNaN(d)。好的,它有65个字符,而不是30个,但你只需要写一次。;-) - RobG

2

那么我应该使用哪些日期时间格式?

一般建议不要使用内置的解析器,因为它不可靠,所以回答“应该”是“没有”。请参见为什么Date.parse会给出错误的结果?

但是,正如str所说,您可以使用带有时区的ECMA-262指定的格式:YYYY-MM-DDTHH:mm:ss.sssZYYYY-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中如何格式化日期的文档?

如何检测无效的日期时间字符串?

这里的前三个答案之一应该可以回答这个问题。

日期库(如Moment.js)如何处理日期字符串?

它们根据默认或提供的格式进行解析。阅读源代码(例如,fecha.js是一个简单的解析器和格式化程序,具有编写良好,易于遵循的代码)。

编写一个解析器并不难,但是尝试猜测输入格式(正如内置解析器所做的那样)往往是困难的,不可靠的,并且在不同的实现中表现不一致。因此,除非输入字符串符合解析器的默认格式,否则解析器应要求提供格式。

附言

在ECMAScript 2019(目前处于草案阶段)中,实现必须支持解析和格式化的字符串格式发生了变化,但我认为避免使用内置解析器的一般建议仍将持续一段时间。


你的问题中几乎所有的内容都已经在我的问题中了 :) 只是我更加强调规范和实现的实际操作,而不是一些浏览器可能会或可能不会额外执行的操作。 - str
我尝试保持简洁,因为你已经完成了汇编。 :-) - RobG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接