新日期(“2017-01-01”)与新日期(“2017-1-1”)有何分别?

我在铬控制台输入 new Date("2017-01-01"),输出显示它的小时是8,但是 new Date("2017-01-1")new Date("2017-1-01")显示它们的小时都是0,那么 new Date(dateString)如何解析?

new Date("2017-01-01")
// Sun Jan 01 2017 08:00:00 GMT+0800 (中国标准时间)*
new Date("2017-01-1")
// Sun Jan 01 2017 00:00:00 GMT+0800 (中国标准时间)*
new Date("2017-1-1")
// Sun Jan 01 2017 00:00:00 GMT+0800 (中国标准时间)*
new Date("2017-1-01")
// Sun Jan 01 2017 00:00:00 GMT+0800 (中国标准时间)*
7248 次浏览

“2017-01-01”跟随 ISO 标准 ES5日期时间字符串格式(简化 ISO 8601扩展格式),因此它是在世界协调时间,也就是中国的上午8点。在 Chrome1中,所有其他字符串都被解析为本地时间。


1 Chromium 中的相关源代码: < a href = “ https://cs.Chromium.org/Chromium/src/v8/src/dateparser-inl.h? type = cs & amp; l = 16”rel = “ noReferrer”> https://cs.Chromium.org/Chromium/src/v8/src/dateparser-inl.h?type=cs&l=16

Chromium 中的日期解析遵循标准 ES5规则以及以下额外规则:

  • 忽略第一个数字之前的任何无法识别的单词。
  • 括号内的文本将被忽略。
  • 后跟 :的无符号数字是一个时间值,它被添加到 TimeComposer中。后跟 ::的数字也加上第二个零。.后面的数字也是时间,后面必须跟着毫秒。任何其他数字都是日期成分,并被添加到 DayComposer
  • Day作曲家中,月名(或实际上: 任何与月名前三个字母相同的单词)被记录为命名月。
  • 一个可以识别为时区的单词就是这样记录的,如 (+|-)(hhmm|hh:)
  • 遗留日期不允许读取一个数字后出现额外的符号(+-)或不匹配的 )(在第一个数字之前,允许出现任何垃圾)。
  • 符合 ES5规则和上述规则的任何字符串都将使用 ES5规则进行解析。这意味着 "1970-01-01"将位于 UTC 时区,而不是本地时区。

什么意思?

首先请注意,“2017-01-01”是以 UTC 时间解析的,因为它是一个“ date”字符串,而不是“ date-time”字符串,并且它与 ES5对“ date”字符串的定义相匹配。如果附加了时间,那么它将遵循 ISO 标准并在当地时间解析它。

例子:

  • 世界协调时2017年1月1日
  • 当地时间2017年1月1日
  • 当地时间2017年1月1日
  • 当地时间2017年1月1日
  • 当地时间2017年5月1日
  • 当地时间2017年5月1日
  • 当地时间2017年6月1日

此格式为国际标准(ISO 格式)

new Date("2017-01-01")

这保证了所有浏览器的输出相同。

不过,其他格式可能会根据浏览器的不同而改变,因为它们的定义不是很清楚。

正如您所看到的,这种格式

new Date("2017-1-1")

在 chrome 中成功解析,但在 IE11中出现错误

在解析日期时,JavaScript 将 ISO 日期解释为 UTC 时间,将其他格式解释为本地时间。

正如 MDN 文章所建议的,

如果字符串仅为 ISO8601日期,则使用 UTC 时区来解释参数。

给定一个日期字符串“ March 7,2014”,parse ()假定一个本地时区 但是如果采用 ISO 格式,比如“2014-03-07”,则会假定时区为 UTC (ES5和 ECMAScript 2015)。因此,使用这些字符串生成的 Date 对象可能表示不同的时刻,这取决于支持的 ECMAScript 版本,除非系统设置的本地时区为 UTC。这意味着,根据正在转换的字符串的格式,两个出现等效的日期字符串可能会产生两个不同的值。

// 2017-03-28 is interpreted as UTC time,
// shown as 2017-03-28 00:00:00 in UTC timezone,
// shown as 2017-03-28 06:00:00 in my timezone:
console.log("ISO dates:");
var isoDates = [new Date("2017-03-28")];
for (var dt of isoDates)
{
console.log(dt.toUTCString() + " / " + dt.toLocaleString());
}


// Other formats are interpreted as local time,
// shown as 2017-03-27 18:00:00 in my timezone,
// shown 2017-03-28 00:00:00 in my timezone:
console.log("Other formats:");
var otherDates = [new Date("2017-3-28"), new Date("March 28, 2017"), new Date("2017/03/28")];
for (var dt of otherDates)
{
console.log(dt.toUTCString() + " / " + dt.toLocaleString());
}

新日期(“2017-01-01”)与新日期(“2017-1-1”)的差异

new Date("2017-01-01")是符合规格的(详见下文)。而 new Date("2017-1-1")不是,因此只能依靠 JavaScript 引擎想要应用的任何 “ ... 特定于实现的启发式或特定于实现的日期格式”。例如,您不能保证它如何(或是否)成功解析,如果成功,它将被解析为 UTC 还是本地时间。

虽然 new Date("2017-01-01")是按照规格设计的,但不幸的是,浏览器应该用它来做什么一直是一个移动的目标,因为它上面没有时区指示器:

  • 在 ES5 (2009年12月)中,没有时区指示符的字符串应该被解析为 UTC。但这与日期/时间格式所基于的 ISO-8601标准不一致,该标准规定,没有时区指示器的字符串应该是本地时间,而不是 UTC。因此,在 ES5中,new Date("2017-01-01")以 UTC 格式进行解析。
  • 在 ES2015 (又名 ES6,2015年6月)中,没有时区指示器的字符串应该是本地时间,而不是像 ISO-8601那样是 UTC。因此,在 ES2015中,new Date("2017-01-01")被解析为本地时间。
  • 但是 ,由于浏览器多年来一直将 -作为 UTC 来解析只有日期的表单,所以 在 ES2016(2016年6月)再次发生了变化。因此,从 ES2016开始,只限约会表单(如 "2017-01-01")以 UTC 格式进行解析,而 日期/时间表单(如 "2017-01-01T00:00:00")以本地时间进行解析。

遗憾的是,并非所有的 JavaScript 引擎目前都实现了该规范。Chrome (在撰写本文时,v56)以 UTC 格式解析日期/时间表单,即使它们应该是本地时间(IE9也是如此)。但是 Chrome、 Firefox 和 IE11(我手边没有 IE10或 Edge)都能正确处理只有日期的表单(UTC)。IE8根本没有实现 ISO-8601表单(在 ES5规范发布之前就已经发布了)。