为什么Date.parse给出不正确的结果?

情况一:

new Date(Date.parse("Jul 8, 2005"));

输出:

2005年7月8日星期五00:00:00 GMT-0700(PST)

案例二:

new Date(Date.parse("2005-07-08"));

输出:

Thu Jul 07 2005 17:00:00 GMT-0700(PST)


为什么第二次解析不正确?

MonsterKK2020/05/28 10:04:03

从CMS接受的答案是正确的,我刚才已经增加了一些功能:

  • 修剪和清理输入空间
  • 解析斜线,破折号,冒号和空格
  • 具有默认的日期和时间

// parse a date time that can contains spaces, dashes, slashes, colons
function parseDate(input) {
    // trimes and remove multiple spaces and split by expected characters
    var parts = input.trim().replace(/ +(?= )/g,'').split(/[\s-\/:]/)
    // new Date(year, month [, day [, hours[, minutes[, seconds[, ms]]]]])
    return new Date(parts[0], parts[1]-1, parts[2] || 1, parts[3] || 0, parts[4] || 0, parts[5] || 0); // Note: months are 0-based
}
Stafan路易2020/05/28 10:04:03

Both are correct, but they are being interpreted as dates with two different timezones. So you compared apples and oranges:

// local dates
new Date("Jul 8, 2005").toISOString()            // "2005-07-08T07:00:00.000Z"
new Date("2005-07-08T00:00-07:00").toISOString() // "2005-07-08T07:00:00.000Z"
// UTC dates
new Date("Jul 8, 2005 UTC").toISOString()        // "2005-07-08T00:00:00.000Z"
new Date("2005-07-08").toISOString()             // "2005-07-08T00:00:00.000Z"

我删除了该Date.parse()调用,因为它已自动用于字符串参数。我还使用ISO8601格式比较了日期,因此您可以直观地比较本地日期和UTC日期之间的日期。时间相隔7个小时,这是时区差异,也是为什么测试显示两个不同的日期。

创建这些相同的本地/ UTC日期的另一种方法是:

new Date(2005, 7-1, 8)           // "2005-07-08T07:00:00.000Z"
new Date(Date.UTC(2005, 7-1, 8)) // "2005-07-08T00:00:00.000Z"

但是我仍然强烈推荐Moment.js它既简单又强大

// parse string
moment("2005-07-08").format()       // "2005-07-08T00:00:00+02:00"
moment.utc("2005-07-08").format()   // "2005-07-08T00:00:00Z"
// year, month, day, etc.
moment([2005, 7-1, 8]).format()     // "2005-07-08T00:00:00+02:00"
moment.utc([2005, 7-1, 8]).format() // "2005-07-08T00:00:00Z"
小胖Gil2020/05/28 10:04:03

这个轻量级的日期解析库应该解决所有类似的问题。我喜欢该库,因为它很容易扩展。也有可能(不是很简单,但是就不那么难了)。

解析示例:

var caseOne = Date.parseDate("Jul 8, 2005", "M d, Y");
var caseTwo = Date.parseDate("2005-07-08", "Y-m-d");

然后格式化回字符串(您会注意到两种情况给出的结果完全相同):

console.log( caseOne.dateFormat("M d, Y") );
console.log( caseTwo.dateFormat("M d, Y") );
console.log( caseOne.dateFormat("Y-m-d") );
console.log( caseTwo.dateFormat("Y-m-d") );
若合2020/05/28 10:04:03

尽管CMS是正确的,认为将字符串传递到parse方法通常是不安全的,但15.9.4.2节中的新ECMA-262第5版(也称为ES5)规范建议Date.parse()实际上应该处理ISO格式的日期。旧的规范没有这样的要求。当然,旧的浏览器和某些当前的浏览器仍然不提供此ES5功能。

您的第二个示例没有错。它是UTC中指定的日期,由Date.prototype.toISOString()表示,但是以您当地的时区表示。

Tom凯2020/05/28 10:04:03

根据http://blog.dygraphs.com/2012/03/javascript-and-dates-what-mess.html的格式,“ yyyy / mm / dd”解决了常见问题。他说:“请尽可能将日期字符串粘贴到“ YYYY / MM / DD”。它被普遍支持且明确。使用这种格式,所有时间都在本地。我已经设置了测试:http : //jsfiddle.net/jlanus/ND2Qg/432/ 此格式:+通过使用ymd排序和4位数字的年份避免了日和月顺序的歧义+避免了UTC与本地问题的冲突dygraphs的家伙通过使用斜杠+ danvk来符合ISO格式,他说这种格式在所有浏览器中都很好。

千羽2020/05/28 10:04:02

使用moment.js解析日期:

var caseOne = moment("Jul 8, 2005", "MMM D, YYYY", true).toDate();
var caseTwo = moment("2005-07-08", "YYYY-MM-DD", true).toDate();

第三个参数确定严格的解析(从2.3.0版本开始可用)。没有它,moment.js可能还会给出错误的结果。

DavaidTony宝儿2020/05/28 10:04:02

疯狂是有办法的。通常,如果浏览器可以将日期解释为ISO-8601,它将被解释为日期。“ 2005-07-08”属于此阵营,因此被解析为UTC。“ 2005年7月8日”不能,因此在当地时间进行了解析。

看到JavaScript和日期,真是麻烦!更多。

猴子2020/05/28 10:04:02

在第5版规范发布之前,该Date.parse方法完全依赖实现除后者返回数字而不是a之外,其他方法new Date(string)等效)。在第5版规范中,添加了该要求以支持简化的(并且略有错误) ISO-8601(另请参见JavaScript中有效的日期时间字符串是什么?)。但是除此之外,除了必须接受任何输出(不说那是什么)之外,对什么/ 应该接受没有任何要求Date.parse(string)DateDate.parsenew Date(string)Date#toString

如2017年的ECMAScript(版本8)的,被要求的实现来解析其输出Date#toStringDate#toUTCString,但是未指定这些字符串的格式。

从ECMAScript 2019(版本9)开始,Date#toString的格式Date#toUTCString分别指定为:

  1. ddd MMM DD YYYY HH:mm:ss ZZ [(时区名称)]
    例如Tue Jul 10 2018 18:39:58 GMT + 0530(IST)
  2. ddd,DD MMM YYYY HH:mm:ss Z
    例如 2018年7月10日星期二13:09:58 GMT

提供了2种Date.parse应在新的实现中可靠解析的格式(请注意,支持并不普遍,并且不兼容的实现将在一段时间内继续使用)。

我建议手动解析日期字符串,并将Date构造函数与年,月和日参数一起使用,以避免产生歧义:

// parse a date in yyyy-mm-dd format
function parseDate(input) {

  let parts = input.split('-');

  // new Date(year, month [, day [, hours[, minutes[, seconds[, ms]]]]])
  return new Date(parts[0], parts[1]-1, parts[2]); // Note: months are 0-based
}