为什么“ 2016-02-16”不等于“ 2016-02-16 00:00”?


96

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

我希望两个字符串都表示相同的时间,毕竟,如果我忽略了时间,那不是那天的午夜吗?

但是一会儿

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

返回2016-02-16,午夜,当地时间,

new Date("2016-02-16")

返回2016-02-16,午夜UTC,这是错误的,或者考虑到其他字符串解析的结果,至少不会达到我的期望。

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

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

我是从类型为'datetime-local'的INPUT元素获取此值的,因此似乎尤其矛盾,因为我必须解决page元素返回的值。

我是在做错事,还是应该以其他方式做事?


2
2016-02-16 00:00-看起来根本不是有效时间。ecma-international.org/ecma-262/6.0/…,但是即使您放进去T,它的行为也确实有所不同
zerkms

您目前如何根据输入元素的值从输入元素中获取Date对象?
BoltClock

4
按照标准-“如果没有HH,mm或ss字段,则使用“ 00”作为值,而缺少sss字段的值为“ 000”。如果没有时区偏移,则日期时间被解释为当地时间。” ---它的行为应相同。
zerkms's

@BoltClock Hmm,由于某些原因(好像是因为zerkms注意到的那样),它正在使用元素的value字段并删除T(我认为是因为在“ T”会令人困惑的上下文中向用户显示该值)
迈克尔

1
@Michael:太棒了。这样的浏览器怪癖是我的最爱。(或者这可能是DOM规范的怪癖?不知道,我还没有真正看过。)
BoltClock

Answers:


100

这是ES5.1规范所说的:

缺少时区偏移的值为“ 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 对此进行了更改,在文档的同一部分中更改为:

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

突破变化的喜悦。

编辑

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


20
的确,打破变化的喜悦。在ES7标准的新草案中,恢复了从UTC到本地时间的部分更改。新标准说,如果未指定时区,则日期应解释为UTC,而如果未指定时区,则日期时间应解释为本地时间。已指定。
hichris123 '16

3
不管是纯日期还是日期时间形式,它们实际上都应该是本地时间。这就是ISO8601的工作方式。可笑的是,仅日期的形式被解释为UTC午夜,因为从概念上讲,永恒的UTC日期甚至没有意义。ECMA tc39对此进行了激烈的辩论。我为当地时间而战,迷路了。github.com/tc39/ecma262/issues/87
马特·约翰逊·品特

10

根据规格

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

并且日期时间字符串格式接受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中测试您的代码即可;)

7

您可能会遇到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而不是本地UTC。


OP在相同的实现上似乎得到不同的结果。
Salman A

6

每个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)

3

返回2016-02-16,午夜UTC,这是错误的,或者考虑到其他字符串解析的结果,至少不会达到我的期望。

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

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

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

根据ecma-262,第20.3.3.2节,Date.parse(字符串)

如果ToString导致突然完成,则立即返回完成记录。否则,parse将结果String解释为日期和时间;它返回一个数字,即与日期和时间相对应的UTC时间值。根据字符串的内容,可以将字符串解释为本地时间,UTC时间或其他时区中的时间。

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

否则如此处2 0.3.1.16所述

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


是的,但是为什么在一种情况下却不这样做?
迈克尔


那么为什么这些结果不同?标准要求它必须相同
zerkms's

@zerkms Standard表示您通过时间单位时将执行的操作,没有说明您不使用时将执行的操作。它清楚地表明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

@ gurvinder372我在问题注释中提供了引号:“如果缺少HH,mm或ss字段,则使用“ 00”作为值,而缺少sss字段的值为“ 000”。如果时区偏移如果没有,则日期时间将被解释为本地时间。”
zerkms
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.