JavaScript date-only format guaranteed to be inter

2019-08-14 09:01发布

问题:

I am looking for a date format (only year, month and day, no time component), that will be guaranteed to be interpreted as local time, in all reasonably modern browsers, when passed to the Date() constructor in JavaScript.

The (3-letter English month abbreviation) (2-digit date) (4-digit year) format seems to work. When I do new Date('Apr 01 2015') on my English system, in Chrome, with my machine being in the Eastern timezone, I get back Wed Apr 01 2015 00:00:00 GMT-0400 (Eastern Daylight Time) which is exactly what I want.

However, I am not sure if this (3-letter English month abbreviation) (2-digit date) (4-digit year) format is guaranteed to be interpreted in the same way regardless of browser and browser/OS locale/language.

Will this format work the same way across different (reasonably modern) browsers and locales? If not, then is there some other format that will work?

Please note that ISO-8601 doesn't answer this question. ISO-8601 date-only strings are interpreted as being in UTC, and not in local time, so new Date('2015-04-01') is interpreted as Tue Mar 31 2015 20:00:00 GMT-0400 (Eastern Daylight Time), i.e. April 1 turns into March 31, which is not what I'm looking for.

回答1:

The YYYY/MM/DD format, specifically in that sequence and using slashes as separators, will always be interpreted as local time, and will work regardless of language or culture. In other words, regardless of whether your culture uses YYYY/MM/DD, MM/DD/YYYY or DD/MM/YYYY, the YYYY/MM/DD format will always work. When a four-digit number comes first, it is always interpreted as year, the middle part is always interpreted as months, and the third part is always interpreted as days.

For example:

new Date("2015/12/15")

This is not a standard though. It's just a discovered truth. It's relatively safe, as there are no places in the world (AFAIK) that use a YYYY/DD/MM format. See Wikipedia's list of Date format by country. Also, it appears to function correctly even when the locale's separator is something other than / or -, such as . used in some locales.

The only case I've seen with this not working is in older versions of Safari, where it would return "Invalid Date" unless it matched the culture in effect. Newer browsers should be fine.

Of course, the better way to approach this is with a library that can guarantee consistency. I recommend moment.js, where you can do:

moment("2015-12-31")

OR

moment("2015-12-31", "YYYY-MM-DD")

... both of which are explicitly local time. UTC would use moment.utc(...) instead of just moment(...).



回答2:

I am looking for a date format (only year, month and day, no time component), that will be guaranteed to be interpreted as local time, in all reasonably modern browsers, when passed to the Date() constructor in JavaScript.

There isn't one. There are a number of formats that, in practice, are reliably parsed by all browsers in use however none of them are required by ECMA-262 to be supported by ECMAScript implementations.

There is one format, a version of ISO 8601, that is specified in ES5 and ECMAScript 2015 however dates without a time zone are to be treated as UTC in ES5 and "local" in ECMAScript 2015.

But that was seen by implementers as "breaking the web", so while some browsers implemented that for a while, they have now reverted to ES5 behaviour and treat a missing timezone as UTC.

A small change in format such as changing the separator from "-" to "/" causes a reversion to implementation dependent behaviour and may cause the date to be treated as UTC or local (though most seem to treat 2015/12/17 as local).

So if you want a guarantee, there isn't one. However, if you are prepared to write a 2 line function, you can parse an ISO 8601 like string reliably based on whatever time zone you like. If you want it to be treated as local (i.e. set to 00:00:00 and offset based on system settings), then:

function parseISOLike(s) {
    var b = s.split(/\D/);
    return new Date(b[0], b[1]-1, b[2])
}

document.write(parseISOLike('2015-12-17'));

will do the job (without checking for valid values) and allow the separator to be any non–digit, so 2015/12/17, 2015-12-17 and 2015.12.17 will all be correctly parsed.

Edit

Parts of the above have been edited thanks to input from Matt Johnson.