Title: [254939] trunk
Revision
254939
Author
[email protected]
Date
2020-01-22 12:55:31 -0800 (Wed, 22 Jan 2020)

Log Message

[JSC] DateMath should accept more ISO-8601 timezone designators even if they are not included in ECMA262 to produce expected results in the wild code
https://bugs.webkit.org/show_bug.cgi?id=160287

Reviewed by Ross Kirsling.

JSTests:

* stress/relaxed-timezone-designators.js: Added.
(shouldBe):
(parsedDate):

Source/WTF:

While ECMA262[1] always requires ":" in a timezone designator between hours and minutes (like, "hh:mm"),
ISO-8601 can accept additional forms, "+hh" and "+hhmm". This patch relaxes our Date parsing to accept this
type of timezone designators so that we can accept wider forms of date time formats. This addition does not
break the existing parsing rules since '+-' prefix can clarify that following sequences are timezones.

[1]: https://tc39.es/ecma262/#sec-date-time-string-format
[2]: https://en.wikipedia.org/wiki/ISO_8601#Time_zone_designators

* wtf/DateMath.cpp:
(WTF::parseES5TimePortion):
(WTF::parseES5DateFromNullTerminatedCharacters):

LayoutTests:

* js/date-parse-test-expected.txt:
* js/script-tests/date-parse-test.js:

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (254938 => 254939)


--- trunk/JSTests/ChangeLog	2020-01-22 20:52:50 UTC (rev 254938)
+++ trunk/JSTests/ChangeLog	2020-01-22 20:55:31 UTC (rev 254939)
@@ -1,5 +1,16 @@
 2020-01-22  Yusuke Suzuki  <[email protected]>
 
+        [JSC] DateMath should accept more ISO-8601 timezone designators even if they are not included in ECMA262 to produce expected results in the wild code
+        https://bugs.webkit.org/show_bug.cgi?id=160287
+
+        Reviewed by Ross Kirsling.
+
+        * stress/relaxed-timezone-designators.js: Added.
+        (shouldBe):
+        (parsedDate):
+
+2020-01-22  Yusuke Suzuki  <[email protected]>
+
         [JSC] Add CheckArrayOrEmpty to handle the case when hoisting CheckArray for places where input can be empty
         https://bugs.webkit.org/show_bug.cgi?id=206571
         <rdar://problem/58757016>

Added: trunk/JSTests/stress/relaxed-timezone-designators.js (0 => 254939)


