content
content copied to clipboard
Update "Non-standard date strings" section of `Date.parse` JS reference
MDN URL
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse#non-standard_date_strings
What specific section or headline is this issue about?
Non-standard date strings
What information was incorrect, unhelpful, or incomplete?
As discovered in #30235, the content in this section is somewhat out of date. A lot of work has been done to Date.parse recently in SpiderMonkey, especially with regard to supporting new formats and achieving parity with Chrome's format support.
What did you expect to see?
More up-to-date information about implementation-specific date formats.
Do you have any supporting links, references, or citations?
#30234, #30235, and the Bugzilla bugs referenced in those PRs.
This section was rewritten a couple of months ago. I've just tested on all three browsers:
- Chrome Canary 121 have the same behaviors
- Safari TP 180 now displays
Date.parse("28")as-61283624160000 - FF nightly 121 now displays
Date.parse("2014-02-30")as1393718400000
Please let me know if there's any other case we should update, or any case you wish to add.
I think a better way would be to use table layout instead of code for this section. I will try to rewrite it and make the case more exhaustive.
A few initial suggestions off the top of my head, just based on the formats already in the docs:
- Single numbers have wildly different behavior depending on if the digit is 0, 1-31, 32-999, or >=1000:
| Input | JavaScriptCore | SpiderMonkey | V8 |
|---|---|---|---|
Date.parse("0") |
-62167219200000 | NaN | 946710000000 |
Date.parse("31") |
-61188912000000 | NaN | NaN |
Date.parse("999") |
-30641760000000 | NaN | -30641733102000 |
Date.parse("1000") |
-30610224000000 | -30610224000000 | -30610224000000 |
- Space delimiters for numeric dates are no longer assumed GMT. Neither are comma delimiters, or really any other delimiter other than dashes in the formal
YYYY-mm-ddformat with no given time, and a strict digit count:
| Input | JavaScriptCore | SpiderMonkey | V8 |
|---|---|---|---|
Date.parse("1970-01-01") |
0 | 0 | 0 |
Date.parse("1970-1-01") |
NaN | 25200000 | 25200000 |
Date.parse("1970,01,01") |
NaN | 25200000 | 25200000 |
Date.parse("1970 01 01") |
NaN | 25200000 | 25200000 |
- Two digit years are complicated. If the first number of a fully numeric date is <=2 digits and is <= 12, it is assumed to be the month. If it is 13-31, it is rejected by SM and V8 because it might be a mday and is therefore ambiguous. If it is 32-49, it is parsed by SM and V8 as year 2032-2049. If it is 50-99, it is parsed by SM and V8 as 1950-1999. Safari just does something crazy.
| Input | JavaScriptCore | SpiderMonkey | V8 |
|---|---|---|---|
Date.parse("12-01-01") |
-61788528000000 | 1007190000000 | 1007190000000 |
Date.parse("31-01-01") |
-61188912000000 | NaN | NaN |
Date.parse("49-01-01") |
-60620832000000 | 2493097200000 | 2493097200000 |
Date.parse("50-01-01") |
-60589296000000 | -631126800000 | -631126800000 |
- To make it more confusing, Safari's behavior differs even further when the delimiter isn't a
'-':(
| Input | JavaScriptCore | SpiderMonkey | V8 |
|---|---|---|---|
Date.parse("12/01/01") |
1007190000000 | 1007190000000 | 1007190000000 |
Date.parse("31/01/01") |
NaN | NaN | NaN |
Date.parse("49/01/01") |
2493097200000 | 2493097200000 | 2493097200000 |
Date.parse("50/01/01") |
-631126800000 | -631126800000 | -631126800000 |
- As you mentioned, FF now rolls over mday which is > the number of days in the parsed month but is still <= 31 (e.g. Apr 31 rolls over to May 1)
> eshost -te 'Date.parse("2014-02-30")'
| Engine | Result |
|---|---|
| JavaScriptCore | NaN |
| SpiderMonkey | 1393743600000 |
| V8 | 1393718400000 |
It's late and I'm sure I'll think of more stuff moving forward as I've gotten quite familiar with the behavior or this function across engines... also see the Bugzilla links referenced in #30234 and #30235 for more examples of our findings and advances during recent development.
If it helps, I did all of this testing and generated all of these tables automatically via a tool that I made which wraps eshost.
For example, the first chart in this post was generated with the command mdhost -f 'Date.parse("#{}")' 0 31 999 1000, which prints the results from eshost and copies a markdown chart of the results to your clipboard. Maybe this will help you with quickly generating at least some starting point for the documentation.
Also, while I understand that this documentation is for Date.parse and not the Date constructor, displaying these results as UTC timestamps is pretty confusing. The eshost output from the constructor paints a much clearer picture of what these formats are being parsed as, and the constructor runs the same function under the hood as Date.parse.
Another potential source of confusion that should be documented is that some of these date formats are assumed local time rather than GMT so these results won't even be consistent across different user locations.
Thanks for the information! I think a lot of the examples are incorporated already, just not in an exhaustive way. Also, the current code is generated by eshost too :)
No problem! Here's a few more...
- Day of week doesn't need to be correct, or even an actual day of week. FF and Chrome allow day of week after month name.
| Input | JavaScriptCore | SpiderMonkey | V8 |
|---|---|---|---|
Date.parse("Thu Jan 01 1970") |
25200000 | 25200000 | 25200000 |
Date.parse("Mon Jan 01 1970") |
25200000 | 25200000 | 25200000 |
Date.parse("foo bar Jan 01 1970") |
25200000 | 25200000 | 25200000 |
Date.parse("Jan Thurs 01 1970") |
NaN | 25200000 | 25200000 |
Date.parse("Jan foo bar 01 1970") |
NaN | 25200000 | 25200000 |
- Day of week works with all formats since FF121. However, note that they aren't assumed GMT when used with a dashed numeric date (this only happens with formal ISO dates with no time specified). Safari doesn't like weekday with some formats.
| Input | JavaScriptCore | SpiderMonkey | V8 |
|---|---|---|---|
Date.parse("1970-01-01") |
0 | 0 | 0 |
Date.parse("Thu 1970-01-01") |
NaN | 25200000 | 25200000 |
Date.parse("Thu Jan.01.1970") |
NaN | 25200000 | 25200000 |
-
FF allows day of week after mday but we are trying to remove this behavior as other engines reject these patterns so we shouldn't encourage it, see bug 1862922.
-
New in FF122 w/ bug 1862910, months now behave like other engines in that only the first 3 characters of a month name matter, the rest can be anything
| Input | JavaScriptCore | SpiderMonkey | V8 |
|---|---|---|---|
Date.parse("Ja 01 1970") |
NaN | NaN | NaN |
Date.parse("Jan 01 1970") |
25200000 | 25200000 | 25200000 |
Date.parse("Janxxx 01 1970") |
25200000 | 25200000 | 25200000 |
- Negative year weirdness, the only reliable way is with expanded years per the spec, other formats are a crap shoot
| Input | JavaScriptCore | SpiderMonkey | V8 |
|---|---|---|---|
new Date("-002022-01-01") |
Sat Dec 31 -2023 16:31:42 GMT-0728 (Mountain Standard Time) | Sat Dec 31 -2023 16:31:42 GMT-0728 (Mountain Standard Time) | Sat Dec 31 -2023 16:31:42 GMT-0728 (Mountain Standard Time) |
new Date("01 01 -2022") |
Invalid Date | Sun Jan 01 -2022 00:00:00 GMT-0728 (Mountain Standard Time) | Invalid Date |
new Date("Jan 1 -2022") |
Sun Jan 01 -2022 00:00:00 GMT-0728 (Mountain Standard Time) | Sun Jan 01 -2022 00:00:00 GMT-0728 (Mountain Standard Time) | Invalid Date |
- Colons do weird things with time, there is an open bug for Chrome parity in this matter
| Input | JavaScriptCore | SpiderMonkey | V8 |
|---|---|---|---|
new Date("1970-01-01 01::02") |
Invalid Date | Thu Jan 01 1970 01:02:00 GMT-0700 (Mountain Standard Time) | Thu Jan 01 1970 01:00:02 GMT-0700 (Mountain Standard Time) |
new Date("05:Jan:2000") |
Invalid Date | Invalid Date | Sat Jan 01 2000 05:00:00 GMT-0700 (Mountain Standard Time) |
What I dread the most is time representations containing English... Like, theoretically you could exhaust all time formats containing dashes, slashes, spaces, etc. but once you have English letters there start to be infinite possibilities.
I tried to read the parser source for Firefox, but no my brain could not generate the formal grammar supported by that parser; I really hope each browser can document the formal syntax of their date strings 😞
It would be really nice if the spec would go further in depth than the current date time string format, rather than telling engines to basically "figure it out". In theory the formal spec would be enough, but in practice a lot of developers use non-standard date formats and then people complain when a website breaks on some browsers.
I agree that coming up with a formal grammar for the current behavior of any given engine is an absolute nightmare, and not even limited to alpha characters which could at least be adequately explained, but once you start getting into the edge cases and the subtle differences in behavior between different formats that appear to display the same date, thar be dragons.
Thankfully, the behavior surrounding different delimiters has vastly improved in FF, but it's still not perfect; '-' behaves slightly differently than its friends '.', '/', and ' '.
Anyway, I think a table with a shit ton of examples is our best bet here, rather than trying to dive into the weeds of defining a grammar which would end up being overly complex and TL;DR for a casual MDN reader just looking to see what formats are supported where.
No we explicitly don't want it to be exhaustive or nearly so—doing so is a nightmare for both us and the readers 😄 Neither would we mention formal syntax, no chance. That was just for my own intellectual curiosity and maybe to save some time of shooting in the dark. My plan is still only to present a few typical cases, showcasing exactly why using non-standard datestrings is a nightmare and why you always need to conduct cross-browser tests before relying on anything. That's the sole purpose. We are not trying to write it as a compatibility data. If you really entertain the idea, maybe you can put it up as a gist or a blog post, and I will definitely link to it.
Agreed. And, I think it'd be appropriate to add a blurb at the top of the section stressing that the non-standard dates are for reference only and that readers should avoid using them at all costs in favor of the spec format. It would literally be more reliable to have an LLM guess what date a user is trying to enter than to feed their input to some random implementation of Date.parse. 😅
By the way, I've added a couple of features to mdhost to help make the tables a bit cleaner for documentation, such as:
- A
-tparameter to specify a separate format string for the table "Input" column, rather than displaying the entire input toeshost - A
-bflag to display browser names rather than engine names
So, now we can do:
mdhost -b -f 'Date.parse("#{}")' -t '"#{}"' "1970-01-01" "Thu 1970-01-01" "Thu Jan.01.1970"
The args after the options are formatted in place of the #{}.
The generated table copied to the clipboard in this example looks like:
| Input | Safari | Firefox | Chrome |
|---|---|---|---|
"1970-01-01" |
0 | 0 | 0 |
"Thu 1970-01-01" |
NaN | 25200000 | 25200000 |
"Thu Jan.01.1970" |
NaN | 25200000 | 25200000 |
It's on RubyGems (gem install mdhost) if you'd like to give it a whirl ^.^
This change in 123 is relevant here: https://bugzilla.mozilla.org/show_bug.cgi?id=1870434
Another case, maybe related to this ticket.
Any string that ends with a -[number] is a valid date.
new Date('something-10') // valid date "2001-09-30T22:00:00.000Z"
Date.parse('hello-10') // also valid "1001887200000"
Date.parse('this-one-too-5') // also valid "988668000000"
@CdTgr A number of other delimiters ( , ., ,, -, /) also work here, see bug 1881930.