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

状况一:

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

输出:

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

案例二:

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

输出:

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


为何第二次解析不正确? java


#1楼

根据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格式,他说这种格式在全部浏览器中都很好。 mysql


#2楼

另外一种解决方案是用日期格式构建一个关联数组,而后从新格式化数据。 c++

此方法对于以异常方式格式化日期颇有用。 sql

一个例子: 数组

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'];

#3楼

这个轻量级的日期解析库应该解决全部相似的问题。 我喜欢该库,由于它很容易扩展。 也有可能(不是很简单,可是就不那么难了)。 浏览器

解析示例: 安全

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") );

#4楼

在最近写JS解释器的经验中,我为ECMA / JS日期的内部工做付出了不少努力。 所以,我认为我将在这里投入2美分。 但愿共享这些内容能够帮助其余人解决有关浏览器在处理日期方面的差别的任何问题。

输入端

全部实如今内部将其日期值存储为64位数字,这些数字表示自1970年1月1日UTC以来的毫秒数(GMT与UTC是同一件事)。 1/1/1970 00:00:00以后的日期为正数,而以前的日期为负数。

所以,如下代码在全部浏览器上都产生彻底相同的结果。

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

在个人时区(EST),结果是1800万,由于这是5个小时内的毫秒数(在夏令时中只有4个小时)。 在不一样的时区,该值将有所不一样。 全部主要的浏览器都以相同的方式进行操做。

这是擦。 尽管主要浏览器会将输入字符串格式解析为日期存在一些差别,但就时区和夏令时而言,它们基本上将它们解释为相同的形式。 支持的一种是ISO 8601格式。 这是ECMA-262 v.5规范中概述的惟一格式。 对于全部其余字符串格式,解释取决于实现。 具备讽刺意味的是,这是浏览器能够不一样的格式。 这是个人计算机上使用ISO 8601字符串格式的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”说明符表示输入已是UTC时间,而且在存储以前不须要偏移。
  • “ -0500”说明符表示输入位于GMT-05:00,所以两个浏览器都将输入解释为位于个人本地时区。 这意味着该值在存储以前已转换为UTC。 在个人状况下,这意味着将日期的内部值加上18000000ms,所以须要-18000000ms(-05:00)的转换才能使我回到本地时间。
  • 可是,若是没有指定符,则FF会将输入视为本地时间,而Chrome会将其视为UTC时间。 对我来讲,这会形成5个小时的储值差别,这是有问题的。 在个人实现中,我最终在这里使用FF,由于我喜欢toString的输出以匹配个人输入值,除非我指定了备用时区,不然我永远不会这样作。 没有指定符应假定本地时间输入。

可是,状况变得更糟的是,FF处理ISO 8601格式的短格式(“ YYYY-MM-DD”)与处理长格式(“ YYYY-MM-DDTHH:mm:ss:sssZ”)的方式有所不一样没有任何逻辑上的缘由。 这是FF的输出,带有长和短ISO日期格式,没有时区说明符。

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时间,而另外一个被解释为本地时间。 这就是为何,

这并不有趣:

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());

底线是用于解析日期字符串的。 长格式是您能够在浏览器中安全解析的惟一ISO 8601字符串。 而且,始终使用“ Z”说明符。 若是这样作,则能够安全地在本地时间和UTC时间之间来回切换。

这适用于全部浏览器(在IE9以后):

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

幸运的是,大多数当前的浏览器的确能平等对待其余输入格式,包括最经常使用的“ 1/1/1970”和“ 1/1/1970 00:00:00 AM”格式。 如下全部格式(以及其余格式)在全部浏览器中均视为本地时间输入,并在存储前转换为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"));

输出侧

在输出端,全部浏览器都以相同的方式转换时区,可是它们对字符串格式的处理方式不一样。 这是toString函数及其输出。 请注意,个人机器上凌晨5:00输出的toUTCStringtoISOString函数。

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

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

直接打印存储的UTC时间

- toUTCString
 - toISOString

In 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

In 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格式输入字符串。 只有当日期须要按字符串排序时,使用这种格式对我有用的惟一时间。 ISO格式能够按原样排序,而其余格式则不能。 若是必须具备跨浏览器的兼容性,请指定时区或使用兼容的字符串格式。

代码new Date('12/4/2013').toString()通过如下内部伪转换:

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

我但愿这个答案会有所帮助。


#5楼

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

从ECMAScript 2017(版本8)开始,要求实现解析Date#toStringDate#toUTCString的输出 ,但未指定这些字符串的格式。

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

  1. ddd MMM DD YYYY HH:mm:ss ZZ [(时区名称)]
    例如,2018年7月10日星期二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) {
  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
}
相关文章
相关标签/搜索