Javascript中new Date函数的奇怪行为

15

我试图在JavaScript中从字符串创建Date对象,但是我发现JavaScript解析日期字符串的方式非常奇怪。

> new Date("2012-01-01");
Sun Jan 01 2012 07:00:00 GMT+0700 (ICT)

> new Date("01-01-2012");
Sun Jan 01 2012 00:00:00 GMT+0700 (ICT)

> new Date("2012-01-01") == new Date("01-01-2012")
false

我使用Chrome 32,如您所见它们相差7个小时。请告诉我这里发生了什么?


1
那真是太奇怪了。就像第一个返回本地时间,第二个返回UTC一样。除了在两种情况下都添加了GMT偏移量。 - brandonscript
4
在这两种情况下,它都会返回当地时间,因为这就是toString的作用。第一种情况只是被解析为UTC时间,而第二种情况被解析为当地时间。 - Dennis
1
你正在依赖于 Date 构造函数解析一个字符串,而这在当前使用的浏览器中实现不一致。你可以使用符合 ES5 标准的字符串,但并非所有浏览器都支持它。最好手动解析字符串并自己构建日期对象。 - RobG
2个回答

16

关键在于浏览器如何实现Date.parse (规范)。该方法是在传递给Date构造函数 (规范)的字符串上调用的,它首先尝试将字符串与已知格式匹配以确定哪些值在何处。我预计不同的浏览器会稍微以不同的方式实现这个决策树,但是Chrome的实现可能假设"2012-01-01"版本是基于Zulu或GMT/UTC的ISO-8601标准的前缀,并包括时区("2012-01-01T00:00:00Z-07:00"),而"01-01-2012"版本是基于您的本地时区的本地化,并且不需要指定它("01-01-2012 00:00:00"),所以7小时的差异基于ISO标准日期和本地化日期之间的7小时偏移量。Date.prototype.toString() (规范),相比之下,应该显示本地时间,并由Date构造函数返回,这就是为什么您测试的两个返回值都是本地化的原因。

Date.parse规范中可以看出:

该函数首先尝试根据日期时间字符串格式(15.9.1.15)中指定的规则解析字符串的格式。如果字符串不符合该格式,则函数可能会回退到任何实现特定的启发式算法或实现特定的日期格式。在格式字符串中包含无法识别的字符串或非法元素值的日期将导致Date.parse返回NaN。

这意味着,如果您不使用15.9.1.15中指定的完整ISO-8601日期,则浏览器可以自行决定,或者只给您NaN。即使这是标准,一些浏览器也以不实际遵循标准而著称,因此您可以考虑通过自己解析数据并使用其他Date构造函数(规范)来明确指定所有参数。


2
很好的答案!我在Node.js中也看到了同样的现象。看起来最好的做法是明确指定日期结构或始终默认为YYYY-MM-DD。 - brandonscript
这是标准做法,而且有一个额外的优点,就是可以按字符串进行“字母顺序”排序,并以日期方式结束。如果这对你很重要的话... - citizenslave
1
@citizenslave - 但是“标准”并不被所有正在使用的浏览器支持,因此目前建议手动解析。 - RobG

3

这种差异的原因在其他答案中已经解释过了。避免这个问题的一个好方法是自己解析字符串。如果你想让2012-01-01被视为UTC时间,则可以:

function dateFromUTCString(s) {
  var s = s.split(/\D/);
  return new Date(Date.UTC(s[0], --s[1], s[2]));
}

如果你想将其视为本地日期,则应执行以下操作:
function dateFromString(s) {
  var s = s.split(/\D/);
  return new Date(s[0], --s[1], s[2]);
}

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