1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-08 22:37:35 +00:00

LibJS: Convert remaining Date AOs using JS::Value as in/output to double

There was an awful lot of JS::Value <-> double conversion going on, even
through these AOs only work with number values anyway.
They don't need a global object either as they won't allocate or throw,
that was simply to pass it to infallible calls of ToIntegerOrInfinity.
This commit is contained in:
Linus Groh 2022-05-06 20:45:25 +02:00
parent b9b3d01bea
commit f7c9bd0760
10 changed files with 357 additions and 354 deletions

View file

@ -300,25 +300,25 @@ double utc_time(double time)
} }
// 21.4.1.11 MakeTime ( hour, min, sec, ms ), https://tc39.es/ecma262/#sec-maketime // 21.4.1.11 MakeTime ( hour, min, sec, ms ), https://tc39.es/ecma262/#sec-maketime
Value make_time(GlobalObject& global_object, Value hour, Value min, Value sec, Value ms) double make_time(double hour, double min, double sec, double ms)
{ {
// 1. If hour is not finite or min is not finite or sec is not finite or ms is not finite, return NaN. // 1. If hour is not finite or min is not finite or sec is not finite or ms is not finite, return NaN.
if (!hour.is_finite_number() || !min.is_finite_number() || !sec.is_finite_number() || !ms.is_finite_number()) if (!isfinite(hour) || !isfinite(min) || !isfinite(sec) || !isfinite(ms))
return js_nan(); return NAN;
// 2. Let h be 𝔽(! ToIntegerOrInfinity(hour)). // 2. Let h be 𝔽(! ToIntegerOrInfinity(hour)).
auto h = MUST(hour.to_integer_or_infinity(global_object)); auto h = to_integer_or_infinity(hour);
// 3. Let m be 𝔽(! ToIntegerOrInfinity(min)). // 3. Let m be 𝔽(! ToIntegerOrInfinity(min)).
auto m = MUST(min.to_integer_or_infinity(global_object)); auto m = to_integer_or_infinity(min);
// 4. Let s be 𝔽(! ToIntegerOrInfinity(sec)). // 4. Let s be 𝔽(! ToIntegerOrInfinity(sec)).
auto s = MUST(sec.to_integer_or_infinity(global_object)); auto s = to_integer_or_infinity(sec);
// 5. Let milli be 𝔽(! ToIntegerOrInfinity(ms)). // 5. Let milli be 𝔽(! ToIntegerOrInfinity(ms)).
auto milli = MUST(ms.to_integer_or_infinity(global_object)); auto milli = to_integer_or_infinity(ms);
// 6. Let t be ((h * msPerHour + m * msPerMinute) + s * msPerSecond) + milli, performing the arithmetic according to IEEE 754-2019 rules (that is, as if using the ECMAScript operators * and +). // 6. Let t be ((h * msPerHour + m * msPerMinute) + s * msPerSecond) + milli, performing the arithmetic according to IEEE 754-2019 rules (that is, as if using the ECMAScript operators * and +).
// NOTE: C++ arithmetic abides by IEEE 754 rules // NOTE: C++ arithmetic abides by IEEE 754 rules
auto t = ((h * ms_per_hour + m * ms_per_minute) + s * ms_per_second) + milli; auto t = ((h * ms_per_hour + m * ms_per_minute) + s * ms_per_second) + milli;
// 7. Return t. // 7. Return t.
return Value(t); return t;
} }
// Day(t), https://tc39.es/ecma262/#eqn-Day // Day(t), https://tc39.es/ecma262/#eqn-Day
@ -335,69 +335,69 @@ double time_within_day(double time)
} }
// 21.4.1.12 MakeDay ( year, month, date ), https://tc39.es/ecma262/#sec-makeday // 21.4.1.12 MakeDay ( year, month, date ), https://tc39.es/ecma262/#sec-makeday
Value make_day(GlobalObject& global_object, Value year, Value month, Value date) double make_day(double year, double month, double date)
{ {
// 1. If year is not finite or month is not finite or date is not finite, return NaN. // 1. If year is not finite or month is not finite or date is not finite, return NaN.
if (!year.is_finite_number() || !month.is_finite_number() || !date.is_finite_number()) if (!isfinite(year) || !isfinite(month) || !isfinite(date))
return js_nan(); return NAN;
// 2. Let y be 𝔽(! ToIntegerOrInfinity(year)). // 2. Let y be 𝔽(! ToIntegerOrInfinity(year)).
auto y = MUST(year.to_integer_or_infinity(global_object)); auto y = to_integer_or_infinity(year);
// 3. Let m be 𝔽(! ToIntegerOrInfinity(month)). // 3. Let m be 𝔽(! ToIntegerOrInfinity(month)).
auto m = MUST(month.to_integer_or_infinity(global_object)); auto m = to_integer_or_infinity(month);
// 4. Let dt be 𝔽(! ToIntegerOrInfinity(date)). // 4. Let dt be 𝔽(! ToIntegerOrInfinity(date)).
auto dt = MUST(date.to_integer_or_infinity(global_object)); auto dt = to_integer_or_infinity(date);
// 5. Let ym be y + 𝔽(floor((m) / 12)). // 5. Let ym be y + 𝔽(floor((m) / 12)).
auto ym = y + floor(m / 12); auto ym = y + floor(m / 12);
// 6. If ym is not finite, return NaN. // 6. If ym is not finite, return NaN.
if (!Value(ym).is_finite_number()) if (!isfinite(ym))
return js_nan(); return NAN;
// 7. Let mn be 𝔽((m) modulo 12). // 7. Let mn be 𝔽((m) modulo 12).
auto mn = modulo(m, 12); auto mn = modulo(m, 12);
// 8. Find a finite time value t such that YearFromTime(t) is ym and MonthFromTime(t) is mn and DateFromTime(t) is 1𝔽; but if this is not possible (because some argument is out of range), return NaN. // 8. Find a finite time value t such that YearFromTime(t) is ym and MonthFromTime(t) is mn and DateFromTime(t) is 1𝔽; but if this is not possible (because some argument is out of range), return NaN.
if (!AK::is_within_range<int>(ym) || !AK::is_within_range<int>(mn + 1)) if (!AK::is_within_range<int>(ym) || !AK::is_within_range<int>(mn + 1))
return js_nan(); return NAN;
// FIXME: We are avoiding AK::years_to_days_since_epoch here because it is implemented by looping over // FIXME: We are avoiding AK::years_to_days_since_epoch here because it is implemented by looping over
// the range [1970, ym), which will spin for any time value with an extremely large year. // the range [1970, ym), which will spin for any time value with an extremely large year.
auto t = time_from_year(ym) + (day_of_year(static_cast<int>(ym), static_cast<int>(mn) + 1, 1) * ms_per_day); auto t = time_from_year(ym) + (day_of_year(static_cast<int>(ym), static_cast<int>(mn) + 1, 1) * ms_per_day);
// 9. Return Day(t) + dt - 1𝔽. // 9. Return Day(t) + dt - 1𝔽.
return Value(day(static_cast<double>(t)) + dt - 1); return day(static_cast<double>(t)) + dt - 1;
} }
// 21.4.1.13 MakeDate ( day, time ), https://tc39.es/ecma262/#sec-makedate // 21.4.1.13 MakeDate ( day, time ), https://tc39.es/ecma262/#sec-makedate
Value make_date(Value day, Value time) double make_date(double day, double time)
{ {
// 1. If day is not finite or time is not finite, return NaN. // 1. If day is not finite or time is not finite, return NaN.
if (!day.is_finite_number() || !time.is_finite_number()) if (!isfinite(day) || !isfinite(time))
return js_nan(); return NAN;
// 2. Let tv be day × msPerDay + time. // 2. Let tv be day × msPerDay + time.
auto tv = Value(day.as_double() * ms_per_day + time.as_double()); auto tv = day * ms_per_day + time;
// 3. If tv is not finite, return NaN. // 3. If tv is not finite, return NaN.
if (!tv.is_finite_number()) if (!isfinite(tv))
return js_nan(); return NAN;
// 4. Return tv. // 4. Return tv.
return tv; return tv;
} }
// 21.4.1.14 TimeClip ( time ), https://tc39.es/ecma262/#sec-timeclip // 21.4.1.14 TimeClip ( time ), https://tc39.es/ecma262/#sec-timeclip
Value time_clip(GlobalObject& global_object, Value time) double time_clip(double time)
{ {
// 1. If time is not finite, return NaN. // 1. If time is not finite, return NaN.
if (!time.is_finite_number()) if (!isfinite(time))
return js_nan(); return NAN;
// 2. If abs((time)) > 8.64 × 10^15, return NaN. // 2. If abs((time)) > 8.64 × 10^15, return NaN.
if (fabs(time.as_double()) > 8.64E15) if (fabs(time) > 8.64E15)
return js_nan(); return NAN;
// 3. Return 𝔽(! ToIntegerOrInfinity(time)). // 3. Return 𝔽(! ToIntegerOrInfinity(time)).
return Value(MUST(time.to_integer_or_infinity(global_object))); return to_integer_or_infinity(time);
} }
} }

