为什么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"));

输出:

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


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


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

我建议你手动解析你的日期字符串,并使用带有年月日参数的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 / JS日期的内部工作搏斗了很多。 所以,我想我会在这里投入2美分。 希望分享这些东西可以帮助其他人解决浏览器在处理日期方面的差异。

输入端

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

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

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

在我的时区(EST)中,结果为18000000,因为这是5个小时内有多少ms(在夏令时期间只有4个小时)。 该值在不同时区会有所不同。 所有主流浏览器都以同样的方式进行操作。

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

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”说明符表示输入格林尼治标准时间-05:00,因此两个浏览器都将输入解释为处于本地时区。 这意味着该值在被存储之前转换为UTC。 就我而言,这意味着将18000000ms添加到日期的内部值,因此需要-18000000ms(-05:00)的转换才能让我回到当地时间。
  • 但是,如果没有说明符,则FF会将输入视为本地时间,而Chrome会将其视为UTC时间。 对我而言,这会在储值中产生5小时的差异,这是有问题的。 在我的实现中,我在这里结束了FF,因为我喜欢toString的输出以匹配我的输入值,除非我指定一个替代时区,这是我从来没有做过的。 缺少说明者应该假定当地时间输入。
  • 但FF的情况变得更糟了,FF将长格式(“YYYY-MM-DDTHH:mm:ss:sssZ”)视为ISO 8601格式的短格式(“YYYY-MM-DD”)无论如何没有任何逻辑的理由。 这里是来自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函数和它们输出的内容。 注意toUTCStringtoISOString函数在我的机器上输出5:00 AM。

    打印前从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"
    

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


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

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

    链接地址: http://www.djcxy.com/p/387.html

    上一篇: Why does Date.parse give incorrect results?

    下一篇: How is Docker different from a normal virtual machine?