content icon indicating copy to clipboard operation
content copied to clipboard

Update "Non-standard date strings" section of `Date.parse` JS reference

Open vinnydiehl opened this issue 1 year ago • 14 comments

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.

vinnydiehl avatar Nov 18 '23 08:11 vinnydiehl

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") as 1393718400000

Please let me know if there's any other case we should update, or any case you wish to add.

Josh-Cena avatar Nov 18 '23 15:11 Josh-Cena

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.

Josh-Cena avatar Nov 18 '23 15:11 Josh-Cena

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-dd format 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.

vinnydiehl avatar Nov 24 '23 11:11 vinnydiehl

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.

vinnydiehl avatar Nov 24 '23 12:11 vinnydiehl

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 :)

Josh-Cena avatar Nov 24 '23 18:11 Josh-Cena

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)

vinnydiehl avatar Nov 25 '23 06:11 vinnydiehl

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 😞

Josh-Cena avatar Nov 25 '23 07:11 Josh-Cena

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.

vinnydiehl avatar Nov 25 '23 07:11 vinnydiehl

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.

Josh-Cena avatar Nov 25 '23 07:11 Josh-Cena

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. 😅

vinnydiehl avatar Nov 25 '23 07:11 vinnydiehl

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 -t parameter to specify a separate format string for the table "Input" column, rather than displaying the entire input to eshost
  • A -b flag 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 ^.^

vinnydiehl avatar Nov 25 '23 08:11 vinnydiehl

This change in 123 is relevant here: https://bugzilla.mozilla.org/show_bug.cgi?id=1870434

vinnydiehl avatar Jan 02 '24 08:01 vinnydiehl

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 avatar May 07 '24 08:05 CdTgr

@CdTgr A number of other delimiters ( , ., ,, -, /) also work here, see bug 1881930.

vinnydiehl avatar May 07 '24 08:05 vinnydiehl