From a26472fb0c19b4774671b795c48f01f40273152d Mon Sep 17 00:00:00 2001 From: Michael Anthony Leon Date: Fri, 14 Oct 2022 11:48:10 -0700 Subject: [PATCH] Fault-tolerant Date parsing for dashes Summary: This is addressing a gap between Hermes and V8 regarding date parsing, raised in https://github.com/facebook/hermes/issues/796 . There is whitespace allowed between M D Y. However, we should also support allowing a dash to be present as the first character in those gaps. Reviewed By: jpporto Differential Revision: D40368889 fbshipit-source-id: 5a9529300a221609ebb5c59dc44c4cf97a153700 --- lib/VM/JSLib/DateUtil.cpp | 27 +++++++++++++++++++++------ test/hermes/date-constructor.js | 19 +++++++++++++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/lib/VM/JSLib/DateUtil.cpp b/lib/VM/JSLib/DateUtil.cpp index 3519884cda5..9bc49929639 100644 --- a/lib/VM/JSLib/DateUtil.cpp +++ b/lib/VM/JSLib/DateUtil.cpp @@ -901,7 +901,7 @@ static double parseESDate(StringView str) { /// Reads the next \p len characters into `tok`, /// but instead of consuming \p len chars, it consumes a single word - /// whatever how long it is (i.e. until a space is encountered). + /// whatever how long it is (i.e. until a space or dash is encountered). /// e.g. /// &str ="Garbage G MayG" /// scanStrAndSkipWord(3); consumeSpaces(); // &str="G MayG", &tok="Gar" @@ -913,7 +913,7 @@ static double parseESDate(StringView str) { if (it + len > str.end()) return false; tok = str.slice(it, it + len); - while (it != str.end() && !std::isspace(*it)) + while (it != str.end() && !std::isspace(*it) && *it != '-') it++; return true; }; @@ -931,6 +931,21 @@ static double parseESDate(StringView str) { ++it; }; + /// Only one dash is ever allowed, and the dash must come first. + /// There is no limit to the number of spaces. + /// This is in line with V8's behavior. + auto consumeSpacesOrDash = [&]() { + auto first = true; + while (it != str.end()) { + if (std::isspace(*it) || (first && *it == '-')) { + ++it; + } else { + return; + } + first = false; + } + }; + // Weekday if (!scanStr(3)) return nan; @@ -964,7 +979,7 @@ static double parseESDate(StringView str) { // Day scanInt(it, end, d); // Month - consumeSpaces(); + consumeSpacesOrDash(); // e.g. `Janwhatever` will get read as `Jan` if (!scanStrAndSkipWord(3)) return nan; @@ -980,7 +995,7 @@ static double parseESDate(StringView str) { // `tok` is now set to the Month candidate. if (tokIsMonth()) { // Day - consumeSpaces(); + consumeSpacesOrDash(); if (!scanInt(it, end, d)) return nan; break; @@ -993,12 +1008,12 @@ static double parseESDate(StringView str) { } // Year - consumeSpaces(); + consumeSpacesOrDash(); if (!scanInt(it, end, y)) return nan; // Hour:minute:second. - consumeSpaces(); + consumeSpacesOrDash(); if (it != end) { if (!scanInt(it, end, h)) diff --git a/test/hermes/date-constructor.js b/test/hermes/date-constructor.js index 1640aa49c58..8e445f0fc1a 100644 --- a/test/hermes/date-constructor.js +++ b/test/hermes/date-constructor.js @@ -313,6 +313,25 @@ print(Date.parse('Tue May 05 2020 00:00:00'.padEnd(60))); // trailing spaces. print(Date.parse('Tue May 05 2020'.padEnd(60))); // CHECK-NEXT: 1588662000000 +// Fault tolerance on dashes +print(Date.parse("Friday, 02 Aug 2019 07:14:03 UTC")); +// CHECK-NEXT: 1564730043000 +print(Date.parse("Friday, 02-Aug-2019-07:14:03 UTC")); +// CHECK-NEXT: 1564730043000 +print(Date.parse("Friday, 02- Aug-2019-07:14:03 UTC")); +// CHECK-NEXT: 1564730043000 +print(Date.parse("Friday, 02- Aug- 2019- 07:14:03 UTC")); +// CHECK-NEXT: 1564730043000 +// Invalid use of dashes +print(Date.parse("Friday, 02 - Aug-2019-07:14:03 UTC")); +// CHECK-NEXT: NaN +print(Date.parse("Friday, 02-Aug -2019-07:14:03 UTC")); +// CHECK-NEXT: NaN +print(Date.parse("Friday, 02-Aug-2019- -07:14:03 UTC")); +// CHECK-NEXT: NaN +print(Date.parse("Friday, 02-Aug-2019 -07:14:03 UTC")); +// CHECK-NEXT: NaN + // Quick check that getters work; internal functions are unit tested instead. print('getters'); // CHECK-LABEL: getters