【问题标题】:Why isn't "2016-02-16" equal to "2016-02-16 00:00"?为什么“2016-02-16”不等于“2016-02-16 00:00”?
【发布时间】:2016-05-28 15:55:57
【问题描述】:

我正在尝试将两个日期字符串都传递给new Date(t)

我希望两个字符串代表相同的时间,毕竟如果我省略时间,那不应该是当天的午夜吗?

不过,

new Date("2016-02-16 00:00")

按预期返回当地时间 2016 年 2 月 16 日午夜,

new Date("2016-02-16")

返回 2016 年 2 月 16 日午夜 UTC,这是错误的,或者至少不是我所期望的,因为其他字符串解析为。

如果他们都具有相同的行为,我会理解它,无论是将时间返回为当地时间,还是作为 UTC,但为什么他们返回这样不同的东西似乎非常不一致。

作为一种解决方法,每当我遇到没有相应时间戳的日期时,我都可以附加“00:00”以获得一致的行为,但这似乎相当脆弱。

我从一个“datetime-local”类型的 INPUT 元素中获取此值,因此我必须解决页面元素返回的值似乎特别不一致。

我做错了什么,还是应该做些不同的事情?

【问题讨论】:

  • 2016-02-16 00:00 -- 这看起来根本不像是有效时间。 ecma-international.org/ecma-262/6.0/…,但即使你把 T 放在那里,它的行为确实不同
  • 您目前是如何根据输入元素的值从输入元素中获取 Date 对象的?
  • 按照标准-“如果 HH、mm 或 ss 字段不存在,则使用“00”作为值,并且不存在的 sss 字段的值为“000”。如果时区偏移量不存在,日期时间被解释为本地时间。” --- 它的行为应该相同。
  • @BoltClock 嗯,看起来它正在获取元素的 value 字段,并且(正如 zerkms 注意到的那样)出于某种原因删除了 T (我认为是因为该值在以下上下文中显示给用户“T”会令人困惑)
  • @Michael:太棒了。像这样的浏览器怪癖是我最喜欢的。 (或者它可能是一个 DOM 规范怪癖?不知道,我还没有真正看过。)

标签: javascript date datetime


【解决方案1】:

这是ES5.1 specification 所说的:

缺席时区偏移的值为“Z”。

它还说:

函数首先尝试根据日期时间字符串格式(15.9.1.15)中调用的规则解析字符串的格式。如果字符串不符合该格式,则函数可能会退回到任何特定于实现的启发式或特定于实现的日期格式。

由于格式需要在日期和时间之间使用T 分隔符,因此有效时间为UTC:

> new Date("2016-02-16T00:00:00")
Tue Feb 16 2016 01:00:00 GMT+0100 (CET)
> new Date("2016-02-16")
Tue Feb 16 2016 01:00:00 GMT+0100 (CET)

...而在 node.js 中,无效时间(没有 T 分隔符)似乎转到特定于实现的本地时间:

> new Date("2016-02-16 00:00:00")
Tue Feb 16 2016 00:00:00 GMT+0100 (CET)

注意 ES6 改变了这个,在同一个 part of the documentation 它改变为:

如果不存在时区偏移,则日期时间被解释为本地时间。

breaking changes的喜悦。

编辑

根据TC39,该规范旨在解释为没有时区的日期和时间字符串(例如“2016-02-16T00:00:00”)被视为本地(根据 ISO 8601),但是仅日期字符串(例如“2016-02-16”)为 UTC(与 ISO 8601 不一致)。

【讨论】:

  • 确实是打破变化的喜悦。在 ES7 标准的新草案中,part of the change from UTC to local time was reverted 新标准规定,如果未指定时区,则日期应解释为 UTC,而如果未指定时区,则日期时间应解释为本地时间。
  • 无论是仅日期还是日期时间形式,它们都应该是本地时间。这就是 ISO8601 的工作原理。仅日期形式被解释为午夜 UTC 是荒谬的,因为一个永恒的 UTC 日期在概念上甚至没有意义。在 ECMA tc39 上对此进行了激烈的辩论。我为当地时间而战,但输了。 github.com/tc39/ecma262/issues/87
【解决方案2】:

根据specifications

函数首先尝试解析字符串的格式 根据日期时间字符串格式调用的规则 (15.9.1.15)。如果字符串不符合该格式 函数可以回退到任何特定于实现的启发式或 特定于实现的日期格式。

并且Date Time String Formats 接受2016-02-16 作为有效日期

此格式包括仅日期格式:

YYYY
YYYY-MM
YYYY-MM-DD

[...] 如果 HH、mm 或 ss 字段不存在,则使用“00”作为值 缺席sss字段的值为“000”。缺席的价值 时区偏移为“Z”。

