diff --git a/Libraries/LibWeb/HTML/Dates.cpp b/Libraries/LibWeb/HTML/Dates.cpp
index e898898d9a5..684088a4591 100644
--- a/Libraries/LibWeb/HTML/Dates.cpp
+++ b/Libraries/LibWeb/HTML/Dates.cpp
@@ -209,25 +209,6 @@ bool is_valid_time_string(StringView value)
return true;
}
-// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-time-string
-WebIDL::ExceptionOr> parse_time_string(JS::Realm& realm, StringView value)
-{
- // FIXME: Implement spec compliant time string parsing
- auto parts = value.split_view(':', SplitBehavior::KeepEmpty);
- if (parts.size() >= 2) {
- if (auto hours = parts.at(0).to_number(); hours.has_value()) {
- if (auto minutes = parts.at(1).to_number(); minutes.has_value()) {
- if (parts.size() >= 3) {
- if (auto seconds = parts.at(2).to_number(); seconds.has_value())
- return JS::Date::create(realm, JS::make_time(*hours, *minutes, *seconds, 0));
- }
- return JS::Date::create(realm, JS::make_date(0, JS::make_time(*hours, *minutes, 0, 0)));
- }
- }
- }
- return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Can't parse time string"sv };
-}
-
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-month-component
static Optional parse_a_month_component(GenericLexer& input)
{
@@ -440,7 +421,7 @@ static Optional parse_a_time_component(GenericLexer& input)
return {};
// 6. Let second be 0.
- i32 second = 0;
+ f32 second = 0;
// 7. If position is not beyond the end of input and the character at position is U+003A (:), then:
if (input.consume_specific(':')) {
@@ -463,18 +444,13 @@ static Optional parse_a_time_component(GenericLexer& input)
return {};
// Otherwise, interpret the resulting sequence as a base-ten number (possibly with a fractional part). Set second
// to that number.
- // NB: The spec doesn't state requirements for what we must do with the fractional part of the second(s) string.
- // The spec neither requires that we separately preserve it nor requires that we completely discard it. If we
- // did have any reason at all to preserve it, we could parse the string into a float here. But there doesn't
- // seem to be any point in doing that, because there’s nothing in the corresponding calling algorithm(s) in the
- // spec that takes a milliseconds field and does anything with it anyway.
- auto maybe_second = second_string.to_number();
+ auto maybe_second = second_string.to_number();
if (!maybe_second.has_value())
return {};
second = maybe_second.value();
// 4. If second is not a number in the range 0 ≤ second < 60, then fail.
- if (second < 0 || second > 60)
+ if (second < 0 || second >= 60)
return {};
}
@@ -482,6 +458,27 @@ static Optional parse_a_time_component(GenericLexer& input)
return HourMinuteSecond { hour, minute, second };
}
+// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-time-string
+WebIDL::ExceptionOr> parse_time_string(JS::Realm& realm, StringView value)
+{
+ // 1. Let input be the string being parsed.
+ // 2. Let position be a pointer into input, initially pointing at the start of the string.
+ GenericLexer input { value };
+
+ // 3. Parse a time component to obtain hour, minute, and second. If this returns nothing, then fail.
+ auto hour_minute_second = parse_a_time_component(input);
+ if (!hour_minute_second.has_value())
+ return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Can't parse time string"sv };
+
+ // 4. If position is not beyond the end of input, then fail.
+ if (!input.is_eof())
+ return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Can't parse time string"sv };
+
+ // 5. Let time be the time with hour hour, minute minute, and second second.
+ // 6. Return time.
+ return JS::Date::create(realm, JS::make_time(hour_minute_second->hour, hour_minute_second->minute, hour_minute_second->second, static_cast(hour_minute_second->second * 1000) % 1000));
+}
+
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-local-date-and-time-string
Optional parse_a_local_date_and_time_string(StringView input_view)
{
diff --git a/Libraries/LibWeb/HTML/Dates.h b/Libraries/LibWeb/HTML/Dates.h
index a3186299452..05bbcebfca9 100644
--- a/Libraries/LibWeb/HTML/Dates.h
+++ b/Libraries/LibWeb/HTML/Dates.h
@@ -43,7 +43,7 @@ struct YearMonthDay {
struct HourMinuteSecond {
i32 hour;
i32 minute;
- i32 second;
+ f32 second;
};
struct DateAndTime {
diff --git a/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Libraries/LibWeb/HTML/HTMLInputElement.cpp
index 199a0ea76db..6cb987e4270 100644
--- a/Libraries/LibWeb/HTML/HTMLInputElement.cpp
+++ b/Libraries/LibWeb/HTML/HTMLInputElement.cpp
@@ -2209,7 +2209,7 @@ static Optional convert_local_date_and_time_string_to_number(StringView
auto date = date_and_time.date;
auto time = date_and_time.time;
- auto date_time = UnixDateTime::from_unix_time_parts(date.year, date.month, date.day, time.hour, time.minute, time.second, 0);
+ auto date_time = UnixDateTime::from_unix_time_parts(date.year, date.month, date.day, time.hour, time.minute, time.second, static_cast(time.second * 1000) % 1000);
return date_time.milliseconds_since_epoch();
}
diff --git a/Tests/LibWeb/Text/expected/HTML/HTMLInputElement-invalid-time-digits.txt b/Tests/LibWeb/Text/expected/HTML/HTMLInputElement-invalid-time-digits.txt
index aaecaf93c4a..bf93285b1bb 100644
--- a/Tests/LibWeb/Text/expected/HTML/HTMLInputElement-invalid-time-digits.txt
+++ b/Tests/LibWeb/Text/expected/HTML/HTMLInputElement-invalid-time-digits.txt
@@ -1 +1,6 @@
+Parsed invalid as NaN
+Parsed xx:34:56 as NaN
+Parsed 12:xx:56 as NaN
+Parsed 12:34:xx as NaN
+Parsed 12:34:60 as NaN
PASS (didn't crash)
diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeOverflow.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeOverflow.txt
index 897e3e2ad25..04ffd2307f9 100644
--- a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeOverflow.txt
+++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeOverflow.txt
@@ -2,8 +2,7 @@ Harness status: OK
Found 49 tests
-43 Pass
-6 Fail
+49 Pass
Pass [INPUT in DATETIME-LOCAL status] The max attribute is not set
Pass [INPUT in DATETIME-LOCAL status] Value is empty string
Pass [INPUT in DATETIME-LOCAL status] The max attribute is an invalid local date time string
@@ -11,9 +10,9 @@ Pass [INPUT in DATETIME-LOCAL status] The max attribute is greater than the valu
Pass [INPUT in DATETIME-LOCAL status] The value is an invalid local date time string(hour is greater than 23)
Pass [INPUT in DATETIME-LOCAL status] The value if an invalid local date time string(year is two digits)
Pass [INPUT in DATETIME-LOCAL status] The value is greater than max
-Fail [INPUT in DATETIME-LOCAL status] The value is greater than max(with millisecond in 1 digit)
-Fail [INPUT in DATETIME-LOCAL status] The value is greater than max(with millisecond in 2 digits)
-Fail [INPUT in DATETIME-LOCAL status] The value is greater than max(with millisecond in 3 digits)
+Pass [INPUT in DATETIME-LOCAL status] The value is greater than max(with millisecond in 1 digit)
+Pass [INPUT in DATETIME-LOCAL status] The value is greater than max(with millisecond in 2 digits)
+Pass [INPUT in DATETIME-LOCAL status] The value is greater than max(with millisecond in 3 digits)
Pass [INPUT in DATETIME-LOCAL status] The value is greater than max(Year is 10000 should be valid)
Pass [INPUT in DATE status] The max attribute is not set
Pass [INPUT in DATE status] Value is empty string
@@ -35,9 +34,9 @@ Pass [INPUT in TIME status] The value attribute is an invalid time string(second
Pass [INPUT in TIME status] The max attribute is greater than value attribute
Pass [INPUT in TIME status] The time missing second and minute parts is invalid
Pass [INPUT in TIME status] The value attribute is greater than max attribute
-Fail [INPUT in TIME status] The value is greater than max(with millisecond in 1 digit)
-Fail [INPUT in TIME status] The value is greater than max(with millisecond in 2 digit)
-Fail [INPUT in TIME status] The value is greater than max(with millisecond in 3 digit)
+Pass [INPUT in TIME status] The value is greater than max(with millisecond in 1 digit)
+Pass [INPUT in TIME status] The value is greater than max(with millisecond in 2 digit)
+Pass [INPUT in TIME status] The value is greater than max(with millisecond in 3 digit)
Pass [INPUT in TIME status] The time missing second part is valid
Pass [INPUT in TIME status] The time is max for reversed range
Pass [INPUT in TIME status] The time is outside the accepted range for reversed range
diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow.txt
index b75209dfccf..d21e6aa3569 100644
--- a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow.txt
+++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow.txt
@@ -2,8 +2,7 @@ Harness status: OK
Found 47 tests
-41 Pass
-6 Fail
+47 Pass
Pass [INPUT in DATETIME-LOCAL status] The min attribute is not set
Pass [INPUT in DATETIME-LOCAL status] Value is empty string
Pass [INPUT in DATETIME-LOCAL status] The min attribute is an invalid local date time string
@@ -11,9 +10,9 @@ Pass [INPUT in DATETIME-LOCAL status] The min attribute is less than the value a
Pass [INPUT in DATETIME-LOCAL status] The value is an invalid local date time string(hour is greater than 23)
Pass [INPUT in DATETIME-LOCAL status] The value is an invalid local date time string(year is two digits)
Pass [INPUT in DATETIME-LOCAL status] The value is less than min
-Fail [INPUT in DATETIME-LOCAL status] The value is less than min(with millisecond in 1 digit)
-Fail [INPUT in DATETIME-LOCAL status] The value is less than min(with millisecond in 2 digits)
-Fail [INPUT in DATETIME-LOCAL status] The value is less than min(with millisecond in 3 digits)
+Pass [INPUT in DATETIME-LOCAL status] The value is less than min(with millisecond in 1 digit)
+Pass [INPUT in DATETIME-LOCAL status] The value is less than min(with millisecond in 2 digits)
+Pass [INPUT in DATETIME-LOCAL status] The value is less than min(with millisecond in 3 digits)
Pass [INPUT in DATETIME-LOCAL status] The value is less than min(Year is 10000 should be valid)
Pass [INPUT in DATETIME-LOCAL status] The value is greater than max
Pass [INPUT in DATE status] The min attribute is not set
@@ -33,9 +32,9 @@ Pass [INPUT in TIME status] The value attribute is an invalid time string
Pass [INPUT in TIME status] The min attribute is less than value attribute
Pass [INPUT in TIME status] The time missing second and minute parts is invalid
Pass [INPUT in TIME status] The value attribute is less than min attribute
-Fail [INPUT in TIME status] The value is less than min(with millisecond in 1 digit)
-Fail [INPUT in TIME status] The value is less than min(with millisecond in 2 digit)
-Fail [INPUT in TIME status] The value is less than min(with millisecond in 3 digit)
+Pass [INPUT in TIME status] The value is less than min(with millisecond in 1 digit)
+Pass [INPUT in TIME status] The value is less than min(with millisecond in 2 digit)
+Pass [INPUT in TIME status] The value is less than min(with millisecond in 3 digit)
Pass [INPUT in TIME status] The time missing second part is valid
Pass [INPUT in TIME status] The time is max for reversed range
Pass [INPUT in TIME status] The time is outside the accepted range for reversed range
diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-input-element/input-seconds-leading-zeroes.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-input-element/input-seconds-leading-zeroes.txt
new file mode 100644
index 00000000000..f13b515438a
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-input-element/input-seconds-leading-zeroes.txt
@@ -0,0 +1,17 @@
+Harness status: OK
+
+Found 12 tests
+
+12 Pass
+Pass Expected valueAsNumber=946729801000 from 2000-01-01T12:30:01
+Pass Expected digits unchanged in round-trip of 2000-01-01T12:30:01
+Pass Expected valueAsNumber=946729800500 from 2000-01-01T12:30:00.5
+Pass Expected digits unchanged in round-trip of 2000-01-01T12:30:00.5
+Pass Expected valueAsNumber=946729800040 from 2000-01-01T12:30:00.04
+Pass Expected digits unchanged in round-trip of 2000-01-01T12:30:00.04
+Pass Expected valueAsNumber=946729800003 from 2000-01-01T12:30:00.003
+Pass Expected digits unchanged in round-trip of 2000-01-01T12:30:00.003
+Pass Expected valueAsNumber=45001000 from 12:30:01
+Pass Expected valueAsNumber=45000500 from 12:30:00.5
+Pass Expected valueAsNumber=45000040 from 12:30:00.04
+Pass Expected valueAsNumber=45000003 from 12:30:00.003
\ No newline at end of file
diff --git a/Tests/LibWeb/Text/input/HTML/HTMLInputElement-invalid-time-digits.html b/Tests/LibWeb/Text/input/HTML/HTMLInputElement-invalid-time-digits.html
index f4e7cf19cc8..4160f0e378e 100644
--- a/Tests/LibWeb/Text/input/HTML/HTMLInputElement-invalid-time-digits.html
+++ b/Tests/LibWeb/Text/input/HTML/HTMLInputElement-invalid-time-digits.html
@@ -4,10 +4,10 @@
test(() => {
const inputElement = document.createElement("input");
inputElement.type = "time";
- inputElement.value = "invalid";
- inputElement.value = "xx:34:56";
- inputElement.value = "12:xx:56";
- inputElement.value = "12:34:xx";
+ ["invalid", "xx:34:56", "12:xx:56", "12:34:xx", "12:34:60"].forEach(value => {
+ inputElement.value = value;
+ println(`Parsed ${value} as ${inputElement.valueAsNumber}`);
+ });
println("PASS (didn't crash)");
});
diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/the-input-element/input-seconds-leading-zeroes.html b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/the-input-element/input-seconds-leading-zeroes.html
new file mode 100644
index 00000000000..08beb222f78
--- /dev/null
+++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/the-input-element/input-seconds-leading-zeroes.html
@@ -0,0 +1,52 @@
+
+
+
+
+ HTMLInputElement leading zeroes in seconds/millis
+
+
+
+
+
input times and datetimes with leading zeroes in seconds/millis