--- trunk/JSTests/stress/relaxed-timezone-designators.js	                        (rev 0)
+++ trunk/JSTests/stress/relaxed-timezone-designators.js	2020-01-22 20:55:31 UTC (rev 254939)
@@ -0,0 +1,96 @@
+function shouldBe(actual, expected) {
+    if (Number.isNaN(actual) || actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function parsedDate(string) {
+    return Date.parse(string);
+}
+
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+0`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-0`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+000`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-000`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+00000`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-00000`)), true);
+
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+:00`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-:00`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+0:00`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-0:00`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+000:00`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-000:00`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+0000:00`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-0000:00`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+00000:00`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-00000:00`)), true);
+
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+00:`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-00:`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+00:0`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-00:0`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+00:000`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-00:000`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+00:0000`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-00:0000`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+00:00000`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-00:00000`)), true);
+
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+25`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-25`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+2500`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-2500`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+0080`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-0080`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+0060`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-0060`)), true);
+
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+24A`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-24A`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+2400A`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-2400A`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+0080A`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-0080A`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+0059A`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-0059A`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+24:00A`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-24:00A`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+00:80A`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-00:80A`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+00:59A`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-00:59A`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+24:00:`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-24:00:`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+00:80:`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-00:80:`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+00:59:`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-00:59:`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+24:00:00`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-24:00:00`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+00:80:00`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-00:80:00`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+00:59:00`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-00:59:00`)), true);
+
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000++2`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-+2`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000++200`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-+200`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000+00:0+`)), true);
+shouldBe(Number.isNaN(parsedDate(`1970-01-01T00:00:00.000-00:0+`)), true);
+
+shouldBe(parsedDate(`1970-01-01T00:00:00.000+00`), 0);
+shouldBe(parsedDate(`1970-01-01T00:00:00.000-00`), 0);
+shouldBe(parsedDate(`1970-01-01T00:00:00.000+0000`), 0);
+shouldBe(parsedDate(`1970-01-01T00:00:00.000-0000`), 0);
+shouldBe(parsedDate(`1970-01-01T00:00:00.000+0030`), -(60 * 30 * 1000));
+shouldBe(parsedDate(`1970-01-01T00:00:00.000-0030`), +(60 * 30 * 1000));
+shouldBe(parsedDate(`1970-01-01T00:00:00.000+0130`), -(60 * (30 + 60) * 1000));
+shouldBe(parsedDate(`1970-01-01T00:00:00.000-0130`), +(60 * (30 + 60) * 1000));
+
+shouldBe(parsedDate(`1970-01-01T00:00:00.000+24:59`), -((24 * 60 + 59) * 60 * 1000));
+shouldBe(parsedDate(`1970-01-01T00:00:00.000-24:59`), +((24 * 60 + 59) * 60 * 1000));
+shouldBe(parsedDate(`1970-01-01T00:00:00.000+2459`), -((24 * 60 + 59) * 60 * 1000));
+shouldBe(parsedDate(`1970-01-01T00:00:00.000-2459`), +((24 * 60 + 59) * 60 * 1000));

Modified: trunk/LayoutTests/ChangeLog (254938 => 254939)


--- trunk/LayoutTests/ChangeLog	2020-01-22 20:52:50 UTC (rev 254938)
+++ trunk/LayoutTests/ChangeLog	2020-01-22 20:55:31 UTC (rev 254939)
@@ -1,3 +1,13 @@
+2020-01-22  Yusuke Suzuki  <[email protected]>
+
+        [JSC] DateMath should accept more ISO-8601 timezone designators even if they are not included in ECMA262 to produce expected results in the wild code
+        https://bugs.webkit.org/show_bug.cgi?id=160287
+
+        Reviewed by Ross Kirsling.
+
+        * js/date-parse-test-expected.txt:
+        * js/script-tests/date-parse-test.js:
+
 2020-01-22  Diego Pino Garcia  <[email protected]>
 
         [GTK] Gardening, update TestExpectations

Modified: trunk/LayoutTests/js/date-parse-test-expected.txt (254938 => 254939)


--- trunk/LayoutTests/js/date-parse-test-expected.txt	2020-01-22 20:52:50 UTC (rev 254938)
+++ trunk/LayoutTests/js/date-parse-test-expected.txt	2020-01-22 20:55:31 UTC (rev 254939)
@@ -78,9 +78,12 @@
 PASS String(Date.parse('1970-01-01T00:00:00.000-')) is "NaN"
 PASS String(Date.parse('1970-01-01T00:00:00.000+')) is "NaN"
 PASS String(Date.parse('1970-01-01T00:00:00.000+0')) is "NaN"
-PASS String(Date.parse('1970-01-01T00:00:00.000+00')) is "NaN"
+PASS String(Date.parse('1970-01-01T00:00:00.000+00')) is "0"
 PASS String(Date.parse('1970-01-01T00:00:00.000+00:')) is "NaN"
 PASS String(Date.parse('1970-01-01T00:00:00.000+00:0')) is "NaN"
+PASS String(Date.parse('1970-01-01T00:00:00.000+0000')) is "0"
+PASS String(Date.parse('1970-01-01T00:00:00.000+0000:')) is "NaN"
+PASS String(Date.parse('1970-01-01T00:00:00.000+0000:0')) is "NaN"
 PASS String(Date.parse('1970-01-01T00:00:00.000+0:0')) is "NaN"
 PASS Date.parse("Dec 25 1995 GMT") == 819849600000 is true
 PASS Date.parse("DEC 25 1995 GMT") == 819849600000 is true

Modified: trunk/LayoutTests/js/script-tests/date-parse-test.js (254938 => 254939)


--- trunk/LayoutTests/js/script-tests/date-parse-test.js	2020-01-22 20:52:50 UTC (rev 254938)
+++ trunk/LayoutTests/js/script-tests/date-parse-test.js	2020-01-22 20:55:31 UTC (rev 254939)
@@ -130,9 +130,12 @@
 shouldBe("String(Date.parse('1970-01-01T00:00:00.000-'))", '"NaN"');
 shouldBe("String(Date.parse('1970-01-01T00:00:00.000+'))", '"NaN"');
 shouldBe("String(Date.parse('1970-01-01T00:00:00.000+0'))", '"NaN"');
-shouldBe("String(Date.parse('1970-01-01T00:00:00.000+00'))", '"NaN"');
+shouldBe("String(Date.parse('1970-01-01T00:00:00.000+00'))", '"0"');
 shouldBe("String(Date.parse('1970-01-01T00:00:00.000+00:'))", '"NaN"');
 shouldBe("String(Date.parse('1970-01-01T00:00:00.000+00:0'))", '"NaN"');
+shouldBe("String(Date.parse('1970-01-01T00:00:00.000+0000'))", '"0"');
+shouldBe("String(Date.parse('1970-01-01T00:00:00.000+0000:'))", '"NaN"');
+shouldBe("String(Date.parse('1970-01-01T00:00:00.000+0000:0'))", '"NaN"');
 shouldBe("String(Date.parse('1970-01-01T00:00:00.000+0:0'))", '"NaN"');
 
 // test old implementation fallback

Modified: trunk/Source/WTF/ChangeLog (254938 => 254939)


--- trunk/Source/WTF/ChangeLog	2020-01-22 20:52:50 UTC (rev 254938)
+++ trunk/Source/WTF/ChangeLog	2020-01-22 20:55:31 UTC (rev 254939)
@@ -1,3 +1,22 @@
+2020-01-22  Yusuke Suzuki  <[email protected]>
+
+        [JSC] DateMath should accept more ISO-8601 timezone designators even if they are not included in ECMA262 to produce expected results in the wild code
+        https://bugs.webkit.org/show_bug.cgi?id=160287
+
+        Reviewed by Ross Kirsling.
+
+        While ECMA262[1] always requires ":" in a timezone designator between hours and minutes (like, "hh:mm"),
+        ISO-8601 can accept additional forms, "+hh" and "+hhmm". This patch relaxes our Date parsing to accept this
+        type of timezone designators so that we can accept wider forms of date time formats. This addition does not
+        break the existing parsing rules since '+-' prefix can clarify that following sequences are timezones.
+
+        [1]: https://tc39.es/ecma262/#sec-date-time-string-format
+        [2]: https://en.wikipedia.org/wiki/ISO_8601#Time_zone_designators
+
+        * wtf/DateMath.cpp:
+        (WTF::parseES5TimePortion):
+        (WTF::parseES5DateFromNullTerminatedCharacters):
+
 2020-01-21  Ross Kirsling  <[email protected]>
 
         [JSC] Date parse logic should be less redundant

Modified: trunk/Source/WTF/wtf/DateMath.cpp (254938 => 254939)


--- trunk/Source/WTF/wtf/DateMath.cpp	2020-01-22 20:52:50 UTC (rev 254938)
+++ trunk/Source/WTF/wtf/DateMath.cpp	2020-01-22 20:55:31 UTC (rev 254939)
@@ -518,7 +518,7 @@
     return postParsePosition;
 }
 
-// Parses a time with the format HH:mm[:ss[.sss]][Z|(+|-)00:00].
+// Parses a time with the format HH:mm[:ss[.sss]][Z|(+|-)(00:00|0000|00)].
 // Fractional seconds parsing is lenient, allows any number of digits.
 // Returns 0 if a parse error occurs, else returns the end of the parsed portion of the string.
 static char* parseES5TimePortion(char* currentPosition, long& hours, long& minutes, double& seconds, bool& isLocalTime, long& timeZoneSeconds)
@@ -577,6 +577,7 @@
     if (*currentPosition == 'Z')
         return currentPosition + 1;
 
+    // Parse (+|-)(00:00|0000|00).
     bool tzNegative;
     if (*currentPosition == '-')
         tzNegative = true;
@@ -588,25 +589,39 @@
     }
     ++currentPosition;
     
-    long tzHours;
-    long tzHoursAbs;
-    long tzMinutes;
+    long tzHours = 0;
+    long tzHoursAbs = 0;
+    long tzMinutes = 0;
     
     if (!isASCIIDigit(*currentPosition))
         return 0;
     if (!parseLong(currentPosition, &postParsePosition, 10, &tzHours))
         return 0;
-    if (*postParsePosition != ':' || (postParsePosition - currentPosition) != 2)
-        return 0;
-    tzHoursAbs = labs(tzHours);
-    currentPosition = postParsePosition + 1;
+    if (*postParsePosition != ':') {
+        if ((postParsePosition - currentPosition) == 2) {
+            // "00" case.
+            tzHoursAbs = labs(tzHours);
+        } else if ((postParsePosition - currentPosition) == 4) {
+            // "0000" case.
+            tzHoursAbs = labs(tzHours);
+            tzMinutes = tzHoursAbs % 100;
+            tzHoursAbs = tzHoursAbs / 100;
+        } else
+            return 0;
+    } else {
+        // "00:00" case.
+        if ((postParsePosition - currentPosition) != 2)
+            return 0;
+        tzHoursAbs = labs(tzHours);
+        currentPosition = postParsePosition + 1; // Skip ":".
     
-    if (!isASCIIDigit(*currentPosition))
-        return 0;
-    if (!parseLong(currentPosition, &postParsePosition, 10, &tzMinutes))
-        return 0;
-    if ((postParsePosition - currentPosition) != 2)
-        return 0;
+        if (!isASCIIDigit(*currentPosition))
+            return 0;
+        if (!parseLong(currentPosition, &postParsePosition, 10, &tzMinutes))
+            return 0;
+        if ((postParsePosition - currentPosition) != 2)
+            return 0;
+    }
     currentPosition = postParsePosition;
     
     if (tzHoursAbs > 24)
@@ -647,7 +662,7 @@
     // Look for a time portion.
     // Note: As of ES2016, when a UTC offset is missing, date-time forms are local time while date-only forms are UTC.
     if (*currentPosition == 'T') {
-        // Parse the time HH:mm[:ss[.sss]][Z|(+|-)00:00]
+        // Parse the time HH:mm[:ss[.sss]][Z|(+|-)(00:00|0000|00)]
         currentPosition = parseES5TimePortion(currentPosition + 1, hours, minutes, seconds, isLocalTime, timeZoneSeconds);
         if (!currentPosition)
             return std::numeric_limits<double>::quiet_NaN();
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to