因此2016-02-16 转换为2016-02-16T00:00:00.000Z

另一个日期2016-02-16 00:00 不符合格式,因此它的解析是特定于实现的。显然,此类日期被视为具有本地时区,您的示例日期将根据时区返回不同的值:

/* tz = +05:00 */ new Date("2016-02-16 00:00").toISOString() // 2016-02-15T19:00:00.000Z
/* tz = -08:00 */ new Date("2016-02-16 00:00").toISOString() // 2016-02-16T08:00:00.000Z

总结:

  • 对于符合标准的日期时间格式,行为是明确定义的 - 在没有时区偏移的情况下,日期字符串被视为 UTC (ES5) 或本地 (ES6)。
  • 对于不一致的日期时间格式,行为是特定于实现的——在没有时区偏移的情况下,通常行为是将日期视为本地日期。
  • 事实上,实现可以选择返回NaN 而不是尝试 来解析不一致的日期。只需在 Internet Explorer 11 中测试您的代码;)

【讨论】:

    【解决方案3】:

    您可能会遇到 ES5、ES6 实现与您的预期结果之间的差异。根据 MDN 的Date.parse,“尤其是在不同的 ECMAScript 实现中,诸如“2015-10-12 12:00:00”之类的字符串可能会被解析为 NaN、UTC 或本地时区”。

    在 Firefox 44 和 IE 11 中的附加测试显示,它们都返回了 new Date("2016-02-16 00:00") 的日期对象,该对象在尝试获取日期组件值时返回 NaN,其 toString 值为 “无效日期”(不是“NaN”)。因此,附加“ 00:00 以获得一致的行为”很容易在不同的浏览器中中断。

    如其他答案中所述new Date("2016-02-16") 默认使用零时区偏移量,产生午夜 UTC 而不是本地。

    【讨论】:

    • OP 似乎在相同的实现上得到不同的结果。
    【解决方案4】:

    根据DateParser::Parse() 的 Chrome 的 V8 源代码。

    ES5 ISO 8601 日期:

    [('-'|'+')yy]yyyy[-MM[-DD]][THH:mm[:ss[.sss]][Z|(+|-)hh:mm]]

    后跟“:”的无符号数字是一个时间值,它被添加到 TimeComposer。

    如果缺少时区,则默认为 Z

    > new Date("2016-02-16 00:00")
      Tue Feb 16 2016 00:00:00 GMT+0800 (China Standard Time)
    

    匹配两种格式的字符串(例如1970-01-01)将被解析为 ES5 日期时间字符串 - 这意味着它将默认为 UTC time-zone。如果遵循 ES5 规范,这是不可避免的。

    > new Date("2016-02-16")
    Tue Feb 16 2016 08:00:00 GMT+0800 (China Standard Time)
    

    【讨论】:

      【解决方案5】:

      返回 2016 年 2 月 16 日午夜 UTC,这是错误的,或者至少不是我所期望的,因为其他字符串解析为。

      它将时区偏移量添加到00:00

      new Date("2016-02-16") 输出Tue Feb 16 2016 05:30:00 GMT+0530 (India Standard Time)

      我的时区是具有偏移值(以分钟为单位)+330 的 IST,因此它在 00:00 时增加了 330 分钟。

      根据ecma-262, section 20.3.3.2 Date.parse ( string )

      如果 ToString 导致突然完成,则完成记录为 立即返回。否则, parse 解释结果字符串 作为日期和时间;它返回一个数字,UTC 时间值 对应日期和时间。字符串可以解释为 本地时间、UTC 时间或其他时区的时间,具体取决于 关于字符串的内容。

      当您明确设置时间单位new Date("2016-02-16 00:00")时,它将使用设置为hoursminutes

      否则如 20.3.1.16

      中所述

      如果时区偏移不存在,则日期时间被解释为 当地时间。

      【讨论】:

      • 是的,但为什么它在一种情况下这样做,而在另一种情况下却不行?
      • @Michael 是的,检查ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf section 20.3.3.2
      • 那么为什么会有不同的结果呢?该标准声称它必须相同
      • @zerkms 标准说明了当你超过时间单位时它会做什么,它没有说明当你不超过时它会做什么。上面明明写着The String may be interpreted as a local time, a UTC time, or a time in some other time zone, depending on the contents of the String.哪里说的一定是一样的?
      • @gurvinder372 我在问题 cmets 中提供了引用:“如果 HH、mm 或 ss 字段不存在,则使用“00”作为值,并且不存在的 sss 字段的值为“ 000"。如果不存在时区偏移量,则日期时间被解释为本地时间。"
      猜你喜欢
      • 2016-05-25
      • 2013-03-23
      • 1970-01-01
      • 1970-01-01
      • 2020-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多