View file

@ -63,9 +63,9 @@ double local_time(double time);
double utc_time(double time); double utc_time(double time);
double day(double); double day(double);
double time_within_day(double); double time_within_day(double);
Value make_time(GlobalObject& global_object, Value hour, Value min, Value sec, Value ms); double make_time(double hour, double min, double sec, double ms);
Value make_day(GlobalObject& global_object, Value year, Value month, Value date); double make_day(double year, double month, double date);
Value make_date(Value day, Value time); double make_date(double day, double time);
Value time_clip(GlobalObject& global_object, Value time); double time_clip(double time);
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020, Linus Groh <linusg@serenityos.org> * Copyright (c) 2020-2022, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2020, Nico Weber <thakis@chromium.org> * Copyright (c) 2020, Nico Weber <thakis@chromium.org>
* Copyright (c) 2021, Petróczi Zoltán <petroczizoltan@tutanota.com> * Copyright (c) 2021, Petróczi Zoltán <petroczizoltan@tutanota.com>
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org> * Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
@ -23,7 +23,7 @@
namespace JS { namespace JS {
// 21.4.3.2 Date.parse ( string ), https://tc39.es/ecma262/#sec-date.parse // 21.4.3.2 Date.parse ( string ), https://tc39.es/ecma262/#sec-date.parse
static Value parse_simplified_iso8601(GlobalObject& global_object, String const& iso_8601) static double parse_simplified_iso8601(String const& iso_8601)
{ {
// 21.4.1.15 Date Time String Format, https://tc39.es/ecma262/#sec-date-time-string-format // 21.4.1.15 Date Time String Format, https://tc39.es/ecma262/#sec-date-time-string-format
GenericLexer lexer(iso_8601); GenericLexer lexer(iso_8601);
@ -126,7 +126,7 @@ static Value parse_simplified_iso8601(GlobalObject& global_object, String const&
auto lex_time = [&]() { return lex_hours_minutes(hours, minutes) && (!lexer.consume_specific(':') || lex_seconds_milliseconds()) && lex_timezone(); }; auto lex_time = [&]() { return lex_hours_minutes(hours, minutes) && (!lexer.consume_specific(':') || lex_seconds_milliseconds()) && lex_timezone(); };
if (!lex_date() || (lexer.consume_specific('T') && !lex_time()) || !lexer.is_eof()) { if (!lex_date() || (lexer.consume_specific('T') && !lex_time()) || !lexer.is_eof()) {
return js_nan(); return NAN;
} }
// We parsed a valid date simplified ISO 8601 string. // We parsed a valid date simplified ISO 8601 string.
@ -144,22 +144,22 @@ static Value parse_simplified_iso8601(GlobalObject& global_object, String const&
else if (timezone == '+') else if (timezone == '+')
time_ms -= *timezone_hours * 3'600'000 + *timezone_minutes * 60'000; time_ms -= *timezone_hours * 3'600'000 + *timezone_minutes * 60'000;
return time_clip(global_object, Value(time_ms)); return time_clip(time_ms);
} }
static Value parse_date_string(GlobalObject& global_object, String const& date_string) static double parse_date_string(String const& date_string)
{ {
auto value = parse_simplified_iso8601(global_object, date_string); auto value = parse_simplified_iso8601(date_string);
if (value.is_finite_number()) if (isfinite(value))
return value; return value;
// Date.parse() is allowed to accept an arbitrary number of implementation-defined formats. // Date.parse() is allowed to accept an arbitrary number of implementation-defined formats.
// Parse formats of this type: "Wed Apr 17 23:08:53 +0000 2019" // Parse formats of this type: "Wed Apr 17 23:08:53 +0000 2019"
auto maybe_datetime = Core::DateTime::parse("%a %b %e %T %z %Y", date_string); auto maybe_datetime = Core::DateTime::parse("%a %b %e %T %z %Y", date_string);
if (maybe_datetime.has_value()) if (maybe_datetime.has_value())
return Value(1000.0 * maybe_datetime.value().timestamp()); return 1000.0 * maybe_datetime->timestamp();
return js_nan(); return NAN;
} }
DateConstructor::DateConstructor(GlobalObject& global_object) DateConstructor::DateConstructor(GlobalObject& global_object)
@ -200,20 +200,20 @@ ThrowCompletionOr<Object*> DateConstructor::construct(FunctionObject& new_target
auto& vm = this->vm(); auto& vm = this->vm();
auto& global_object = this->global_object(); auto& global_object = this->global_object();
Value date_value; double date_value;
// 2. Let numberOfArgs be the number of elements in values. // 2. Let numberOfArgs be the number of elements in values.
// 3. If numberOfArgs = 0, then // 3. If numberOfArgs = 0, then
if (vm.argument_count() == 0) { if (vm.argument_count() == 0) {
// a. Let dv be the time value (UTC) identifying the current time. // a. Let dv be the time value (UTC) identifying the current time.
auto now = AK::Time::now_realtime().to_milliseconds(); auto now = AK::Time::now_realtime().to_milliseconds();
date_value = Value(static_cast<double>(now)); date_value = static_cast<double>(now);
} }
// 4. Else if numberOfArgs = 1, then // 4. Else if numberOfArgs = 1, then
else if (vm.argument_count() == 1) { else if (vm.argument_count() == 1) {
// a. Let value be values[0]. // a. Let value be values[0].
auto value = vm.argument(0); auto value = vm.argument(0);
Value time_value; double time_value;
// b. If Type(value) is Object and value has a [[DateValue]] internal slot, then // b. If Type(value) is Object and value has a [[DateValue]] internal slot, then
if (value.is_object() && is<Date>(value.as_object())) { if (value.is_object() && is<Date>(value.as_object())) {
@ -229,28 +229,28 @@ ThrowCompletionOr<Object*> DateConstructor::construct(FunctionObject& new_target
if (primitive.is_string()) { if (primitive.is_string()) {
// 1. Assert: The next step never returns an abrupt completion because Type(v) is String. // 1. Assert: The next step never returns an abrupt completion because Type(v) is String.
// 2. Let tv be the result of parsing v as a date, in exactly the same manner as for the parse method (21.4.3.2). // 2. Let tv be the result of parsing v as a date, in exactly the same manner as for the parse method (21.4.3.2).
time_value = parse_date_string(global_object, primitive.as_string().string()); time_value = parse_date_string(primitive.as_string().string());
} }
// iii. Else, // iii. Else,
else { else {
// 1. Let tv be ? ToNumber(v). // 1. Let tv be ? ToNumber(v).
time_value = TRY(primitive.to_number(global_object)); time_value = TRY(primitive.to_number(global_object)).as_double();
} }
} }
// d. Let dv be TimeClip(tv). // d. Let dv be TimeClip(tv).
date_value = time_clip(global_object, time_value); date_value = time_clip(time_value);
} }
// 5. Else, // 5. Else,
else { else {
// a. Assert: numberOfArgs ≥ 2. // a. Assert: numberOfArgs ≥ 2.
// b. Let y be ? ToNumber(values[0]). // b. Let y be ? ToNumber(values[0]).
auto year = TRY(vm.argument(0).to_number(global_object)); auto year = TRY(vm.argument(0).to_number(global_object)).as_double();
// c. Let m be ? ToNumber(values[1]). // c. Let m be ? ToNumber(values[1]).
auto month = TRY(vm.argument(1).to_number(global_object)); auto month = TRY(vm.argument(1).to_number(global_object)).as_double();
auto arg_or = [&vm, &global_object](size_t i, i32 fallback) -> ThrowCompletionOr<Value> { auto arg_or = [&vm, &global_object](size_t i, double fallback) -> ThrowCompletionOr<double> {
return vm.argument_count() > i ? vm.argument(i).to_number(global_object) : Value(fallback); return vm.argument_count() > i ? TRY(vm.argument(i).to_number(global_object)).as_double() : fallback;
}; };
// d. If numberOfArgs > 2, let dt be ? ToNumber(values[2]); else let dt be 1𝔽. // d. If numberOfArgs > 2, let dt be ? ToNumber(values[2]); else let dt be 1𝔽.
@ -266,28 +266,28 @@ ThrowCompletionOr<Object*> DateConstructor::construct(FunctionObject& new_target
// i. If y is NaN, let yr be NaN. // i. If y is NaN, let yr be NaN.
// j. Else, // j. Else,
if (!year.is_nan()) { if (!isnan(year)) {
// i. Let yi be ! ToIntegerOrInfinity(y). // i. Let yi be ! ToIntegerOrInfinity(y).
auto year_double = MUST(year.to_integer_or_infinity(global_object)); auto year_integer = to_integer_or_infinity(year);
// ii. If 0 ≤ yi ≤ 99, let yr be 1900𝔽 + 𝔽(yi); otherwise, let yr be y. // ii. If 0 ≤ yi ≤ 99, let yr be 1900𝔽 + 𝔽(yi); otherwise, let yr be y.
if (0 <= year_double && year_double <= 99) if (0 <= year_integer && year_integer <= 99)
year = Value(1900 + year_double); year = 1900 + year_integer;
} }
// k. Let finalDate be MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli)). // k. Let finalDate be MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli)).
auto day = make_day(global_object, year, month, date); auto day = make_day(year, month, date);
auto time = make_time(global_object, hours, minutes, seconds, milliseconds); auto time = make_time(hours, minutes, seconds, milliseconds);
auto final_date = make_date(day, time); auto final_date = make_date(day, time);
// l. Let dv be TimeClip(UTC(finalDate)). // l. Let dv be TimeClip(UTC(finalDate)).
date_value = time_clip(global_object, Value(utc_time(final_date.as_double()))); date_value = time_clip(utc_time(final_date));
} }
// 6. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%Date.prototype%", « [[DateValue]] »). // 6. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%Date.prototype%", « [[DateValue]] »).
// 7. Set O.[[DateValue]] to dv. // 7. Set O.[[DateValue]] to dv.
// 8. Return O. // 8. Return O.
return TRY(ordinary_create_from_constructor<Date>(global_object, new_target, &GlobalObject::date_prototype, date_value.as_double())); return TRY(ordinary_create_from_constructor<Date>(global_object, new_target, &GlobalObject::date_prototype, date_value));
} }
// 21.4.3.1 Date.now ( ), https://tc39.es/ecma262/#sec-date.now // 21.4.3.1 Date.now ( ), https://tc39.es/ecma262/#sec-date.now
@ -306,18 +306,18 @@ JS_DEFINE_NATIVE_FUNCTION(DateConstructor::parse)
auto date_string = TRY(vm.argument(0).to_string(global_object)); auto date_string = TRY(vm.argument(0).to_string(global_object));
return parse_date_string(global_object, date_string); return Value(parse_date_string(date_string));
} }
// 21.4.3.4 Date.UTC ( year [ , month [ , date [ , hours [ , minutes [ , seconds [ , ms ] ] ] ] ] ] ), https://tc39.es/ecma262/#sec-date.utc // 21.4.3.4 Date.UTC ( year [ , month [ , date [ , hours [ , minutes [ , seconds [ , ms ] ] ] ] ] ] ), https://tc39.es/ecma262/#sec-date.utc
JS_DEFINE_NATIVE_FUNCTION(DateConstructor::utc) JS_DEFINE_NATIVE_FUNCTION(DateConstructor::utc)
{ {
auto arg_or = [&vm, &global_object](size_t i, i32 fallback) -> ThrowCompletionOr<Value> { auto arg_or = [&vm, &global_object](size_t i, double fallback) -> ThrowCompletionOr<double> {
return vm.argument_count() > i ? vm.argument(i).to_number(global_object) : Value(fallback); return vm.argument_count() > i ? TRY(vm.argument(i).to_number(global_object)).as_double() : fallback;
}; };
// 1. Let y be ? ToNumber(year). // 1. Let y be ? ToNumber(year).
auto year = TRY(vm.argument(0).to_number(global_object)); auto year = TRY(vm.argument(0).to_number(global_object)).as_double();
// 2. If month is present, let m be ? ToNumber(month); else let m be +0𝔽. // 2. If month is present, let m be ? ToNumber(month); else let m be +0𝔽.
auto month = TRY(arg_or(1, 0)); auto month = TRY(arg_or(1, 0));
// 3. If date is present, let dt be ? ToNumber(date); else let dt be 1𝔽. // 3. If date is present, let dt be ? ToNumber(date); else let dt be 1𝔽.
@ -333,19 +333,19 @@ JS_DEFINE_NATIVE_FUNCTION(DateConstructor::utc)
// 8. If y is NaN, let yr be NaN. // 8. If y is NaN, let yr be NaN.
// 9. Else, // 9. Else,
if (!year.is_nan()) { if (!isnan(year)) {
// a. Let yi be ! ToIntegerOrInfinity(y). // a. Let yi be ! ToIntegerOrInfinity(y).
auto year_double = MUST(year.to_integer_or_infinity(global_object)); auto year_integer = to_integer_or_infinity(year);
// b. If 0 ≤ yi ≤ 99, let yr be 1900𝔽 + 𝔽(yi); otherwise, let yr be y. // b. If 0 ≤ yi ≤ 99, let yr be 1900𝔽 + 𝔽(yi); otherwise, let yr be y.
if (0 <= year_double && year_double <= 99) if (0 <= year_integer && year_integer <= 99)
year = Value(1900 + year_double); year = 1900 + year_integer;
} }
// 10. Return TimeClip(MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli))). // 10. Return TimeClip(MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli))).
auto day = make_day(global_object, year, month, date); auto day = make_day(year, month, date);
auto time = make_time(global_object, hours, minutes, seconds, milliseconds); auto time = make_time(hours, minutes, seconds, milliseconds);
return time_clip(global_object, make_date(day, time)); return Value(time_clip(make_date(day, time)));
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020, Linus Groh <linusg@serenityos.org> * Copyright (c) 2020-2022, Linus Groh <linusg@serenityos.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -71,7 +71,7 @@ private:
JS_DECLARE_NATIVE_FUNCTION(symbol_to_primitive); JS_DECLARE_NATIVE_FUNCTION(symbol_to_primitive);
}; };
ThrowCompletionOr<Value> this_time_value(GlobalObject& global_object, Value value); ThrowCompletionOr<double> this_time_value(GlobalObject& global_object, Value value);
String time_string(double time); String time_string(double time);
String date_string(double time); String date_string(double time);
String time_zone_string(double time); String time_zone_string(double time);

View file

@ -504,15 +504,15 @@ static Optional<StyleAndValue> find_calendar_field(StringView name, Unicode::Cal
} }
// 11.5.6 FormatDateTimePattern ( dateTimeFormat, patternParts, x, rangeFormatOptions ), https://tc39.es/ecma402/#sec-formatdatetimepattern // 11.5.6 FormatDateTimePattern ( dateTimeFormat, patternParts, x, rangeFormatOptions ), https://tc39.es/ecma402/#sec-formatdatetimepattern
ThrowCompletionOr<Vector<PatternPartition>> format_date_time_pattern(GlobalObject& global_object, DateTimeFormat& date_time_format, Vector<PatternPartition> pattern_parts, Value time, Unicode::CalendarPattern const* range_format_options) ThrowCompletionOr<Vector<PatternPartition>> format_date_time_pattern(GlobalObject& global_object, DateTimeFormat& date_time_format, Vector<PatternPartition> pattern_parts, double time, Unicode::CalendarPattern const* range_format_options)
{ {
auto& vm = global_object.vm(); auto& vm = global_object.vm();
// 1. Let x be TimeClip(x). // 1. Let x be TimeClip(x).
time = time_clip(global_object, time); time = time_clip(time);
// 2. If x is NaN, throw a RangeError exception. // 2. If x is NaN, throw a RangeError exception.
if (time.is_nan()) if (isnan(time))
return vm.throw_completion<RangeError>(global_object, ErrorType::IntlInvalidTime); return vm.throw_completion<RangeError>(global_object, ErrorType::IntlInvalidTime);
// 3. Let locale be dateTimeFormat.[[Locale]]. // 3. Let locale be dateTimeFormat.[[Locale]].
@ -567,7 +567,7 @@ ThrowCompletionOr<Vector<PatternPartition>> format_date_time_pattern(GlobalObjec
} }
// 13. Let tm be ToLocalTime(x, dateTimeFormat.[[Calendar]], dateTimeFormat.[[TimeZone]]). // 13. Let tm be ToLocalTime(x, dateTimeFormat.[[Calendar]], dateTimeFormat.[[TimeZone]]).
auto local_time = TRY(to_local_time(global_object, time.as_double(), date_time_format.calendar(), date_time_format.time_zone())); auto local_time = TRY(to_local_time(global_object, time, date_time_format.calendar(), date_time_format.time_zone()));
// 14. Let result be a new empty List. // 14. Let result be a new empty List.
Vector<PatternPartition> result; Vector<PatternPartition> result;
@ -788,7 +788,7 @@ ThrowCompletionOr<Vector<PatternPartition>> format_date_time_pattern(GlobalObjec
} }
// 11.5.7 PartitionDateTimePattern ( dateTimeFormat, x ), https://tc39.es/ecma402/#sec-partitiondatetimepattern // 11.5.7 PartitionDateTimePattern ( dateTimeFormat, x ), https://tc39.es/ecma402/#sec-partitiondatetimepattern
ThrowCompletionOr<Vector<PatternPartition>> partition_date_time_pattern(GlobalObject& global_object, DateTimeFormat& date_time_format, Value time) ThrowCompletionOr<Vector<PatternPartition>> partition_date_time_pattern(GlobalObject& global_object, DateTimeFormat& date_time_format, double time)
{ {
// 1. Let patternParts be PartitionPattern(dateTimeFormat.[[Pattern]]). // 1. Let patternParts be PartitionPattern(dateTimeFormat.[[Pattern]]).
auto pattern_parts = partition_pattern(date_time_format.pattern()); auto pattern_parts = partition_pattern(date_time_format.pattern());
@ -801,7 +801,7 @@ ThrowCompletionOr<Vector<PatternPartition>> partition_date_time_pattern(GlobalOb
} }
// 11.5.8 FormatDateTime ( dateTimeFormat, x ), https://tc39.es/ecma402/#sec-formatdatetime // 11.5.8 FormatDateTime ( dateTimeFormat, x ), https://tc39.es/ecma402/#sec-formatdatetime
ThrowCompletionOr<String> format_date_time(GlobalObject& global_object, DateTimeFormat& date_time_format, Value time) ThrowCompletionOr<String> format_date_time(GlobalObject& global_object, DateTimeFormat& date_time_format, double time)
{ {
// 1. Let parts be ? PartitionDateTimePattern(dateTimeFormat, x). // 1. Let parts be ? PartitionDateTimePattern(dateTimeFormat, x).
auto parts = TRY(partition_date_time_pattern(global_object, date_time_format, time)); auto parts = TRY(partition_date_time_pattern(global_object, date_time_format, time));
@ -820,7 +820,7 @@ ThrowCompletionOr<String> format_date_time(GlobalObject& global_object, DateTime
} }
// 11.5.9 FormatDateTimeToParts ( dateTimeFormat, x ), https://tc39.es/ecma402/#sec-formatdatetimetoparts // 11.5.9 FormatDateTimeToParts ( dateTimeFormat, x ), https://tc39.es/ecma402/#sec-formatdatetimetoparts
ThrowCompletionOr<Array*> format_date_time_to_parts(GlobalObject& global_object, DateTimeFormat& date_time_format, Value time) ThrowCompletionOr<Array*> format_date_time_to_parts(GlobalObject& global_object, DateTimeFormat& date_time_format, double time)
{ {
auto& vm = global_object.vm(); auto& vm = global_object.vm();
@ -891,33 +891,33 @@ ThrowCompletionOr<void> for_each_range_pattern_with_source(Unicode::CalendarRang
} }
// 11.5.10 PartitionDateTimeRangePattern ( dateTimeFormat, x, y ), https://tc39.es/ecma402/#sec-partitiondatetimerangepattern // 11.5.10 PartitionDateTimeRangePattern ( dateTimeFormat, x, y ), https://tc39.es/ecma402/#sec-partitiondatetimerangepattern
ThrowCompletionOr<Vector<PatternPartitionWithSource>> partition_date_time_range_pattern(GlobalObject& global_object, DateTimeFormat& date_time_format, Value start, Value end) ThrowCompletionOr<Vector<PatternPartitionWithSource>> partition_date_time_range_pattern(GlobalObject& global_object, DateTimeFormat& date_time_format, double start, double end)
{ {
auto& vm = global_object.vm(); auto& vm = global_object.vm();
// 1. Let x be TimeClip(x). // 1. Let x be TimeClip(x).
start = time_clip(global_object, start); start = time_clip(start);
// 2. If x is NaN, throw a RangeError exception. // 2. If x is NaN, throw a RangeError exception.
if (start.is_nan()) if (isnan(start))
return vm.throw_completion<RangeError>(global_object, ErrorType::IntlInvalidTime); return vm.throw_completion<RangeError>(global_object, ErrorType::IntlInvalidTime);
// 3. Let y be TimeClip(y). // 3. Let y be TimeClip(y).
end = time_clip(global_object, end); end = time_clip(end);
// 4. If y is NaN, throw a RangeError exception. // 4. If y is NaN, throw a RangeError exception.
if (end.is_nan()) if (isnan(end))
return vm.throw_completion<RangeError>(global_object, ErrorType::IntlInvalidTime); return vm.throw_completion<RangeError>(global_object, ErrorType::IntlInvalidTime);
// 5. If x is greater than y, throw a RangeError exception. // 5. If x is greater than y, throw a RangeError exception.
if (start.as_double() > end.as_double()) if (start > end)
return vm.throw_completion<RangeError>(global_object, ErrorType::IntlStartTimeAfterEndTime, start, end); return vm.throw_completion<RangeError>(global_object, ErrorType::IntlStartTimeAfterEndTime, start, end);
// 6. Let tm1 be ToLocalTime(x, dateTimeFormat.[[Calendar]], dateTimeFormat.[[TimeZone]]). // 6. Let tm1 be ToLocalTime(x, dateTimeFormat.[[Calendar]], dateTimeFormat.[[TimeZone]]).
auto start_local_time = TRY(to_local_time(global_object, start.as_double(), date_time_format.calendar(), date_time_format.time_zone())); auto start_local_time = TRY(to_local_time(global_object, start, date_time_format.calendar(), date_time_format.time_zone()));
// 7. Let tm2 be ToLocalTime(y, dateTimeFormat.[[Calendar]], dateTimeFormat.[[TimeZone]]). // 7. Let tm2 be ToLocalTime(y, dateTimeFormat.[[Calendar]], dateTimeFormat.[[TimeZone]]).
auto end_local_time = TRY(to_local_time(global_object, end.as_double(), date_time_format.calendar(), date_time_format.time_zone())); auto end_local_time = TRY(to_local_time(global_object, end, date_time_format.calendar(), date_time_format.time_zone()));
// 8. Let rangePatterns be dateTimeFormat.[[RangePatterns]]. // 8. Let rangePatterns be dateTimeFormat.[[RangePatterns]].
auto range_patterns = date_time_format.range_patterns(); auto range_patterns = date_time_format.range_patterns();
@ -1121,7 +1121,7 @@ ThrowCompletionOr<Vector<PatternPartitionWithSource>> partition_date_time_range_
} }
// 11.5.11 FormatDateTimeRange ( dateTimeFormat, x, y ), https://tc39.es/ecma402/#sec-formatdatetimerange // 11.5.11 FormatDateTimeRange ( dateTimeFormat, x, y ), https://tc39.es/ecma402/#sec-formatdatetimerange
ThrowCompletionOr<String> format_date_time_range(GlobalObject& global_object, DateTimeFormat& date_time_format, Value start, Value end) ThrowCompletionOr<String> format_date_time_range(GlobalObject& global_object, DateTimeFormat& date_time_format, double start, double end)
{ {
// 1. Let parts be ? PartitionDateTimeRangePattern(dateTimeFormat, x, y). // 1. Let parts be ? PartitionDateTimeRangePattern(dateTimeFormat, x, y).
auto parts = TRY(partition_date_time_range_pattern(global_object, date_time_format, start, end)); auto parts = TRY(partition_date_time_range_pattern(global_object, date_time_format, start, end));
@ -1140,7 +1140,7 @@ ThrowCompletionOr<String> format_date_time_range(GlobalObject& global_object, Da
} }
// 11.5.12 FormatDateTimeRangeToParts ( dateTimeFormat, x, y ), https://tc39.es/ecma402/#sec-formatdatetimerangetoparts // 11.5.12 FormatDateTimeRangeToParts ( dateTimeFormat, x, y ), https://tc39.es/ecma402/#sec-formatdatetimerangetoparts
ThrowCompletionOr<Array*> format_date_time_range_to_parts(GlobalObject& global_object, DateTimeFormat& date_time_format, Value start, Value end) ThrowCompletionOr<Array*> format_date_time_range_to_parts(GlobalObject& global_object, DateTimeFormat& date_time_format, double start, double end)
{ {
auto& vm = global_object.vm(); auto& vm = global_object.vm();

View file

@ -203,13 +203,13 @@ ThrowCompletionOr<Object*> to_date_time_options(GlobalObject& global_object, Val
Optional<Unicode::CalendarPattern> date_time_style_format(StringView data_locale, DateTimeFormat& date_time_format); Optional<Unicode::CalendarPattern> date_time_style_format(StringView data_locale, DateTimeFormat& date_time_format);
Optional<Unicode::CalendarPattern> basic_format_matcher(Unicode::CalendarPattern const& options, Vector<Unicode::CalendarPattern> formats); Optional<Unicode::CalendarPattern> basic_format_matcher(Unicode::CalendarPattern const& options, Vector<Unicode::CalendarPattern> formats);
Optional<Unicode::CalendarPattern> best_fit_format_matcher(Unicode::CalendarPattern const& options, Vector<Unicode::CalendarPattern> formats); Optional<Unicode::CalendarPattern> best_fit_format_matcher(Unicode::CalendarPattern const& options, Vector<Unicode::CalendarPattern> formats);
ThrowCompletionOr<Vector<PatternPartition>> format_date_time_pattern(GlobalObject& global_object, DateTimeFormat& date_time_format, Vector<PatternPartition> pattern_parts, Value time, Unicode::CalendarPattern const* range_format_options); ThrowCompletionOr<Vector<PatternPartition>> format_date_time_pattern(GlobalObject& global_object, DateTimeFormat& date_time_format, Vector<PatternPartition> pattern_parts, double time, Unicode::CalendarPattern const* range_format_options);
ThrowCompletionOr<Vector<PatternPartition>> partition_date_time_pattern(GlobalObject& global_object, DateTimeFormat& date_time_format, Value time); ThrowCompletionOr<Vector<PatternPartition>> partition_date_time_pattern(GlobalObject& global_object, DateTimeFormat& date_time_format, double time);
ThrowCompletionOr<String> format_date_time(GlobalObject& global_object, DateTimeFormat& date_time_format, Value time); ThrowCompletionOr<String> format_date_time(GlobalObject& global_object, DateTimeFormat& date_time_format, double time);
ThrowCompletionOr<Array*> format_date_time_to_parts(GlobalObject& global_object, DateTimeFormat& date_time_format, Value time); ThrowCompletionOr<Array*> format_date_time_to_parts(GlobalObject& global_object, DateTimeFormat& date_time_format, double time);
ThrowCompletionOr<Vector<PatternPartitionWithSource>> partition_date_time_range_pattern(GlobalObject& global_object, DateTimeFormat& date_time_format, Value start, Value end); ThrowCompletionOr<Vector<PatternPartitionWithSource>> partition_date_time_range_pattern(GlobalObject& global_object, DateTimeFormat& date_time_format, double start, double end);
ThrowCompletionOr<String> format_date_time_range(GlobalObject& global_object, DateTimeFormat& date_time_format, Value start, Value end); ThrowCompletionOr<String> format_date_time_range(GlobalObject& global_object, DateTimeFormat& date_time_format, double start, double end);
ThrowCompletionOr<Array*> format_date_time_range_to_parts(GlobalObject& global_object, DateTimeFormat& date_time_format, Value start, Value end); ThrowCompletionOr<Array*> format_date_time_range_to_parts(GlobalObject& global_object, DateTimeFormat& date_time_format, double start, double end);
ThrowCompletionOr<LocalTime> to_local_time(GlobalObject& global_object, double time, StringView calendar, StringView time_zone); ThrowCompletionOr<LocalTime> to_local_time(GlobalObject& global_object, double time, StringView calendar, StringView time_zone);
template<typename Callback> template<typename Callback>

View file

@ -44,19 +44,21 @@ ThrowCompletionOr<Value> DateTimeFormatFunction::call()
// 1. Let dtf be F.[[DateTimeFormat]]. // 1. Let dtf be F.[[DateTimeFormat]].
// 2. Assert: Type(dtf) is Object and dtf has an [[InitializedDateTimeFormat]] internal slot. // 2. Assert: Type(dtf) is Object and dtf has an [[InitializedDateTimeFormat]] internal slot.
double date_value;
// 3. If date is not provided or is undefined, then // 3. If date is not provided or is undefined, then
if (date.is_undefined()) { if (date.is_undefined()) {
// a. Let x be ! Call(%Date.now%, undefined). // a. Let x be ! Call(%Date.now%, undefined).
date = MUST(JS::call(global_object, global_object.date_constructor_now_function(), js_undefined())); date_value = MUST(JS::call(global_object, global_object.date_constructor_now_function(), js_undefined())).as_double();
} }
// 4. Else, // 4. Else,
else { else {
// a. Let x be ? ToNumber(date). // a. Let x be ? ToNumber(date).
date = TRY(date.to_number(global_object)); date_value = TRY(date.to_number(global_object)).as_double();
} }
// 5. Return ? FormatDateTime(dtf, x). // 5. Return ? FormatDateTime(dtf, x).
auto formatted = TRY(format_date_time(global_object, m_date_time_format, date)); auto formatted = TRY(format_date_time(global_object, m_date_time_format, date_value));
return js_string(vm, move(formatted)); return js_string(vm, move(formatted));
} }

View file

@ -69,19 +69,21 @@ JS_DEFINE_NATIVE_FUNCTION(DateTimeFormatPrototype::format_to_parts)
// 2. Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]). // 2. Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]).
auto* date_time_format = TRY(typed_this_object(global_object)); auto* date_time_format = TRY(typed_this_object(global_object));
double date_value;
// 3. If date is undefined, then // 3. If date is undefined, then
if (date.is_undefined()) { if (date.is_undefined()) {
// a. Let x be ! Call(%Date.now%, undefined). // a. Let x be ! Call(%Date.now%, undefined).
date = MUST(call(global_object, global_object.date_constructor_now_function(), js_undefined())); date_value = MUST(call(global_object, global_object.date_constructor_now_function(), js_undefined())).as_double();
} }
// 4. Else, // 4. Else,
else { else {
// a. Let x be ? ToNumber(date). // a. Let x be ? ToNumber(date).
date = TRY(date.to_number(global_object)); date_value = TRY(date.to_number(global_object)).as_double();
} }
// 5. Return ? FormatDateTimeToParts(dtf, x). // 5. Return ? FormatDateTimeToParts(dtf, x).
return TRY(format_date_time_to_parts(global_object, *date_time_format, date)); return TRY(format_date_time_to_parts(global_object, *date_time_format, date_value));
} }
// 11.3.5 Intl.DateTimeFormat.prototype.formatRange ( startDate, endDate ), https://tc39.es/ecma402/#sec-intl.datetimeformat.prototype.formatRange // 11.3.5 Intl.DateTimeFormat.prototype.formatRange ( startDate, endDate ), https://tc39.es/ecma402/#sec-intl.datetimeformat.prototype.formatRange
@ -101,13 +103,13 @@ JS_DEFINE_NATIVE_FUNCTION(DateTimeFormatPrototype::format_range)
return vm.throw_completion<TypeError>(global_object, ErrorType::IsUndefined, "endDate"sv); return vm.throw_completion<TypeError>(global_object, ErrorType::IsUndefined, "endDate"sv);
// 4. Let x be ? ToNumber(startDate). // 4. Let x be ? ToNumber(startDate).
start_date = TRY(start_date.to_number(global_object)); auto start_date_number = TRY(start_date.to_number(global_object)).as_double();
// 5. Let y be ? ToNumber(endDate). // 5. Let y be ? ToNumber(endDate).
end_date = TRY(end_date.to_number(global_object)); auto end_date_number = TRY(end_date.to_number(global_object)).as_double();
// 6. Return ? FormatDateTimeRange(dtf, x, y). // 6. Return ? FormatDateTimeRange(dtf, x, y).
auto formatted = TRY(format_date_time_range(global_object, *date_time_format, start_date, end_date)); auto formatted = TRY(format_date_time_range(global_object, *date_time_format, start_date_number, end_date_number));
return js_string(vm, move(formatted)); return js_string(vm, move(formatted));
} }
@ -128,13 +130,13 @@ JS_DEFINE_NATIVE_FUNCTION(DateTimeFormatPrototype::format_range_to_parts)
return vm.throw_completion<TypeError>(global_object, ErrorType::IsUndefined, "endDate"sv); return vm.throw_completion<TypeError>(global_object, ErrorType::IsUndefined, "endDate"sv);
// 4. Let x be ? ToNumber(startDate). // 4. Let x be ? ToNumber(startDate).
start_date = TRY(start_date.to_number(global_object)); auto start_date_number = TRY(start_date.to_number(global_object)).as_double();
// 5. Let y be ? ToNumber(endDate). // 5. Let y be ? ToNumber(endDate).
end_date = TRY(end_date.to_number(global_object)); auto end_date_number = TRY(end_date.to_number(global_object)).as_double();
// 6. Return ? FormatDateTimeRangeToParts(dtf, x, y). // 6. Return ? FormatDateTimeRangeToParts(dtf, x, y).
return TRY(format_date_time_range_to_parts(global_object, *date_time_format, start_date, end_date)); return TRY(format_date_time_range_to_parts(global_object, *date_time_format, start_date_number, end_date_number));
} }
// 11.3.7 Intl.DateTimeFormat.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-intl.datetimeformat.prototype.resolvedoptions // 11.3.7 Intl.DateTimeFormat.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-intl.datetimeformat.prototype.resolvedoptions

View file

@ -53,19 +53,19 @@ BigInt* get_epoch_from_iso_parts(GlobalObject& global_object, i32 year, u8 month
VERIFY(is_valid_iso_date(year, month, day)); VERIFY(is_valid_iso_date(year, month, day));
// 2. Let date be MakeDay(𝔽(year), 𝔽(month - 1), 𝔽(day)). // 2. Let date be MakeDay(𝔽(year), 𝔽(month - 1), 𝔽(day)).
auto date = make_day(global_object, Value(year), Value(month - 1), Value(day)); auto date = make_day(year, month - 1, day);
// 3. Let time be MakeTime(𝔽(hour), 𝔽(minute), 𝔽(second), 𝔽(millisecond)). // 3. Let time be MakeTime(𝔽(hour), 𝔽(minute), 𝔽(second), 𝔽(millisecond)).
auto time = make_time(global_object, Value(hour), Value(minute), Value(second), Value(millisecond)); auto time = make_time(hour, minute, second, millisecond);
// 4. Let ms be MakeDate(date, time). // 4. Let ms be MakeDate(date, time).
auto ms = make_date(date, time); auto ms = make_date(date, time);
// 5. Assert: ms is finite. // 5. Assert: ms is finite.
VERIFY(ms.is_finite_number()); VERIFY(isfinite(ms));
// 6. Return ((ms) × 10^6 + microsecond × 10^3 + nanosecond). // 6. Return ((ms) × 10^6 + microsecond × 10^3 + nanosecond).
return js_bigint(vm, Crypto::SignedBigInteger::create_from(static_cast<i64>(ms.as_double())).multiplied_by(Crypto::UnsignedBigInteger { 1'000'000 }).plus(Crypto::SignedBigInteger::create_from((i64)microsecond * 1000)).plus(Crypto::SignedBigInteger(nanosecond))); return js_bigint(vm, Crypto::SignedBigInteger::create_from(static_cast<i64>(ms)).multiplied_by(Crypto::UnsignedBigInteger { 1'000'000 }).plus(Crypto::SignedBigInteger::create_from((i64)microsecond * 1000)).plus(Crypto::SignedBigInteger(nanosecond)));
} }
// -864 * 10^19 - 864 * 10^11 // -864 * 10^19 - 864 * 10^11