为什么Date.parse提供不正确的结果?

案例一:

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

输出:

星期五Jul 08 2005 00:00:00 GMT-0700(PST)

案例二:

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

输出:

星期四07七月2005 17:00:00 GMT-0700(PST)


为什么第二个parsing不正确?

在第五版规范出来之前, Date.parse方法完全依赖实现new Date(string)相当于Date.parse(string)除了后者返回一个数字而不是Date )。 在第五版规范中,添加了这个要求来支持一个简化的(稍微不正确的) ISO-8601 ,但除此之外, 没有什么要求Date.parse / new Date(string)应该接受,而不是他们必须接受任何Date#toString输出(不说那是什么)。

我build议你手动parsing你的datestring,并且使用带有年月日参数的Date构造函数来避免含糊不清:

 // parse a date in yyyy-mm-dd format function parseDate(input) { var 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 } 

在最近的经验写一个JS翻译我与ECMA / JSdate的内部运作大量摔跤。 所以,我想我会在这里投入2美分。 希望分享这些东西可以帮助其他人解决浏览器在处理date方面的差异。

input端

所有实现都在内部将它们的date值存储为64位数字,表示自1970年1月1日以来的毫秒数(GMT与UTC相同)。 在1/1/1970 00:00:00之后发生的date是正数,并且之前的date是负数。

因此,下面的代码在所有浏览器上产生完全相同的结果。

 Date.parse('1/1/1970'); 

在我的时区(EST),结果是18000000,因为这是5个小时内的多less毫秒(在夏令时期间只有4个小时)。 这个值在不同的时区会有所不同。 所有的主stream浏览器都是这样做的。

这是揉搓。 尽pipe主要浏览器将inputstring格式作为date进行parsing,但就时区和夏令时而言,它们基本上将其解释为相同。 一个支持的是ISO 8601格式。 这是ECMA-262 v.5规范中唯一列出的唯一格式。 对于所有其他string格式,解释是依赖于实现的。 具有讽刺意味的是,这是浏览器可以不同的格式。 下面是使用ISO 8601string格式在我的机器上比较1970年1月1日的Chrome和Firefox的输出。

 Date.parse('1970-01-01T00:00:00Z'); // Chrome: 0 FF: 0 Date.parse('1970-01-01T00:00:00-0500'); // Chrome: 18000000 FF: 18000000 Date.parse('1970-01-01T00:00:00'); // Chrome: 0 FF: 18000000 
  • “Z”说明符表示input已经是UTC时间,在存储之前不需要偏移量。
  • “-0500”说明符表示input格林尼治标准时间-05:00,所以两个浏览器将input解释为在我的本地时区。 这意味着在存储之前将该值转换为UTC。 就我而言,这意味着增加18000000ms到date的内部值,因此需要一个-18000000ms(-05:00)的转换让我回到当地时间。
  • 但是,如果没有说明符,则FF会将input视为本地时间,而Chrome会将其视为UTC时间。 对我而言,这会在存储值中产生5小时的差异,这是有问题的。 在我的实现中,我在这里结束了FF,因为我喜欢toString的输出来匹配我的input值,除非我指定一个替代时区,这是我从来没有做过的。 缺less说明者应该假定当地时间input。

但在这里,情况越来越糟,FF将ISO 8601格式(“YYYY-MM-DD”)的缩写forms与对待长格式(“YYYY-MM-DDTHH:mm:ss:sssZ”)没有任何逻辑的理由。 这里是FF的输出与长和短的ISOdate格式,没有时区说明符。

 Date.parse('1970-01-01T00:00:00'); // 18000000 Date.parse('1970-01-01'); // 0 

因此,要直接回答原始提问者的问题, "YYYY-MM-DD"是ISO 8601格式"YYYY-MM-DDTHH:mm:ss:sssZ" 。 所以,它被解释为UTC时间,而另一个被解释为本地。 这就是为什么,

这不是jive:

 console.log(new Date(Date.parse("Jul 8, 2005")).toString()); console.log(new Date(Date.parse("2005-07-08")).toString()); 

这样做:

 console.log(new Date(Date.parse("Jul 8, 2005")).toString()); console.log(new Date(Date.parse("2005-07-08T00:00:00")).toString()); 

底线是parsingdatestring。 唯一可以在浏览器中安全parsing的ISO 8601string是一种长forms。 而且,总是使用“Z”说明符。 如果你这样做,你可以安全地在本地和UTC时间之间来回切换。

这适用于跨浏览器(在IE9之后):

 console.log(new Date(Date.parse("2005-07-08T00:00:00Z")).toString()); 

幸运的是,目前大多数浏览器都会平等对待其他input格式,包括最常用的“1/1/1970”和“1/1/1970 00:00:00 AM”格式。 所有以下格式(及其他格式)在所有浏览器中都被视为本地时间input,并在存储之前转换为UTC。 因此,使他们跨浏览器兼容。 这段代码的输出在我所在时区的所有浏览器中都是一样的。

 console.log(Date.parse("1/1/1970")); console.log(Date.parse("1/1/1970 12:00:00 AM")); console.log(Date.parse("Thu Jan 01 1970")); console.log(Date.parse("Thu Jan 01 1970 00:00:00")); console.log(Date.parse("Thu Jan 01 1970 00:00:00 GMT-0500")); 

输出端

在输出端,所有浏览器都以相同的方式转换时区,但它们处理string格式的方式不同。 这里是toString函数和它们输出的内容。 注意toUTCStringtoISOString函数在我的机器上输出5:00 AM。

