Skip to content

Commit abb4315

Browse files
committed
Fix parsing dates with more than 9 contiguous digits
Most engines like v8, and current versions of spidermonkey versions (128 at least) return NaN while QuickJS parses up to 9 digits at a time, then tries to parse the rest. Trying to parse extra digits can sometimes produce random garbage. To fix it, when parsing the initial integer parse as many digits as we can (max = 0) instead of just 9. Add a few tests, including uncommenting some previous ones, and ensure they pass on v8 version 11. Fixes: #384
1 parent 6e2e68f commit abb4315

File tree

2 files changed

+26
-7
lines changed

2 files changed

+26
-7
lines changed

quickjs.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49998,22 +49998,25 @@ static int string_skip_until(const uint8_t *sp, int *pp, const char *stoplist) {
4999849998
}
4999949999

5000050000
/* parse a numeric field (max_digits = 0 -> no maximum) */
50001+
/* return FALSE if the value overflows INT32_MAX */
5000150002
static BOOL string_get_digits(const uint8_t *sp, int *pp, int *pval,
5000250003
int min_digits, int max_digits)
5000350004
{
50004-
int v = 0;
50005+
uint64_t v = 0;
5000550006
int c, p = *pp, p_start;
5000650007

5000750008
p_start = p;
5000850009
while ((c = sp[p]) >= '0' && c <= '9') {
5000950010
v = v * 10 + c - '0';
50011+
if (v >= INT32_MAX)
50012+
return FALSE;
5001050013
p++;
5001150014
if (p - p_start == max_digits)
5001250015
break;
5001350016
}
5001450017
if (p - p_start < min_digits)
5001550018
return FALSE;
50016-
*pval = v;
50019+
*pval = (int)v;
5001750020
*pp = p;
5001850021
return TRUE;
5001950022
}
@@ -50053,7 +50056,7 @@ static BOOL string_get_tzoffset(const uint8_t *sp, int *pp, int *tzp, BOOL stric
5005350056
sgn = sp[p++];
5005450057
if (sgn == '+' || sgn == '-') {
5005550058
int n = p;
50056-
if (!string_get_digits(sp, &p, &hh, 1, 9))
50059+
if (!string_get_digits(sp, &p, &hh, 1, 0))
5005750060
return FALSE;
5005850061
n = p - n;
5005950062
if (strict && n != 2 && n != 4)
@@ -50245,7 +50248,7 @@ static BOOL js_date_parse_otherstring(const uint8_t *sp,
5024550248
*is_local = FALSE;
5024650249
} else {
5024750250
p++;
50248-
if (string_get_digits(sp, &p, &val, 1, 9)) {
50251+
if (string_get_digits(sp, &p, &val, 1, 0)) {
5024950252
if (c == '-') {
5025050253
if (val == 0)
5025150254
return FALSE;
@@ -50256,7 +50259,7 @@ static BOOL js_date_parse_otherstring(const uint8_t *sp,
5025650259
}
5025750260
}
5025850261
} else
50259-
if (string_get_digits(sp, &p, &val, 1, 9)) {
50262+
if (string_get_digits(sp, &p, &val, 1, 0)) {
5026050263
if (string_skip_char(sp, &p, ':')) {
5026150264
/* time part */
5026250265
fields[3] = val;

tests/test_builtin.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -540,12 +540,28 @@ function test_date()
540540
// is not a valid instance of this format.
541541
// Hence the fractional part after . should have 3 digits and how
542542
// a different number of digits is handled is implementation defined.
543+
// As of March, 2025 these tests match the results of v8 11.3
543544
assert(Date.parse(""), NaN);
545+
assert(Date.parse("13"), NaN);
546+
assert(Date.parse("31"), NaN);
547+
assert(Date.parse("1000"), -30610224000000);
548+
assert(Date.parse("1969"), -31536000000);
549+
assert(Date.parse("1970"), 0);
544550
assert(Date.parse("2000"), 946684800000);
551+
assert(Date.parse("9999"), 253370764800000);
552+
assert(Date.parse("275761"), NaN);
553+
assert(Date.parse("999999"), NaN);
554+
assert(Date.parse("1000000000"), NaN);
555+
assert(Date.parse("-271821"), NaN);
556+
assert(Date.parse("-271820"), -8639977881600000);
557+
assert(Date.parse("-100000"), -3217862419200000);
558+
assert(Date.parse("+100000"), 3093527980800000);
559+
assert(Date.parse("+275760"), 8639977881600000);
560+
assert(Date.parse("+275761"), NaN);
545561
assert(Date.parse("2000-01"), 946684800000);
546562
assert(Date.parse("2000-01-01"), 946684800000);
547-
//assert(Date.parse("2000-01-01T"), NaN);
548-
//assert(Date.parse("2000-01-01T00Z"), NaN);
563+
assert(Date.parse("2000-01-01T"), NaN);
564+
assert(Date.parse("2000-01-01T00Z"), NaN);
549565
assert(Date.parse("2000-01-01T00:00Z"), 946684800000);
550566
assert(Date.parse("2000-01-01T00:00:00Z"), 946684800000);
551567
assert(Date.parse("2000-01-01T00:00:00.1Z"), 946684800100);

0 commit comments

Comments
 (0)