diff --git a/devtools/shared/natural-sort.js b/devtools/shared/natural-sort.js index 0b6a30db5f1c..318dafa650ba 100644 --- a/devtools/shared/natural-sort.js +++ b/devtools/shared/natural-sort.js @@ -22,47 +22,6 @@ const endsWithNullRx = /\0$/; const whitespaceRx = /\s+/g; const startsWithZeroRx = /^0/; const versionRx = /^([\w-]+-)?\d+\.\d+\.\d+$/; -const numericDateRx = /^\d+[- /]\d+[- /]\d+$/; - -// If a string contains any of these, we'll try to parse it as a Date -const dateKeywords = [ - "mon", - "tues", - "wed", - "thur", - "fri", - "sat", - "sun", - - "jan", - "feb", - "mar", - "apr", - "may", - "jun", - "jul", - "aug", - "sep", - "oct", - "nov", - "dec", -]; - -/** - * Figures whether a given string should be considered by naturalSort to be a - * Date, and returns the Date's timestamp if so. Some Date formats, like - * single numbers and MM.DD.YYYY, are not supported due to conflicts with things - * like version numbers. - */ -function tryParseDate(str) { - const lowerCaseStr = str.toLowerCase(); - return ( - !versionRx.test(str) && - (numericDateRx.test(str) || - dateKeywords.some(s => lowerCaseStr.includes(s))) && - Date.parse(str) - ); -} /** * Sort numbers, strings, IP Addresses, Dates, Filenames, version numbers etc. @@ -106,8 +65,10 @@ function naturalSort(a = "", b = "", sessionString, insensitive = false) { .split("\0"); // Hex or date detection. - const aHexOrDate = parseInt(a.match(hexRx), 16) || tryParseDate(a); - const bHexOrDate = parseInt(b.match(hexRx), 16) || tryParseDate(b); + const aHexOrDate = + parseInt(a.match(hexRx), 16) || (!versionRx.test(a) && Date.parse(a)); + const bHexOrDate = + parseInt(b.match(hexRx), 16) || (!versionRx.test(b) && Date.parse(b)); if ( (aHexOrDate || bHexOrDate) && diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index 567b5b24dadd..65fd0a8aee4f 100644 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -1407,16 +1407,6 @@ static bool ParseDate(DateTimeInfo::ForceUTC forceUTC, const CharT* s, if (IsMonthName(s + start, index - start, &mon)) { seenMonthName = true; - // If the next digit is a number, we need to break so it - // gets parsed as mday - if (IsAsciiDigit(s[index])) { - break; - } - } else { - // Reject numbers directly after letters e.g. foo2 - if (IsAsciiDigit(s[index]) && IsAsciiAlpha(s[index - 1])) { - return false; - } } } @@ -1688,48 +1678,46 @@ static bool ParseDate(DateTimeInfo::ForceUTC forceUTC, const CharT* s, continue; } - // Record a month if it is a month name. Note that some numbers are - // initially treated as months; if a numeric field has already been - // interpreted as a month, store that value to the actually appropriate - // date component and set the month here. - int tryMonth; - if (IsMonthName(s + start, index - start, &tryMonth)) { - if (seenMonthName) { - // Overwrite the previous month name - mon = tryMonth; - prevc = 0; - continue; - } - - seenMonthName = true; - - if (mon < 0) { - mon = tryMonth; - } else if (mday < 0) { - mday = mon; - mon = tryMonth; - } else if (year < 0) { - if (mday > 0) { - // If the date is of the form f l month, then when month is - // reached we have f in mon and l in mday. In order to be - // consistent with the f month l and month f l forms, we need to - // swap so that f is in mday and l is in year. - year = mday; - mday = mon; - } else { - year = mon; - } - mon = tryMonth; - } else { - return false; - } - - prevc = 0; - continue; - } - size_t k = std::size(keywords); while (k-- > 0) { + // Record a month if it is a month name. Note that some numbers are + // initially treated as months; if a numeric field has already been + // interpreted as a month, store that value to the actually appropriate + // date component and set the month here. + int tryMonth; + if (IsMonthName(s + start, index - start, &tryMonth)) { + if (seenMonthName) { + // Overwrite the previous month name + mon = tryMonth; + break; + } + + seenMonthName = true; + + if (mon < 0) { + mon = tryMonth; + } else if (mday < 0) { + mday = mon; + mon = tryMonth; + } else if (year < 0) { + if (mday > 0) { + // If the date is of the form f l month, then when month is + // reached we have f in mon and l in mday. In order to be + // consistent with the f month l and month f l forms, we need to + // swap so that f is in mday and l is in year. + year = mday; + mday = mon; + } else { + year = mon; + } + mon = tryMonth; + } else { + return false; + } + + break; + } + const CharsAndAction& keyword = keywords[k]; // If the field doesn't match the keyword, try the next one. @@ -1780,26 +1768,6 @@ static bool ParseDate(DateTimeInfo::ForceUTC forceUTC, const CharT* s, return false; } - // Handle cases where the input is a single number. Single numbers >= 1000 - // are handled by the spec (ParseISOStyleDate), so we don't need to account - // for that here. - if (mon != -1 && year < 0 && mday < 0) { - // Reject 13-31 for Chrome parity - if (mon >= 13 && mon <= 31) { - return false; - } - - mday = 1; - if (mon >= 1 && mon <= 12) { - // 1-12 is parsed as a month with the year defaulted to 2001 - // (again, for Chrome parity) - year = 2001; - } else { - year = FixupNonFullYear(mon); - mon = 1; - } - } - if (year < 0 || mon < 0 || mday < 0) { return false; } diff --git a/js/src/tests/non262/Date/parse-day-of-week.js b/js/src/tests/non262/Date/parse-day-of-week.js index 08bcd3ee052b..cdbfe065ad47 100644 --- a/js/src/tests/non262/Date/parse-day-of-week.js +++ b/js/src/tests/non262/Date/parse-day-of-week.js @@ -62,7 +62,6 @@ const rejected = [ "Sep 26 foo 1995", "Sep 26 1995 foo", "1995 foo Sep 26", - "foo2 Sep 26 1995", ]; for (const format of formats) { diff --git a/js/src/tests/non262/Date/parse-single-number.js b/js/src/tests/non262/Date/parse-single-number.js deleted file mode 100644 index 57afb3c28702..000000000000 --- a/js/src/tests/non262/Date/parse-single-number.js +++ /dev/null @@ -1,57 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const accepted = { - "0": "2000-01-01T00:00:00", - "1": "2001-01-01T00:00:00", - "12": "2001-12-01T00:00:00", - "32": "2032-01-01T00:00:00", - "49": "2049-01-01T00:00:00", - "50": "1950-01-01T00:00:00", - "99": "1999-01-01T00:00:00", - "100": "0100-01-01T00:00:00", - "999": "0999-01-01T00:00:00", - "1000": "1000-01-01T00:00:00Z", - - // Rejecting e.g. S22 (see rejected patterns below) shouldn't - // reject mday directly after month name - "Sep26 1995": "1995-09-26T00:00:00", -}; -const rejected = [ - "S22", - "5C", - "Sep26 foo 1995", -]; - -for (const [test, expected] of Object.entries(accepted)) { - const testDate = new Date(test); - const expectedDate = new Date(expected); - - assertEq( - false, isNaN(testDate), - `${test} should be accepted.` - ); - - assertEq( - testDate.getTime(), expectedDate.getTime(), - `"${test}" should be ${expectedDate} (got ${testDate}).` - ); -} - -for (let i = 13; i <= 31; ++i) { - assertEq( - true, isNaN(new Date(`${i}`)), - `"${i}" should be rejected.` - ); -} -for (const reject of rejected) { - assertEq( - true, isNaN(new Date(reject)), - `"${reject}" should be rejected.` - ); -} - -if (typeof reportCompare === "function") - reportCompare(true, true);