打印前从UTC转换为本地时间

  - toString - toDateString - toTimeString - toLocaleString - toLocaleDateString - toLocaleTimeString 

直接打印存储的UTC时间

  - toUTCString - toISOString 

  在Chrome中 
 toString Thu Jan 01 1970 00:00:00 GMT-05:00 (Eastern Standard Time) toDateString Thu Jan 01 1970 toTimeString 00:00:00 GMT-05:00 (Eastern Standard Time) toLocaleString 1/1/1970 12:00:00 AM toLocaleDateString 1/1/1970 toLocaleTimeString 00:00:00 AM toUTCString Thu, 01 Jan 1970 05:00:00 GMT toISOString 1970-01-01T05:00:00.000Z 

  在Firefox中 
 toString Thu Jan 01 1970 00:00:00 GMT-05:00 (Eastern Standard Time) toDateString Thu Jan 01 1970 toTimeString 00:00:00 GMT-0500 (Eastern Standard Time) toLocaleString Thursday, January 01, 1970 12:00:00 AM toLocaleDateString Thursday, January 01, 1970 toLocaleTimeString 12:00:00 AM toUTCString Thu, 01 Jan 1970 05:00:00 GMT toISOString 1970-01-01T05:00:00.000Z 

我通常不使用ISO格式的stringinput。 使用这种格式唯一有利于我的是date需要被sorting为string。 ISO格式可以按原样sorting,而其他格式则不可以。 如果您必须具有跨浏览器兼容性,请指定时区或使用兼容的string格式。

代码new Date('12/4/2013').toString()经历了以下的内部伪转换:

  "12/4/2013" -> toUCT -> [storage] -> toLocal -> print "12/4/2013" 

我希望这个答案是有帮助的。

有一些疯狂的方法。 作为一般规则,如果浏览器可以将date解释为ISO-8601,那么将会如此。 “2005-07-08”落入这个阵营,所以它被parsing为UTC。 “2005年7月8日”不能,所以它是在当地时间parsing。

查看JavaScript和date,真是太棒了! 为更多。

另一种解决方法是用date格式构build一个关联数组,然后重新格式化数据。

这种方法对于非常规格式的date很有用。

一个例子:

  mydate='01.02.12 10:20:43': myformat='dd/mm/yy HH:MM:ss'; dtsplit=mydate.split(/[\/ .:]/); dfsplit=myformat.split(/[\/ .:]/); // creates assoc array for date df = new Array(); for(dc=0;dc<6;dc++) { df[dfsplit[dc]]=dtsplit[dc]; } // uses assc array for standard mysql format dstring[r] = '20'+df['yy']+'-'+df['mm']+'-'+df['dd']; dstring[r] += ' '+df['HH']+':'+df['MM']+':'+df['ss']; 

根据http://blog.dygraphs.com/2012/03/javascript-and-dates-what-mess.html格式“yyyy / mm / dd”解决了常见的问题。 他说:“只要可能,坚持”YYYY / MM / DD“作为你的datestring,这是普遍支持的,毫不含糊,有了这种格式,所有的时间都是本地的。 我已经设置了testing: http : //jsfiddle.net/jlanus/ND2Qg/432/这种格式:+通过使用ymdsorting和4位年份来避免日月顺序模棱两可+避免了UTC与本地问题通过使用斜杠+ danvk, dygraphs家伙遵守ISO格式,说这种格式在所有浏览器中都是好的。

虽然CMS将string传递到parsing方法是正确的 ,但通常不安全,15.9.4.2节中的ECMA-262第5版 (又名ES5)规范build议Date.parse()实际上应该处理ISO格式的date。 旧规范没有提出这样的要求。 当然,旧的浏览器和一些当前的浏览器仍然不提供这种ES5function。

你的第二个例子没有错。 它是以UTC指定的date,如Date.prototype.toISOString()所暗示的Date.prototype.toISOString() ,但在本地时区中表示。

使用moment.jsparsingdate:

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

第三个参数决定了严格的parsing(从2.3.0开始)。 如果没有它,时间也可能会给出不正确的结果。

这个轻量级的dateparsing库应该可以解决所有类似的问题。 我喜欢图书馆,因为它很容易扩展。 这也是可能的(不是很直截了当,但并不难)。

parsing示例:

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

并格式化回string(你会注意到这两种情况给出了完全相同的结果):

 console.log( caseOne.dateFormat("M d, Y") ); console.log( caseTwo.dateFormat("M d, Y") ); console.log( caseOne.dateFormat("Ymd") ); console.log( caseTwo.dateFormat("Ymd") ); 

这里有一个简短的,灵活的代码片段,用于以跨浏览器安全的方式将date时间string转换为由@ drankin2112详细描述的nicel。

 var inputTimestamp = "2014-04-29 13:00:15"; //example var partsTimestamp = inputTimestamp.split(/[ \/:-]/g); if(partsTimestamp.length < 6) { partsTimestamp = partsTimestamp.concat(['00', '00', '00'].slice(0, 6 - partsTimestamp.length)); } //if your string-format is something like '7/02/2014'... //use: var tstring = partsTimestamp.slice(0, 3).reverse().join('-'); var tstring = partsTimestamp.slice(0, 3).join('-'); tstring += 'T' + partsTimestamp.slice(3).join(':') + 'Z'; //configure as needed var timestamp = Date.parse(tstring); 

您的浏览器应该提供与Date.parse相同的时间戳结果:

 (new Date(tstring)).getTime()