Why does Date.parse give incorrect results?

Case One:

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

Output:

Fri Jul 08 2005 00:00:00 GMT-0700 (PST)

Case Two:

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

Output:

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


Why is the second parse incorrect?


Until the 5th edition spec came out, the Date.parse method was completely implementation dependent ( new Date(string) is equivalent to Date.parse(string) except the latter returns a number rather than a Date ). In the 5th edition spec the requirement was added to support a simplified (and slightly incorrect) ISO-8601, but other than that, there was no requirement for what Date.parse / new Date(string) should accept other than that they had to accept whatever Date#toString output (without saying what that was).

I would recommend you to parse your date string manually, and use the Date constructor with the year, month and day arguments to avoid ambiguity:

// 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
}

During recent experience writing a JS interpreter I wrestled plenty with the inner workings of ECMA/JS dates. So, I figure I'll throw in my 2 cents here. Hopefully sharing this stuff will help others with any questions about the differences among browsers in how they handle dates.

The Input Side

All implementations store their date values internally as 64-bit numbers that represent the number of milliseconds since 1/1/1970 UTC (GMT is the same thing as UTC). Dates occurring after 1/1/1970 00:00:00 are positive numbers and dates prior are negative.

Therefore, the following code produces the exact same result on all browsers.

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

In my timezone (EST), the result is 18000000 because that's how many ms are in 5 hours (it's only 4 hours during daylight savings months). The value will be different in different time zones. All the major browsers do it the same way.

Here is the rub though. While there is some variance in the input string formats that the major browsers will parse as dates, they essentially interpret them the same as far as time zones and daylight savings are concerned. The one hold out is the ISO 8601 format. It's the only format outlined in the ECMA-262 v.5 spec specifically. For all other string formats, the interpretation is implementation-dependent. Ironically, this is the format where browsers can differ. Here is a comparison output of Chrome vs Firefox for 1/1/1970 on my machine using the ISO 8601 string format.

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
  • The "Z" specifier indicates that the input is already in UTC time and requires no offset before storage.
  • The "-0500" specifier indicates that the input is in GMT-05:00 so both browsers interpret the input as being in my local timezone. That means that the value is converted to UTC before being stored. In my case, it means adding 18000000ms to the date's internal value thus requiring a -18000000ms (-05:00) shift to put me back in local time.
  • When there is no specifier however, FF treats the input as local time while Chrome treats it as UTC time. For me this creates a 5 hour difference in the stored value, which is problematic. In my implementation I ended up siding with FF here because I like the output of toString to match my input value unless I specify an alternate timezone, which I never do. The absence of a specifier should presume local time input.
  • But here is where it gets worse, FF treats the short form of the ISO 8601 format ("YYYY-MM-DD") differently than it treats the long form ("YYYY-MM-DDTHH:mm:ss:sssZ") for no logical reason whatsoever. Here is the output from FF with the long and short ISO date formats with no time zone specifier.

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

    So, to answer the original asker's question directly, "YYYY-MM-DD" is the short form of the ISO 8601 format "YYYY-MM-DDTHH:mm:ss:sssZ" . So, it is interpreted as UTC time while the other is interpreted as local. That's why,

    This doesn't jive:

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

    This does:

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

    The bottom line is this for parsing date strings. The ONLY ISO 8601 string that you can safely parse across browsers is the long form. And, ALWAYS use the "Z" specifier. If you do that you can safely go back and forth between local and UTC time.

    This works across browsers (after IE9):

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

    Fortunately, most current browsers do treat the other input formats equally, including the most frequently used '1/1/1970' and '1/1/1970 00:00:00 AM' formats. All of the following formats (and others) are treated as local time input in all browsers and converted to UTC before storage. Thus, making them cross-browser compatible. The output of this code is the same in all browsers in my timezone.

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

    The Output Side

    On the output side, all browsers translate time zones the same way but they handle the string formats differently. Here are the toString functions and what they output. Notice the toUTCString and toISOString functions output 5:00 AM on my machine.

    Converts from UTC to Local time before printing

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

    Prints the stored UTC time directly

     - 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
    

    I normally don't use the ISO format for string input. The only time that using that format is beneficial to me is when dates need to be sorted as strings. The ISO format is sortable as-is while the others are not. If you have to have cross-browser compatibility, either specify the timezone or use a compatible string format.

    The code new Date('12/4/2013').toString() goes through the following internal pseudo-transformation:

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

    I hope this answer was helpful.


    There is some method to the madness. As a general rule, if a browser can interpret a date as an ISO-8601, it will. "2005-07-08" falls into this camp, and so it is parsed as UTC. "Jul 8, 2005" cannot, and so it is parsed in the local time.

    See JavaScript and Dates, What a Mess! for more.

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

    上一篇: java.util.Date使用相同的输入参数显示不同的值

    下一篇: 为什么Date.parse会提供不正确的结果?