Skip to content

Fix Date.parse of a UTC offset or long fraction after the time - #1849

Open
wleeaf wants to merge 1 commit into
quickjs-ng:masterfrom
wleeaf:fix-date-parse-offset-after-time
Open

wleeaf wants to merge 1 commit into
quickjs-ng:masterfrom
wleeaf:fix-date-parse-offset-after-time

Conversation

@wleeaf

@wleeaf wleeaf commented Oct 26, 2026

Copy link
Copy Markdown

For date strings that aren't in the ISO format, js_date_parse_otherstring() parses the time and then calls string_skip_separators(), which skips -. A negative UTC offset directly after the time therefore loses its sign, and the next iteration reads the rest as another time or as a year:

Date.parse(s) before V8 / with this change
2000-01-01 10:00:00-05:00 05:00 local time 2000-01-01T15:00:00.000Z
2000-01-01 10:00:00.250-05:00 05:00:00.250 local time 2000-01-01T15:00:00.250Z
2000-01-01 10:00:00-0500 0500-01-01T10:00:00 (year 500) 2000-01-01T15:00:00.000Z
Jan 1 2000 10:00:00-0500 0500-01-01T10:00:00 2000-01-01T15:00:00.000Z
2000-01-01 10:00:00-05 NaN 2000-01-01T15:00:00.000Z

The first form is what Python's str(datetime) produces for an aware datetime, and PostgreSQL prints timestamptz with an offset like -05. Positive offsets were already parsed (+05:00 works), because + isn't a separator. The time branch now continues with the next iteration when the time is directly followed by + or -. The existing sign handling then parses it as an offset, since a time has been seen.

string_get_milliseconds() also stopped after 9 fraction digits, leaving the rest to be parsed as the next field: 2000-01-01 10:00:00.123456789123+05:00 gave the year 123, and an ISO string with 10 or more digits, such as 2020-01-01T01:01:01.1234567891Z, was NaN. Digits after the third already don't change the value (mul is 0 by then), so the extra digits are now consumed and ignored like the others.

Tests: test_date in tests/test_builtin.js checks the strings above (negative offset with and without milliseconds, -0500, -05, +05:00, the Jan 1 2000 form, and long fractions in both parsers). The new negative-offset checks fail without the change. run-test262 -c tests.conf passes, and so do test262's built-ins/Date tests.

In date strings that aren't in the ISO format, the time is parsed and
then string_skip_separators() skips '-', so a negative UTC offset that
follows the time directly lost its sign and was read as another time:
"2000-01-01 10:00:00-05:00" (the format of Python's str(datetime)) gave
05:00 local time instead of 15:00Z, and "...10:00:00-0500" was read as
the year 500. Positive offsets were parsed correctly. An offset after
the time is now parsed before skipping separators.

string_get_milliseconds() stopped after 9 fraction digits, leaving the
rest to be read as the next field: "...10:00:00.123456789123+05:00"
gave the year 123, and an ISO string with 10 or more digits was NaN.
The extra digits are now consumed and ignored, like those after the
third.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant