mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 13:37:45 +00:00
LibJS: Re-implement the Date constructor / prototype for spec compliance
First, this adds a constructor to the Date object to be created from a plain double. This is a first step to removing Core::DateTime as the basis for the Date object. A subsequent commit will remove the now- unused data from the object. Next, this implements the constructor in accordance to the spec. The constructor when NewTarget is undefined no longer allocates a Date on the heap. The other constructor properly uses recently created AOs to handle time zone and ensure the created [[DateValue]] is valid. Other methods on the constructor (Date.now) have not been touched yet. Last, the prototype is reimplemented. Again, we use other AOs to handle time zones and time clipping. Not all prototypes are fixed; most of them are, but a few (e.g. Date.prototype.getTimezoneOffset) were not fixed, but left in a mostly unimplemented state for another commit. In all of the above, spec comments are added. This is a rather large change; but it's tough to do any of these parts individually without breaking everything else.
This commit is contained in:
parent
d31e6b9391
commit
d83ce7dd0b
9 changed files with 501 additions and 485 deletions
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
|
||||||
|
* Copyright (c) 2022, Tim Flynn <trflynn89@pm.me>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -20,6 +21,11 @@ Date* Date::create(GlobalObject& global_object, Core::DateTime datetime, i16 mil
|
||||||
return global_object.heap().allocate<Date>(global_object, datetime, milliseconds, is_invalid, *global_object.date_prototype());
|
return global_object.heap().allocate<Date>(global_object, datetime, milliseconds, is_invalid, *global_object.date_prototype());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Date* Date::create(GlobalObject& global_object, double date_value)
|
||||||
|
{
|
||||||
|
return global_object.heap().allocate<Date>(global_object, date_value, *global_object.date_prototype());
|
||||||
|
}
|
||||||
|
|
||||||
Date::Date(Core::DateTime datetime, i16 milliseconds, bool is_invalid, Object& prototype)
|
Date::Date(Core::DateTime datetime, i16 milliseconds, bool is_invalid, Object& prototype)
|
||||||
: Object(prototype)
|
: Object(prototype)
|
||||||
, m_datetime(datetime)
|
, m_datetime(datetime)
|
||||||
|
@ -28,6 +34,12 @@ Date::Date(Core::DateTime datetime, i16 milliseconds, bool is_invalid, Object& p
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Date::Date(double date_value, Object& prototype)
|
||||||
|
: Object(prototype)
|
||||||
|
, m_date_value(date_value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Date::~Date()
|
Date::~Date()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -84,9 +96,7 @@ String Date::gmt_date_string() const
|
||||||
|
|
||||||
String Date::iso_date_string() const
|
String Date::iso_date_string() const
|
||||||
{
|
{
|
||||||
auto tm = to_utc_tm();
|
int year = year_from_time(m_date_value);
|
||||||
int year = tm.tm_year + 1900;
|
|
||||||
int month = tm.tm_mon + 1;
|
|
||||||
|
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
if (year < 0)
|
if (year < 0)
|
||||||
|
@ -96,17 +106,17 @@ String Date::iso_date_string() const
|
||||||
else
|
else
|
||||||
builder.appendff("{:04}", year);
|
builder.appendff("{:04}", year);
|
||||||
builder.append('-');
|
builder.append('-');
|
||||||
builder.appendff("{:02}", month);
|
builder.appendff("{:02}", month_from_time(m_date_value) + 1);
|
||||||
builder.append('-');
|
builder.append('-');
|
||||||
builder.appendff("{:02}", tm.tm_mday);
|
builder.appendff("{:02}", date_from_time(m_date_value));
|
||||||
builder.append('T');
|
builder.append('T');
|
||||||
builder.appendff("{:02}", tm.tm_hour);
|
builder.appendff("{:02}", hour_from_time(m_date_value));
|
||||||
builder.append(':');
|
builder.append(':');
|
||||||
builder.appendff("{:02}", tm.tm_min);
|
builder.appendff("{:02}", min_from_time(m_date_value));
|
||||||
builder.append(':');
|
builder.append(':');
|
||||||
builder.appendff("{:02}", tm.tm_sec);
|
builder.appendff("{:02}", sec_from_time(m_date_value));
|
||||||
builder.append('.');
|
builder.append('.');
|
||||||
builder.appendff("{:03}", m_milliseconds);
|
builder.appendff("{:03}", ms_from_time(m_date_value));
|
||||||
builder.append('Z');
|
builder.append('Z');
|
||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
|
||||||
|
* Copyright (c) 2022, Tim Flynn <trflynn89@pm.me>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -19,11 +20,16 @@ public:
|
||||||
static constexpr double time_clip = 8.64e15;
|
static constexpr double time_clip = 8.64e15;
|
||||||
|
|
||||||
static Date* create(GlobalObject&, Core::DateTime, i16 milliseconds, bool is_invalid);
|
static Date* create(GlobalObject&, Core::DateTime, i16 milliseconds, bool is_invalid);
|
||||||
|
static Date* create(GlobalObject&, double date_value);
|
||||||
static Date* now(GlobalObject&);
|
static Date* now(GlobalObject&);
|
||||||
|
|
||||||
Date(Core::DateTime datetime, i16 milliseconds, bool is_invalid, Object& prototype);
|
Date(Core::DateTime datetime, i16 milliseconds, bool is_invalid, Object& prototype);
|
||||||
|
Date(double date_value, Object& prototype);
|
||||||
virtual ~Date() override;
|
virtual ~Date() override;
|
||||||
|
|
||||||
|
double date_value() const { return m_date_value; }
|
||||||
|
void set_date_value(double value) { m_date_value = value; }
|
||||||
|
|
||||||
Core::DateTime& datetime() { return m_datetime; }
|
Core::DateTime& datetime() { return m_datetime; }
|
||||||
const Core::DateTime& datetime() const { return m_datetime; }
|
const Core::DateTime& datetime() const { return m_datetime; }
|
||||||
|
|
||||||
|
@ -74,20 +80,14 @@ public:
|
||||||
String locale_string() const { return m_datetime.to_string(); }
|
String locale_string() const { return m_datetime.to_string(); }
|
||||||
String locale_time_string() const { return m_datetime.to_string("%H:%M:%S"); }
|
String locale_time_string() const { return m_datetime.to_string("%H:%M:%S"); }
|
||||||
|
|
||||||
// https://tc39.es/ecma262/#sec-properties-of-date-instances
|
|
||||||
// [[DateValue]]
|
|
||||||
double date_value() const
|
|
||||||
{
|
|
||||||
return m_is_invalid
|
|
||||||
? AK::NaN<double>
|
|
||||||
: static_cast<double>(m_datetime.timestamp() * 1000 + m_milliseconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
tm to_utc_tm() const;
|
tm to_utc_tm() const;
|
||||||
|
|
||||||
|
double m_date_value { 0 }; // [[DateValue]]
|
||||||
|
|
||||||
Core::DateTime m_datetime;
|
Core::DateTime m_datetime;
|
||||||
i16 m_milliseconds;
|
i16 m_milliseconds;
|
||||||
|
|
||||||
bool m_is_invalid { false };
|
bool m_is_invalid { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* Copyright (c) 2020, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2020, 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@pm.me>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
#include <LibJS/Runtime/AbstractOperations.h>
|
#include <LibJS/Runtime/AbstractOperations.h>
|
||||||
#include <LibJS/Runtime/Date.h>
|
#include <LibJS/Runtime/Date.h>
|
||||||
#include <LibJS/Runtime/DateConstructor.h>
|
#include <LibJS/Runtime/DateConstructor.h>
|
||||||
|
#include <LibJS/Runtime/DatePrototype.h>
|
||||||
#include <LibJS/Runtime/GlobalObject.h>
|
#include <LibJS/Runtime/GlobalObject.h>
|
||||||
#include <LibJS/Runtime/VM.h>
|
#include <LibJS/Runtime/VM.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
@ -156,26 +158,15 @@ DateConstructor::~DateConstructor()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DatetimeAndMilliseconds {
|
|
||||||
Core::DateTime datetime;
|
|
||||||
i16 milliseconds { 0 };
|
|
||||||
};
|
|
||||||
|
|
||||||
static DatetimeAndMilliseconds now()
|
|
||||||
{
|
|
||||||
struct timeval tv;
|
|
||||||
gettimeofday(&tv, nullptr);
|
|
||||||
auto datetime = Core::DateTime::now();
|
|
||||||
auto milliseconds = static_cast<i16>(tv.tv_usec / 1000);
|
|
||||||
return { datetime, milliseconds };
|
|
||||||
}
|
|
||||||
|
|
||||||
// 21.4.2.1 Date ( ...values ), https://tc39.es/ecma262/#sec-date
|
// 21.4.2.1 Date ( ...values ), https://tc39.es/ecma262/#sec-date
|
||||||
ThrowCompletionOr<Value> DateConstructor::call()
|
ThrowCompletionOr<Value> DateConstructor::call()
|
||||||
{
|
{
|
||||||
auto [datetime, milliseconds] = JS::now();
|
// 1. If NewTarget is undefined, then
|
||||||
auto* date = Date::create(global_object(), datetime, milliseconds, false);
|
// a. Let now be the time value (UTC) identifying the current time.
|
||||||
return js_string(heap(), date->string());
|
auto now = AK::Time::now_realtime().to_milliseconds();
|
||||||
|
|
||||||
|
// b. Return ToDateString(now).
|
||||||
|
return js_string(vm(), to_date_string(now));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.2.1 Date ( ...values ), https://tc39.es/ecma262/#sec-date
|
// 21.4.2.1 Date ( ...values ), https://tc39.es/ecma262/#sec-date
|
||||||
|
@ -184,91 +175,94 @@ 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;
|
||||||
|
|
||||||
|
// 2. Let numberOfArgs be the number of elements in values.
|
||||||
|
// 3. If numberOfArgs = 0, then
|
||||||
if (vm.argument_count() == 0) {
|
if (vm.argument_count() == 0) {
|
||||||
auto [datetime, milliseconds] = JS::now();
|
// a. Let dv be the time value (UTC) identifying the current time.
|
||||||
return TRY(ordinary_create_from_constructor<Date>(global_object, new_target, &GlobalObject::date_prototype, datetime, milliseconds, false));
|
auto now = AK::Time::now_realtime().to_milliseconds();
|
||||||
|
date_value = Value(static_cast<double>(now));
|
||||||
}
|
}
|
||||||
|
// 4. Else if numberOfArgs = 1, then
|
||||||
auto create_invalid_date = [&global_object, &new_target]() -> ThrowCompletionOr<Date*> {
|
else if (vm.argument_count() == 1) {
|
||||||
auto datetime = Core::DateTime::create(1970, 1, 1, 0, 0, 0);
|
// a. Let value be values[0].
|
||||||
auto milliseconds = static_cast<i16>(0);
|
|
||||||
return ordinary_create_from_constructor<Date>(global_object, new_target, &GlobalObject::date_prototype, datetime, milliseconds, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (vm.argument_count() == 1) {
|
|
||||||
auto value = vm.argument(0);
|
auto value = vm.argument(0);
|
||||||
if (value.is_string())
|
Value time_value;
|
||||||
value = parse_date_string(global_object, value.as_string().string());
|
|
||||||
else
|
|
||||||
value = TRY(value.to_number(global_object));
|
|
||||||
|
|
||||||
if (!value.is_finite_number())
|
// b. If Type(value) is Object and value has a [[DateValue]] internal slot, then
|
||||||
return TRY(create_invalid_date());
|
if (value.is_object() && is<Date>(value.as_object())) {
|
||||||
|
// i. Let tv be ! thisTimeValue(value).
|
||||||
|
time_value = MUST(this_time_value(global_object, value));
|
||||||
|
}
|
||||||
|
// c. Else,
|
||||||
|
else {
|
||||||
|
// i. Let v be ? ToPrimitive(value).
|
||||||
|
auto primitive = TRY(value.to_primitive(global_object));
|
||||||
|
|
||||||
// A timestamp since the epoch, in UTC.
|
// ii. If Type(v) is String, then
|
||||||
double value_as_double = value.as_double();
|
if (primitive.is_string()) {
|
||||||
if (value_as_double > Date::time_clip)
|
// 1. Assert: The next step never returns an abrupt completion because Type(v) is String.
|
||||||
return TRY(create_invalid_date());
|
// 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).
|
||||||
auto datetime = Core::DateTime::from_timestamp(static_cast<time_t>(value_as_double / 1000));
|
time_value = parse_date_string(global_object, primitive.as_string().string());
|
||||||
auto milliseconds = static_cast<i16>(fmod(value_as_double, 1000));
|
}
|
||||||
return TRY(ordinary_create_from_constructor<Date>(global_object, new_target, &GlobalObject::date_prototype, datetime, milliseconds, false));
|
// iii. Else,
|
||||||
|
else {
|
||||||
|
// 1. Let tv be ? ToNumber(v).
|
||||||
|
time_value = TRY(primitive.to_number(global_object));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// d. Let dv be TimeClip(tv).
|
||||||
|
date_value = time_clip(global_object, time_value);
|
||||||
|
}
|
||||||
|
// 5. Else,
|
||||||
|
else {
|
||||||
|
// a. Assert: numberOfArgs ≥ 2.
|
||||||
|
// b. Let y be ? ToNumber(values[0]).
|
||||||
|
auto year = TRY(vm.argument(0).to_number(global_object));
|
||||||
|
// c. Let m be ? ToNumber(values[1]).
|
||||||
|
auto month = TRY(vm.argument(1).to_number(global_object));
|
||||||
|
|
||||||
|
auto arg_or = [&vm, &global_object](size_t i, i32 fallback) -> ThrowCompletionOr<Value> {
|
||||||
|
return vm.argument_count() > i ? vm.argument(i).to_number(global_object) : Value(fallback);
|
||||||
|
};
|
||||||
|
|
||||||
|
// d. If numberOfArgs > 2, let dt be ? ToNumber(values[2]); else let dt be 1𝔽.
|
||||||
|
auto date = TRY(arg_or(2, 1));
|
||||||
|
// e. If numberOfArgs > 3, let h be ? ToNumber(values[3]); else let h be +0𝔽.
|
||||||
|
auto hours = TRY(arg_or(3, 0));
|
||||||
|
// f. If numberOfArgs > 4, let min be ? ToNumber(values[4]); else let min be +0𝔽.
|
||||||
|
auto minutes = TRY(arg_or(4, 0));
|
||||||
|
// g. If numberOfArgs > 5, let s be ? ToNumber(values[5]); else let s be +0𝔽.
|
||||||
|
auto seconds = TRY(arg_or(5, 0));
|
||||||
|
// h. If numberOfArgs > 6, let milli be ? ToNumber(values[6]); else let milli be +0𝔽.
|
||||||
|
auto milliseconds = TRY(arg_or(6, 0));
|
||||||
|
|
||||||
|
// i. If y is NaN, let yr be NaN.
|
||||||
|
// j. Else,
|
||||||
|
if (!year.is_nan()) {
|
||||||
|
// i. Let yi be ! ToIntegerOrInfinity(y).
|
||||||
|
auto year_double = MUST(year.to_integer_or_infinity(global_object));
|
||||||
|
|
||||||
|
// ii. If 0 ≤ yi ≤ 99, let yr be 1900𝔽 + 𝔽(yi); otherwise, let yr be y.
|
||||||
|
if (0 <= year_double && year_double <= 99)
|
||||||
|
year = Value(1900 + year_double);
|
||||||
|
}
|
||||||
|
|
||||||
|
// k. Let finalDate be MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli)).
|
||||||
|
auto day = make_day(global_object, year, month, date);
|
||||||
|
auto time = make_time(global_object, hours, minutes, seconds, milliseconds);
|
||||||
|
auto final_date = make_date(day, time);
|
||||||
|
|
||||||
|
// l. Let dv be TimeClip(UTC(finalDate)).
|
||||||
|
date_value = time_clip(global_object, Value(utc_time(final_date.as_double())));
|
||||||
}
|
}
|
||||||
|
|
||||||
// A date/time in components, in local time.
|
// 6. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%Date.prototype%", « [[DateValue]] »).
|
||||||
auto arg_or = [&vm, &global_object](size_t i, i32 fallback) -> ThrowCompletionOr<Value> {
|
// 7. Set O.[[DateValue]] to dv.
|
||||||
return vm.argument_count() > i ? vm.argument(i).to_number(global_object) : Value(fallback);
|
// 8. Return O.
|
||||||
};
|
return TRY(ordinary_create_from_constructor<Date>(global_object, new_target, &GlobalObject::date_prototype, date_value.as_double()));
|
||||||
|
|
||||||
auto year_value = TRY(vm.argument(0).to_number(global_object));
|
|
||||||
if (!year_value.is_finite_number())
|
|
||||||
return TRY(create_invalid_date());
|
|
||||||
auto year = year_value.as_i32();
|
|
||||||
|
|
||||||
auto month_index_value = TRY(vm.argument(1).to_number(global_object));
|
|
||||||
if (!month_index_value.is_finite_number())
|
|
||||||
return TRY(create_invalid_date());
|
|
||||||
auto month_index = month_index_value.as_i32();
|
|
||||||
|
|
||||||
auto day_value = TRY(arg_or(2, 1));
|
|
||||||
if (!day_value.is_finite_number())
|
|
||||||
return TRY(create_invalid_date());
|
|
||||||
auto day = day_value.as_i32();
|
|
||||||
|
|
||||||
auto hours_value = TRY(arg_or(3, 0));
|
|
||||||
if (!hours_value.is_finite_number())
|
|
||||||
return TRY(create_invalid_date());
|
|
||||||
auto hours = hours_value.as_i32();
|
|
||||||
|
|
||||||
auto minutes_value = TRY(arg_or(4, 0));
|
|
||||||
if (!minutes_value.is_finite_number())
|
|
||||||
return TRY(create_invalid_date());
|
|
||||||
auto minutes = minutes_value.as_i32();
|
|
||||||
|
|
||||||
auto seconds_value = TRY(arg_or(5, 0));
|
|
||||||
if (!seconds_value.is_finite_number())
|
|
||||||
return TRY(create_invalid_date());
|
|
||||||
auto seconds = seconds_value.as_i32();
|
|
||||||
|
|
||||||
auto milliseconds_value = TRY(arg_or(6, 0));
|
|
||||||
if (!milliseconds_value.is_finite_number())
|
|
||||||
return TRY(create_invalid_date());
|
|
||||||
auto milliseconds = milliseconds_value.as_i32();
|
|
||||||
|
|
||||||
seconds += milliseconds / 1000;
|
|
||||||
milliseconds %= 1000;
|
|
||||||
if (milliseconds < 0) {
|
|
||||||
seconds -= 1;
|
|
||||||
milliseconds += 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (year >= 0 && year <= 99)
|
|
||||||
year += 1900;
|
|
||||||
int month = month_index + 1;
|
|
||||||
auto datetime = Core::DateTime::create(year, month, day, hours, minutes, seconds);
|
|
||||||
auto time = datetime.timestamp() * 1000.0 + milliseconds;
|
|
||||||
if (time > Date::time_clip)
|
|
||||||
return TRY(create_invalid_date());
|
|
||||||
return TRY(ordinary_create_from_constructor<Date>(global_object, new_target, &GlobalObject::date_prototype, datetime, milliseconds, false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
|
||||||
* Copyright (c) 2021, Petróczi Zoltán <petroczizoltan@tutanota.com>
|
* Copyright (c) 2021, Petróczi Zoltán <petroczizoltan@tutanota.com>
|
||||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||||
|
* Copyright (c) 2022, Tim Flynn <trflynn89@pm.me>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -125,100 +126,120 @@ ThrowCompletionOr<Value> this_time_value(GlobalObject& global_object, Value valu
|
||||||
// 21.4.4.2 Date.prototype.getDate ( ), https://tc39.es/ecma262/#sec-date.prototype.getdate
|
// 21.4.4.2 Date.prototype.getDate ( ), https://tc39.es/ecma262/#sec-date.prototype.getdate
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_date)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_date)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be ? thisTimeValue(this value).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 2. If t is NaN, return NaN.
|
||||||
return js_nan();
|
if (time.is_nan())
|
||||||
|
return time;
|
||||||
|
|
||||||
return Value(this_object->date());
|
// 3. Return DateFromTime(LocalTime(t)).
|
||||||
|
return Value(date_from_time(local_time(time.as_double())));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.3 Date.prototype.getDay ( ), https://tc39.es/ecma262/#sec-date.prototype.getday
|
// 21.4.4.3 Date.prototype.getDay ( ), https://tc39.es/ecma262/#sec-date.prototype.getday
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_day)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_day)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be ? thisTimeValue(this value).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 2. If t is NaN, return NaN.
|
||||||
return js_nan();
|
if (time.is_nan())
|
||||||
|
return time;
|
||||||
|
|
||||||
return Value(this_object->day());
|
// 3. Return WeekDay(LocalTime(t)).
|
||||||
|
return Value(week_day(local_time(time.as_double())));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.4 Date.prototype.getFullYear ( ), https://tc39.es/ecma262/#sec-date.prototype.getfullyear
|
// 21.4.4.4 Date.prototype.getFullYear ( ), https://tc39.es/ecma262/#sec-date.prototype.getfullyear
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_full_year)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_full_year)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be ? thisTimeValue(this value).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 2. If t is NaN, return NaN.
|
||||||
return js_nan();
|
if (time.is_nan())
|
||||||
|
return time;
|
||||||
|
|
||||||
return Value(this_object->year());
|
// 3. Return YearFromTime(LocalTime(t)).
|
||||||
|
return Value(year_from_time(local_time(time.as_double())));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.5 Date.prototype.getHours ( ), https://tc39.es/ecma262/#sec-date.prototype.gethours
|
// 21.4.4.5 Date.prototype.getHours ( ), https://tc39.es/ecma262/#sec-date.prototype.gethours
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_hours)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_hours)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be ? thisTimeValue(this value).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 2. If t is NaN, return NaN.
|
||||||
return js_nan();
|
if (time.is_nan())
|
||||||
|
return time;
|
||||||
|
|
||||||
return Value(this_object->hours());
|
// 3. Return HourFromTime(LocalTime(t)).
|
||||||
|
return Value(hour_from_time(local_time(time.as_double())));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.6 Date.prototype.getMilliseconds ( ), https://tc39.es/ecma262/#sec-date.prototype.getmilliseconds
|
// 21.4.4.6 Date.prototype.getMilliseconds ( ), https://tc39.es/ecma262/#sec-date.prototype.getmilliseconds
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_milliseconds)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_milliseconds)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be ? thisTimeValue(this value).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 2. If t is NaN, return NaN.
|
||||||
return js_nan();
|
if (time.is_nan())
|
||||||
|
return time;
|
||||||
|
|
||||||
return Value(this_object->milliseconds());
|
// 3. Return msFromTime(LocalTime(t)).
|
||||||
|
return Value(ms_from_time(local_time(time.as_double())));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.7 Date.prototype.getMinutes ( ), https://tc39.es/ecma262/#sec-date.prototype.getminutes
|
// 21.4.4.7 Date.prototype.getMinutes ( ), https://tc39.es/ecma262/#sec-date.prototype.getminutes
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_minutes)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_minutes)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be ? thisTimeValue(this value).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 2. If t is NaN, return NaN.
|
||||||
return js_nan();
|
if (time.is_nan())
|
||||||
|
return time;
|
||||||
|
|
||||||
return Value(this_object->minutes());
|
// 3. Return MinFromTime(LocalTime(t)).
|
||||||
|
return Value(min_from_time(local_time(time.as_double())));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.8 Date.prototype.getMonth ( ), https://tc39.es/ecma262/#sec-date.prototype.getmonth
|
// 21.4.4.8 Date.prototype.getMonth ( ), https://tc39.es/ecma262/#sec-date.prototype.getmonth
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_month)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_month)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be ? thisTimeValue(this value).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 2. If t is NaN, return NaN.
|
||||||
return js_nan();
|
if (time.is_nan())
|
||||||
|
return time;
|
||||||
|
|
||||||
return Value(this_object->month());
|
// 3. Return MonthFromTime(LocalTime(t)).
|
||||||
|
return Value(month_from_time(local_time(time.as_double())));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.9 Date.prototype.getSeconds ( ), https://tc39.es/ecma262/#sec-date.prototype.getseconds
|
// 21.4.4.9 Date.prototype.getSeconds ( ), https://tc39.es/ecma262/#sec-date.prototype.getseconds
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_seconds)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_seconds)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be ? thisTimeValue(this value).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 2. If t is NaN, return NaN.
|
||||||
return js_nan();
|
if (time.is_nan())
|
||||||
|
return time;
|
||||||
|
|
||||||
return Value(this_object->seconds());
|
// 3. Return SecFromTime(LocalTime(t)).
|
||||||
|
return Value(sec_from_time(local_time(time.as_double())));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.10 Date.prototype.getTime ( ), https://tc39.es/ecma262/#sec-date.prototype.gettime
|
// 21.4.4.10 Date.prototype.getTime ( ), https://tc39.es/ecma262/#sec-date.prototype.gettime
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_time)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_time)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Return ? thisTimeValue(this value).
|
||||||
|
return this_time_value(global_object, vm.this_value(global_object));
|
||||||
if (this_object->is_invalid())
|
|
||||||
return js_nan();
|
|
||||||
|
|
||||||
return Value(this_object->time());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.11 Date.prototype.getTimezoneOffset ( ), https://tc39.es/ecma262/#sec-date.prototype.gettimezoneoffset
|
// 21.4.4.11 Date.prototype.getTimezoneOffset ( ), https://tc39.es/ecma262/#sec-date.prototype.gettimezoneoffset
|
||||||
|
@ -226,7 +247,7 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_timezone_offset)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
auto* this_object = TRY(typed_this_object(global_object));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
if (!Value(this_object->date_value()).is_finite_number())
|
||||||
return js_nan();
|
return js_nan();
|
||||||
|
|
||||||
// FIXME: Make this actually do something once we support timezones instead of just UTC
|
// FIXME: Make this actually do something once we support timezones instead of just UTC
|
||||||
|
@ -236,395 +257,380 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_timezone_offset)
|
||||||
// 21.4.4.12 Date.prototype.getUTCDate ( ), https://tc39.es/ecma262/#sec-date.prototype.getutcdate
|
// 21.4.4.12 Date.prototype.getUTCDate ( ), https://tc39.es/ecma262/#sec-date.prototype.getutcdate
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_date)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_date)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be ? thisTimeValue(this value).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 2. If t is NaN, return NaN.
|
||||||
return js_nan();
|
if (time.is_nan())
|
||||||
|
return time;
|
||||||
|
|
||||||
return Value(this_object->utc_date());
|
// 3. Return DateFromTime(t).
|
||||||
|
return Value(date_from_time(time.as_double()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.13 Date.prototype.getUTCDay ( ), https://tc39.es/ecma262/#sec-date.prototype.getutcday
|
// 21.4.4.13 Date.prototype.getUTCDay ( ), https://tc39.es/ecma262/#sec-date.prototype.getutcday
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_day)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_day)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be ? thisTimeValue(this value).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 2. If t is NaN, return NaN.
|
||||||
return js_nan();
|
if (time.is_nan())
|
||||||
|
return time;
|
||||||
|
|
||||||
return Value(this_object->utc_day());
|
// 3. Return WeekDay(t).
|
||||||
|
return Value(week_day(time.as_double()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.14 Date.prototype.getUTCFullYear ( ), https://tc39.es/ecma262/#sec-date.prototype.getutcfullyear
|
// 21.4.4.14 Date.prototype.getUTCFullYear ( ), https://tc39.es/ecma262/#sec-date.prototype.getutcfullyear
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_full_year)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_full_year)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be ? thisTimeValue(this value).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 2. If t is NaN, return NaN.
|
||||||
return js_nan();
|
if (time.is_nan())
|
||||||
|
return time;
|
||||||
|
|
||||||
return Value(this_object->utc_full_year());
|
// 3. Return YearFromTime(t).
|
||||||
|
return Value(year_from_time(time.as_double()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.15 Date.prototype.getUTCHours ( ), https://tc39.es/ecma262/#sec-date.prototype.getutchours
|
// 21.4.4.15 Date.prototype.getUTCHours ( ), https://tc39.es/ecma262/#sec-date.prototype.getutchours
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_hours)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_hours)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be ? thisTimeValue(this value).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 2. If t is NaN, return NaN.
|
||||||
return js_nan();
|
if (time.is_nan())
|
||||||
|
return time;
|
||||||
|
|
||||||
return Value(this_object->utc_hours());
|
// 3. Return HourFromTime(t).
|
||||||
|
return Value(hour_from_time(time.as_double()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.16 Date.prototype.getUTCMilliseconds ( ), https://tc39.es/ecma262/#sec-date.prototype.getutcmilliseconds
|
// 21.4.4.16 Date.prototype.getUTCMilliseconds ( ), https://tc39.es/ecma262/#sec-date.prototype.getutcmilliseconds
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_milliseconds)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_milliseconds)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be ? thisTimeValue(this value).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 2. If t is NaN, return NaN.
|
||||||
return js_nan();
|
if (time.is_nan())
|
||||||
|
return time;
|
||||||
|
|
||||||
return Value(this_object->utc_milliseconds());
|
// 3. Return msFromTime(t).
|
||||||
|
return Value(ms_from_time(time.as_double()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.17 Date.prototype.getUTCMinutes ( ), https://tc39.es/ecma262/#sec-date.prototype.getutcminutes
|
// 21.4.4.17 Date.prototype.getUTCMinutes ( ), https://tc39.es/ecma262/#sec-date.prototype.getutcminutes
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_minutes)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_minutes)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be ? thisTimeValue(this value).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 2. If t is NaN, return NaN.
|
||||||
return js_nan();
|
if (time.is_nan())
|
||||||
|
return time;
|
||||||
|
|
||||||
return Value(this_object->utc_minutes());
|
// 3. Return MinFromTime(t).
|
||||||
|
return Value(min_from_time(time.as_double()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.18 Date.prototype.getUTCMonth ( ), https://tc39.es/ecma262/#sec-date.prototype.getutcmonth
|
// 21.4.4.18 Date.prototype.getUTCMonth ( ), https://tc39.es/ecma262/#sec-date.prototype.getutcmonth
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_month)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_month)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be ? thisTimeValue(this value).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 2. If t is NaN, return NaN.
|
||||||
return js_nan();
|
if (time.is_nan())
|
||||||
|
return time;
|
||||||
|
|
||||||
return Value(this_object->utc_month());
|
// 3. Return MonthFromTime(t).
|
||||||
|
return Value(month_from_time(time.as_double()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.19 Date.prototype.getUTCSeconds ( ), https://tc39.es/ecma262/#sec-date.prototype.getutcseconds
|
// 21.4.4.19 Date.prototype.getUTCSeconds ( ), https://tc39.es/ecma262/#sec-date.prototype.getutcseconds
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_seconds)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_seconds)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be ? thisTimeValue(this value).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 2. If t is NaN, return NaN.
|
||||||
return js_nan();
|
if (time.is_nan())
|
||||||
|
return time;
|
||||||
|
|
||||||
return Value(this_object->utc_seconds());
|
// 3. Return SecFromTime(t).
|
||||||
|
return Value(sec_from_time(time.as_double()));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static ThrowCompletionOr<Value> argument_or_value(GlobalObject& global_object, size_t index, T fallback)
|
||||||
|
{
|
||||||
|
auto& vm = global_object.vm();
|
||||||
|
|
||||||
|
if (vm.argument_count() > index)
|
||||||
|
return vm.argument(index).to_number(global_object);
|
||||||
|
|
||||||
|
return Value(fallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.20 Date.prototype.setDate ( date ), https://tc39.es/ecma262/#sec-date.prototype.setdate
|
// 21.4.4.20 Date.prototype.setDate ( date ), https://tc39.es/ecma262/#sec-date.prototype.setdate
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_date)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_date)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be LocalTime(? thisTimeValue(this value)).
|
||||||
|
auto this_time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
auto time = local_time(this_time.as_double());
|
||||||
|
|
||||||
auto& datetime = this_object->datetime();
|
// 2. Let dt be ? ToNumber(date).
|
||||||
|
auto date = TRY(vm.argument(0).to_number(global_object));
|
||||||
|
|
||||||
auto new_date_value = TRY(vm.argument(0).to_number(global_object));
|
// 3. Let newDate be MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t)).
|
||||||
if (!new_date_value.is_finite_number()) {
|
auto year = Value(year_from_time(time));
|
||||||
this_object->set_is_invalid(true);
|
auto month = Value(month_from_time(time));
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
auto new_date = new_date_value.as_i32();
|
|
||||||
|
|
||||||
datetime.set_time(datetime.year(), datetime.month(), new_date, datetime.hour(), datetime.minute(), datetime.second());
|
auto day = make_day(global_object, year, month, date);
|
||||||
if (this_object->time() > Date::time_clip) {
|
auto new_date = make_date(day, Value(time_within_day(time)));
|
||||||
this_object->set_is_invalid(true);
|
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
|
|
||||||
this_object->set_is_invalid(false);
|
// 4. Let u be TimeClip(UTC(newDate)).
|
||||||
return Value(this_object->time());
|
new_date = time_clip(global_object, Value(utc_time(new_date.as_double())));
|
||||||
|
|
||||||
|
// 5. Set the [[DateValue]] internal slot of this Date object to u.
|
||||||
|
auto* this_object = MUST(typed_this_object(global_object));
|
||||||
|
this_object->set_date_value(new_date.as_double());
|
||||||
|
|
||||||
|
// 6. Return u.
|
||||||
|
return new_date;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.21 Date.prototype.setFullYear ( year [ , month [ , date ] ] ), https://tc39.es/ecma262/#sec-date.prototype.setfullyear
|
// 21.4.4.21 Date.prototype.setFullYear ( year [ , month [ , date ] ] ), https://tc39.es/ecma262/#sec-date.prototype.setfullyear
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_full_year)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_full_year)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be ? thisTimeValue(this value).
|
||||||
|
auto this_time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
auto& datetime = this_object->datetime();
|
// 2. If t is NaN, set t to +0𝔽; otherwise, set t to LocalTime(t).
|
||||||
|
double time = 0;
|
||||||
|
if (!this_time.is_nan())
|
||||||
|
time = local_time(this_time.as_double());
|
||||||
|
|
||||||
auto arg_or = [&vm, &global_object](size_t i, i32 fallback) -> ThrowCompletionOr<Value> {
|
// 3. Let y be ? ToNumber(year).
|
||||||
return vm.argument_count() > i ? vm.argument(i).to_number(global_object) : Value(fallback);
|
auto year = TRY(vm.argument(0).to_number(global_object));
|
||||||
};
|
|
||||||
|
|
||||||
auto new_year_value = TRY(vm.argument(0).to_number(global_object));
|
// 4. If month is not present, let m be MonthFromTime(t); otherwise, let m be ? ToNumber(month).
|
||||||
if (!new_year_value.is_finite_number()) {
|
auto month = TRY(argument_or_value(global_object, 1, month_from_time(time)));
|
||||||
this_object->set_is_invalid(true);
|
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
auto new_year = new_year_value.as_i32();
|
|
||||||
|
|
||||||
auto new_month_value = TRY(arg_or(1, datetime.month() - 1));
|
// 5. If date is not present, let dt be DateFromTime(t); otherwise, let dt be ? ToNumber(date).
|
||||||
if (!new_month_value.is_finite_number()) {
|
auto date = TRY(argument_or_value(global_object, 2, date_from_time(time)));
|
||||||
this_object->set_is_invalid(true);
|
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
auto new_month = new_month_value.as_i32() + 1; // JS Months: 0 - 11, DateTime months: 1-12
|
|
||||||
|
|
||||||
auto new_day_value = TRY(arg_or(2, datetime.day()));
|
// 6. Let newDate be MakeDate(MakeDay(y, m, dt), TimeWithinDay(t)).
|
||||||
if (!new_day_value.is_finite_number()) {
|
auto day = make_day(global_object, year, month, date);
|
||||||
this_object->set_is_invalid(true);
|
auto new_date = make_date(day, Value(time_within_day(time)));
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
auto new_day = new_day_value.as_i32();
|
|
||||||
|
|
||||||
datetime.set_time(new_year, new_month, new_day, datetime.hour(), datetime.minute(), datetime.second());
|
// 7. Let u be TimeClip(UTC(newDate)).
|
||||||
if (this_object->time() > Date::time_clip) {
|
new_date = time_clip(global_object, Value(utc_time(new_date.as_double())));
|
||||||
this_object->set_is_invalid(true);
|
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
|
|
||||||
this_object->set_is_invalid(false);
|
// 8. Set the [[DateValue]] internal slot of this Date object to u.
|
||||||
|
auto* this_object = MUST(typed_this_object(global_object));
|
||||||
|
this_object->set_date_value(new_date.as_double());
|
||||||
|
|
||||||
return Value(this_object->time());
|
// 9. Return u.
|
||||||
|
return new_date;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.22 Date.prototype.setHours ( hour [ , min [ , sec [ , ms ] ] ] ), https://tc39.es/ecma262/#sec-date.prototype.sethours
|
// 21.4.4.22 Date.prototype.setHours ( hour [ , min [ , sec [ , ms ] ] ] ), https://tc39.es/ecma262/#sec-date.prototype.sethours
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_hours)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_hours)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be LocalTime(? thisTimeValue(this value)).
|
||||||
|
auto this_time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
auto time = local_time(this_time.as_double());
|
||||||
|
|
||||||
auto arg_or = [&vm, &global_object](size_t i, i32 fallback) -> ThrowCompletionOr<Value> {
|
// 2. Let h be ? ToNumber(hour).
|
||||||
return vm.argument_count() > i ? vm.argument(i).to_number(global_object) : Value(fallback);
|
auto hour = TRY(vm.argument(0).to_number(global_object));
|
||||||
};
|
|
||||||
|
|
||||||
auto& datetime = this_object->datetime();
|
// 3. If min is not present, let m be MinFromTime(t); otherwise, let m be ? ToNumber(min).
|
||||||
|
auto minute = TRY(argument_or_value(global_object, 1, min_from_time(time)));
|
||||||
|
|
||||||
auto new_hours_value = TRY(vm.argument(0).to_number(global_object));
|
// 4. If sec is not present, let s be SecFromTime(t); otherwise, let s be ? ToNumber(sec).
|
||||||
if (!new_hours_value.is_finite_number()) {
|
auto second = TRY(argument_or_value(global_object, 2, sec_from_time(time)));
|
||||||
this_object->set_is_invalid(true);
|
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
auto new_hours = new_hours_value.as_i32();
|
|
||||||
|
|
||||||
auto new_minutes_value = TRY(arg_or(1, datetime.minute()));
|
// 5. If ms is not present, let milli be msFromTime(t); otherwise, let milli be ? ToNumber(ms).
|
||||||
if (!new_minutes_value.is_finite_number()) {
|
auto millisecond = TRY(argument_or_value(global_object, 3, ms_from_time(time)));
|
||||||
this_object->set_is_invalid(true);
|
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
auto new_minutes = new_minutes_value.as_i32();
|
|
||||||
|
|
||||||
auto new_seconds_value = TRY(arg_or(2, datetime.second()));
|
// 6. Let date be MakeDate(Day(t), MakeTime(h, m, s, milli)).
|
||||||
if (!new_seconds_value.is_finite_number()) {
|
auto new_time = make_time(global_object, hour, minute, second, millisecond);
|
||||||
this_object->set_is_invalid(true);
|
auto date = make_date(Value(day(time)), new_time);
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
auto new_seconds = new_seconds_value.as_i32();
|
|
||||||
|
|
||||||
auto new_milliseconds_value = TRY(arg_or(3, this_object->milliseconds()));
|
// 7. Let u be TimeClip(UTC(date)).
|
||||||
if (!new_milliseconds_value.is_finite_number()) {
|
date = time_clip(global_object, Value(utc_time(date.as_double())));
|
||||||
this_object->set_is_invalid(true);
|
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
auto new_milliseconds = new_milliseconds_value.as_i32();
|
|
||||||
|
|
||||||
new_seconds += new_milliseconds / 1000;
|
// 8. Set the [[DateValue]] internal slot of this Date object to u.
|
||||||
this_object->set_milliseconds(new_milliseconds % 1000);
|
auto* this_object = MUST(typed_this_object(global_object));
|
||||||
|
this_object->set_date_value(date.as_double());
|
||||||
|
|
||||||
datetime.set_time(datetime.year(), datetime.month(), datetime.day(), new_hours, new_minutes, new_seconds);
|
// 9. Return u.
|
||||||
if (this_object->time() > Date::time_clip) {
|
return date;
|
||||||
this_object->set_is_invalid(true);
|
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
|
|
||||||
this_object->set_is_invalid(false);
|
|
||||||
return Value(this_object->time());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.23 Date.prototype.setMilliseconds ( ms ), https://tc39.es/ecma262/#sec-date.prototype.setmilliseconds
|
// 21.4.4.23 Date.prototype.setMilliseconds ( ms ), https://tc39.es/ecma262/#sec-date.prototype.setmilliseconds
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_milliseconds)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_milliseconds)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be LocalTime(? thisTimeValue(this value)).
|
||||||
|
auto this_time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
auto time = local_time(this_time.as_double());
|
||||||
|
|
||||||
auto new_milliseconds_value = TRY(vm.argument(0).to_number(global_object));
|
// 2. Set ms to ? ToNumber(ms).
|
||||||
|
auto millisecond = TRY(vm.argument(0).to_number(global_object));
|
||||||
|
|
||||||
if (!new_milliseconds_value.is_finite_number()) {
|
// 3. Let time be MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms).
|
||||||
this_object->set_is_invalid(true);
|
auto hour = Value(hour_from_time(time));
|
||||||
return js_nan();
|
auto minute = Value(min_from_time(time));
|
||||||
}
|
auto second = Value(sec_from_time(time));
|
||||||
|
|
||||||
auto new_milliseconds = new_milliseconds_value.as_i32();
|
auto new_time = make_time(global_object, hour, minute, second, millisecond);
|
||||||
|
|
||||||
this_object->set_milliseconds(new_milliseconds % 1000);
|
// 4. Let u be TimeClip(UTC(MakeDate(Day(t), time))).
|
||||||
|
auto date = make_date(Value(day(time)), new_time);
|
||||||
|
date = time_clip(global_object, Value(utc_time(date.as_double())));
|
||||||
|
|
||||||
auto added_seconds = new_milliseconds / 1000;
|
// 5. Set the [[DateValue]] internal slot of this Date object to u.
|
||||||
if (added_seconds > 0) {
|
auto* this_object = MUST(typed_this_object(global_object));
|
||||||
auto& datetime = this_object->datetime();
|
this_object->set_date_value(date.as_double());
|
||||||
datetime.set_time(datetime.year(), datetime.month(), datetime.day(), datetime.hour(), datetime.minute(), datetime.second() + added_seconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this_object->time() > Date::time_clip) {
|
// 6. Return u.
|
||||||
this_object->set_is_invalid(true);
|
return date;
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
|
|
||||||
this_object->set_is_invalid(false);
|
|
||||||
|
|
||||||
return Value(this_object->time());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.24 Date.prototype.setMinutes ( min [ , sec [ , ms ] ] ), https://tc39.es/ecma262/#sec-date.prototype.setminutes
|
// 21.4.4.24 Date.prototype.setMinutes ( min [ , sec [ , ms ] ] ), https://tc39.es/ecma262/#sec-date.prototype.setminutes
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_minutes)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_minutes)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be LocalTime(? thisTimeValue(this value)).
|
||||||
|
auto this_time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
auto time = local_time(this_time.as_double());
|
||||||
|
|
||||||
auto arg_or = [&vm, &global_object](size_t i, i32 fallback) -> ThrowCompletionOr<Value> {
|
// 2. Let m be ? ToNumber(min).
|
||||||
return vm.argument_count() > i ? vm.argument(i).to_number(global_object) : Value(fallback);
|
auto minute = TRY(vm.argument(0).to_number(global_object));
|
||||||
};
|
|
||||||
|
|
||||||
auto& datetime = this_object->datetime();
|
// 3. If sec is not present, let s be SecFromTime(t); otherwise, let s be ? ToNumber(sec).
|
||||||
|
auto second = TRY(argument_or_value(global_object, 1, sec_from_time(time)));
|
||||||
|
|
||||||
auto new_minutes_value = TRY(vm.argument(0).to_number(global_object));
|
// 4. If ms is not present, let milli be msFromTime(t); otherwise, let milli be ? ToNumber(ms).
|
||||||
if (!new_minutes_value.is_finite_number()) {
|
auto millisecond = TRY(argument_or_value(global_object, 2, ms_from_time(time)));
|
||||||
this_object->set_is_invalid(true);
|
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
auto new_minutes = new_minutes_value.as_i32();
|
|
||||||
|
|
||||||
auto new_seconds_value = TRY(arg_or(1, datetime.second()));
|
// 5. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)).
|
||||||
if (!new_seconds_value.is_finite_number()) {
|
auto hour = Value(hour_from_time(time));
|
||||||
this_object->set_is_invalid(true);
|
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
auto new_seconds = new_seconds_value.as_i32();
|
|
||||||
|
|
||||||
auto new_milliseconds_value = TRY(arg_or(2, this_object->milliseconds()));
|
auto new_time = make_time(global_object, hour, minute, second, millisecond);
|
||||||
if (!new_milliseconds_value.is_finite_number()) {
|
auto date = make_date(Value(day(time)), new_time);
|
||||||
this_object->set_is_invalid(true);
|
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
auto new_milliseconds = new_milliseconds_value.as_i32();
|
|
||||||
|
|
||||||
new_seconds += new_milliseconds / 1000;
|
// 6. Let u be TimeClip(UTC(date)).
|
||||||
this_object->set_milliseconds(new_milliseconds % 1000);
|
date = time_clip(global_object, Value(utc_time(date.as_double())));
|
||||||
|
|
||||||
datetime.set_time(datetime.year(), datetime.month(), datetime.day(), datetime.hour(), new_minutes, new_seconds);
|
// 7. Set the [[DateValue]] internal slot of this Date object to u.
|
||||||
if (this_object->time() > Date::time_clip) {
|
auto* this_object = MUST(typed_this_object(global_object));
|
||||||
this_object->set_is_invalid(true);
|
this_object->set_date_value(date.as_double());
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
|
|
||||||
this_object->set_is_invalid(false);
|
// 8. Return u.
|
||||||
return Value(this_object->time());
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.25 Date.prototype.setMonth ( month [ , date ] ), https://tc39.es/ecma262/#sec-date.prototype.setmonth
|
// 21.4.4.25 Date.prototype.setMonth ( month [ , date ] ), https://tc39.es/ecma262/#sec-date.prototype.setmonth
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_month)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_month)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be LocalTime(? thisTimeValue(this value)).
|
||||||
|
auto this_time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
auto time = local_time(this_time.as_double());
|
||||||
|
|
||||||
auto arg_or = [&vm, &global_object](size_t i, i32 fallback) -> ThrowCompletionOr<Value> {
|
// 2. Let m be ? ToNumber(month).
|
||||||
return vm.argument_count() > i ? vm.argument(i).to_number(global_object) : Value(fallback);
|
auto month = TRY(vm.argument(0).to_number(global_object));
|
||||||
};
|
|
||||||
|
|
||||||
auto& datetime = this_object->datetime();
|
// 3. If date is not present, let dt be DateFromTime(t); otherwise, let dt be ? ToNumber(date).
|
||||||
|
auto date = TRY(argument_or_value(global_object, 1, date_from_time(time)));
|
||||||
|
|
||||||
auto new_month_value = TRY(vm.argument(0).to_number(global_object));
|
// 4. Let newDate be MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t)).
|
||||||
if (!new_month_value.is_finite_number()) {
|
auto year = Value(year_from_time(time));
|
||||||
this_object->set_is_invalid(true);
|
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
auto new_month = new_month_value.as_i32() + 1; // JS Months: 0 - 11, DateTime months: 1-12
|
|
||||||
|
|
||||||
auto new_date_value = TRY(arg_or(1, this_object->date()));
|
auto day = make_day(global_object, year, month, date);
|
||||||
if (!new_date_value.is_finite_number()) {
|
auto new_date = make_date(day, Value(time_within_day(time)));
|
||||||
this_object->set_is_invalid(true);
|
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
auto new_date = new_date_value.as_i32();
|
|
||||||
|
|
||||||
datetime.set_time(datetime.year(), new_month, new_date, datetime.hour(), datetime.minute(), datetime.second());
|
// 5. Let u be TimeClip(UTC(newDate)).
|
||||||
if (this_object->time() > Date::time_clip) {
|
new_date = time_clip(global_object, Value(utc_time(new_date.as_double())));
|
||||||
this_object->set_is_invalid(true);
|
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
|
|
||||||
this_object->set_is_invalid(false);
|
// 6. Set the [[DateValue]] internal slot of this Date object to u.
|
||||||
return Value(this_object->time());
|
auto* this_object = MUST(typed_this_object(global_object));
|
||||||
|
this_object->set_date_value(new_date.as_double());
|
||||||
|
|
||||||
|
// 7. Return u.
|
||||||
|
return new_date;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.26 Date.prototype.setSeconds ( sec [ , ms ] ), https://tc39.es/ecma262/#sec-date.prototype.setseconds
|
// 21.4.4.26 Date.prototype.setSeconds ( sec [ , ms ] ), https://tc39.es/ecma262/#sec-date.prototype.setseconds
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_seconds)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_seconds)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be LocalTime(? thisTimeValue(this value)).
|
||||||
|
auto this_time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
auto time = local_time(this_time.as_double());
|
||||||
|
|
||||||
auto arg_or = [&vm, &global_object](size_t i, i32 fallback) -> ThrowCompletionOr<Value> {
|
// 2. Let s be ? ToNumber(sec).
|
||||||
return vm.argument_count() > i ? vm.argument(i).to_number(global_object) : Value(fallback);
|
auto second = TRY(vm.argument(0).to_number(global_object));
|
||||||
};
|
|
||||||
|
|
||||||
auto& datetime = this_object->datetime();
|
// 3. If ms is not present, let milli be msFromTime(t); otherwise, let milli be ? ToNumber(ms).
|
||||||
|
auto millisecond = TRY(argument_or_value(global_object, 1, ms_from_time(time)));
|
||||||
|
|
||||||
auto new_seconds_value = TRY(vm.argument(0).to_number(global_object));
|
// 4. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli)).
|
||||||
if (!new_seconds_value.is_finite_number()) {
|
auto hour = Value(hour_from_time(time));
|
||||||
this_object->set_is_invalid(true);
|
auto minute = Value(min_from_time(time));
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
auto new_seconds = new_seconds_value.as_i32();
|
|
||||||
|
|
||||||
auto new_milliseconds_value = TRY(arg_or(1, this_object->milliseconds()));
|
auto new_time = make_time(global_object, hour, minute, second, millisecond);
|
||||||
if (!new_milliseconds_value.is_finite_number()) {
|
auto new_date = make_date(Value(day(time)), new_time);
|
||||||
this_object->set_is_invalid(true);
|
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
auto new_milliseconds = new_milliseconds_value.as_i32();
|
|
||||||
|
|
||||||
new_seconds += new_milliseconds / 1000;
|
// 5. Let u be TimeClip(UTC(date)).
|
||||||
this_object->set_milliseconds(new_milliseconds % 1000);
|
new_date = time_clip(global_object, Value(utc_time(new_date.as_double())));
|
||||||
|
|
||||||
datetime.set_time(datetime.year(), datetime.month(), datetime.day(), datetime.hour(), datetime.minute(), new_seconds);
|
// 6. Set the [[DateValue]] internal slot of this Date object to u.
|
||||||
if (this_object->time() > Date::time_clip) {
|
auto* this_object = MUST(typed_this_object(global_object));
|
||||||
this_object->set_is_invalid(true);
|
this_object->set_date_value(new_date.as_double());
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
|
|
||||||
this_object->set_is_invalid(false);
|
// 7. Return u.
|
||||||
return Value(this_object->time());
|
return new_date;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.27 Date.prototype.setTime ( time ), https://tc39.es/ecma262/#sec-date.prototype.settime
|
// 21.4.4.27 Date.prototype.setTime ( time ), https://tc39.es/ecma262/#sec-date.prototype.settime
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_time)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_time)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Perform ? thisTimeValue(this value).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
auto new_time_value = TRY(vm.argument(0).to_number(global_object));
|
// 2. Let t be ? ToNumber(time).
|
||||||
if (!new_time_value.is_finite_number()) {
|
time = TRY(vm.argument(0).to_number(global_object));
|
||||||
this_object->set_is_invalid(true);
|
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
auto new_time = new_time_value.as_double();
|
|
||||||
|
|
||||||
if (new_time > Date::time_clip) {
|
// 3. Let v be TimeClip(t).
|
||||||
this_object->set_is_invalid(true);
|
time = time_clip(global_object, time);
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto new_date_time = Core::DateTime::from_timestamp(static_cast<time_t>(new_time / 1000));
|
// 4. Set the [[DateValue]] internal slot of this Date object to v.
|
||||||
this_object->datetime().set_time(new_date_time.year(), new_date_time.month(), new_date_time.day(), new_date_time.hour(), new_date_time.minute(), new_date_time.second());
|
auto* this_object = MUST(typed_this_object(global_object));
|
||||||
this_object->set_milliseconds(static_cast<i16>(fmod(new_time, 1000)));
|
this_object->set_date_value(time.as_double());
|
||||||
|
|
||||||
this_object->set_is_invalid(false);
|
// 5. Return v.
|
||||||
return Value(this_object->time());
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.35 Date.prototype.toDateString ( ), https://tc39.es/ecma262/#sec-date.prototype.todatestring
|
// 21.4.4.35 Date.prototype.toDateString ( ), https://tc39.es/ecma262/#sec-date.prototype.todatestring
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_date_string)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_date_string)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let O be this Date object.
|
||||||
|
// 2. Let tv be ? thisTimeValue(O).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 3. If tv is NaN, return "Invalid Date".
|
||||||
return js_string(vm, "Invalid Date");
|
if (time.is_nan())
|
||||||
|
return js_string(vm, "Invalid Date"sv);
|
||||||
|
|
||||||
auto string = this_object->date_string();
|
// 4. Let t be LocalTime(tv).
|
||||||
return js_string(vm, move(string));
|
// 5. Return DateString(t).
|
||||||
|
return js_string(vm, date_string(local_time(time.as_double())));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.36 Date.prototype.toISOString ( ), https://tc39.es/ecma262/#sec-date.prototype.toisostring
|
// 21.4.4.36 Date.prototype.toISOString ( ), https://tc39.es/ecma262/#sec-date.prototype.toisostring
|
||||||
|
@ -632,7 +638,7 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_iso_string)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
auto* this_object = TRY(typed_this_object(global_object));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
if (!Value(this_object->date_value()).is_finite_number())
|
||||||
return vm.throw_completion<RangeError>(global_object, ErrorType::InvalidTimeValue);
|
return vm.throw_completion<RangeError>(global_object, ErrorType::InvalidTimeValue);
|
||||||
|
|
||||||
auto string = this_object->iso_date_string();
|
auto string = this_object->iso_date_string();
|
||||||
|
@ -740,13 +746,11 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_locale_time_string)
|
||||||
// 21.4.4.41 Date.prototype.toString ( ), https://tc39.es/ecma262/#sec-date.prototype.tostring
|
// 21.4.4.41 Date.prototype.toString ( ), https://tc39.es/ecma262/#sec-date.prototype.tostring
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_string)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_string)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let tv be ? thisTimeValue(this value).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 2. Return ToDateString(tv).
|
||||||
return js_string(vm, "Invalid Date");
|
return js_string(vm, JS::to_date_string(time.as_double()));
|
||||||
|
|
||||||
auto string = this_object->string();
|
|
||||||
return js_string(vm, move(string));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.41.1 TimeString ( tv ), https://tc39.es/ecma262/#sec-timestring
|
// 21.4.4.41.1 TimeString ( tv ), https://tc39.es/ecma262/#sec-timestring
|
||||||
|
@ -861,25 +865,52 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_temporal_instant)
|
||||||
// 21.4.4.42 Date.prototype.toTimeString ( ), https://tc39.es/ecma262/#sec-date.prototype.totimestring
|
// 21.4.4.42 Date.prototype.toTimeString ( ), https://tc39.es/ecma262/#sec-date.prototype.totimestring
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_time_string)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_time_string)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let O be this Date object.
|
||||||
|
// 2. Let tv be ? thisTimeValue(O).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 3. If tv is NaN, return "Invalid Date".
|
||||||
return js_string(vm, "Invalid Date");
|
if (time.is_nan())
|
||||||
|
return js_string(vm, "Invalid Date"sv);
|
||||||
|
|
||||||
auto string = this_object->time_string();
|
// 4. Let t be LocalTime(tv).
|
||||||
|
// 5. Return the string-concatenation of TimeString(t) and TimeZoneString(tv).
|
||||||
|
auto string = String::formatted("{}{}", time_string(local_time(time.as_double())), time_zone_string(time.as_double()));
|
||||||
return js_string(vm, move(string));
|
return js_string(vm, move(string));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21.4.4.43 Date.prototype.toUTCString ( ), https://tc39.es/ecma262/#sec-date.prototype.toutcstring
|
// 21.4.4.43 Date.prototype.toUTCString ( ), https://tc39.es/ecma262/#sec-date.prototype.toutcstring
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_utc_string)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_utc_string)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let O be this Date object.
|
||||||
|
// 2. Let tv be ? thisTimeValue(O).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 3. If tv is NaN, return "Invalid Date".
|
||||||
return js_string(vm, "Invalid Date");
|
if (time.is_nan())
|
||||||
|
return js_string(vm, "Invalid Date"sv);
|
||||||
|
|
||||||
// HTTP dates are always expressed in GMT.
|
// 4. Let weekday be the Name of the entry in Table 62 with the Number WeekDay(tv).
|
||||||
auto string = this_object->gmt_date_string();
|
auto weekday = day_names[week_day(time.as_double())];
|
||||||
|
|
||||||
|
// 5. Let month be the Name of the entry in Table 63 with the Number MonthFromTime(tv).
|
||||||
|
auto month = month_names[month_from_time(time.as_double())];
|
||||||
|
|
||||||
|
// 6. Let day be the String representation of DateFromTime(tv), formatted as a two-digit decimal number, padded to the left with the code unit 0x0030 (DIGIT ZERO) if necessary.
|
||||||
|
auto day = date_from_time(time.as_double());
|
||||||
|
|
||||||
|
// 7. Let yv be YearFromTime(tv).
|
||||||
|
auto year = year_from_time(time.as_double());
|
||||||
|
|
||||||
|
// 8. If yv ≥ +0𝔽, let yearSign be the empty String; otherwise, let yearSign be "-".
|
||||||
|
auto year_sign = year >= 0 ? ""sv : "-"sv;
|
||||||
|
|
||||||
|
// 9. Let year be the String representation of abs(ℝ(yv)), formatted as a decimal number.
|
||||||
|
year = abs(year);
|
||||||
|
|
||||||
|
// 10. Let paddedYear be ! StringPad(year, 4𝔽, "0", start).
|
||||||
|
// 11. Return the string-concatenation of weekday, ",", the code unit 0x0020 (SPACE), day, the code unit 0x0020 (SPACE), month, the code unit 0x0020 (SPACE), yearSign, paddedYear, the code unit 0x0020 (SPACE), and TimeString(tv).
|
||||||
|
auto string = String::formatted("{}, {:02} {} {}{:04} {}", weekday, day, month, year_sign, year, time_string(time.as_double()));
|
||||||
return js_string(vm, move(string));
|
return js_string(vm, move(string));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -906,39 +937,62 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::symbol_to_primitive)
|
||||||
// B.2.4.1 Date.prototype.getYear ( ), https://tc39.es/ecma262/#sec-date.prototype.getyear
|
// B.2.4.1 Date.prototype.getYear ( ), https://tc39.es/ecma262/#sec-date.prototype.getyear
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_year)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_year)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be ? thisTimeValue(this value).
|
||||||
|
auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
if (this_object->is_invalid())
|
// 2. If t is NaN, return NaN.
|
||||||
return js_nan();
|
if (time.is_nan())
|
||||||
|
return time;
|
||||||
|
|
||||||
return Value(this_object->year() - 1900);
|
// 3. Return Return YearFromTime(LocalTime(t)) - 1900𝔽.
|
||||||
|
return Value(year_from_time(local_time(time.as_double())) - 1900);
|
||||||
}
|
}
|
||||||
|
|
||||||
// B.2.4.2 Date.prototype.setYear ( year ), https://tc39.es/ecma262/#sec-date.prototype.setyear
|
// B.2.4.2 Date.prototype.setYear ( year ), https://tc39.es/ecma262/#sec-date.prototype.setyear
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_year)
|
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_year)
|
||||||
{
|
{
|
||||||
auto* this_object = TRY(typed_this_object(global_object));
|
// 1. Let t be ? thisTimeValue(this value).
|
||||||
|
auto this_time = TRY(this_time_value(global_object, vm.this_value(global_object)));
|
||||||
|
|
||||||
auto& datetime = this_object->datetime();
|
// 2. If t is NaN, set t to +0𝔽; otherwise, set t to LocalTime(t).
|
||||||
|
double time = 0;
|
||||||
|
if (!this_time.is_nan())
|
||||||
|
time = local_time(this_time.as_double());
|
||||||
|
|
||||||
auto new_year_value = TRY(vm.argument(0).to_number(global_object));
|
// 3. Let y be ? ToNumber(year).
|
||||||
if (!new_year_value.is_finite_number()) {
|
auto year = TRY(vm.argument(0).to_number(global_object));
|
||||||
this_object->set_is_invalid(true);
|
|
||||||
return js_nan();
|
|
||||||
}
|
|
||||||
auto new_year = new_year_value.as_i32();
|
|
||||||
if (new_year >= 0 && new_year <= 99)
|
|
||||||
new_year += 1900;
|
|
||||||
|
|
||||||
datetime.set_time(new_year, datetime.month(), datetime.day(), datetime.hour(), datetime.minute(), datetime.second());
|
auto* this_object = MUST(typed_this_object(global_object));
|
||||||
if (this_object->time() > Date::time_clip) {
|
|
||||||
this_object->set_is_invalid(true);
|
// 4. If y is NaN, then
|
||||||
return js_nan();
|
if (year.is_nan()) {
|
||||||
|
// a. Set the [[DateValue]] internal slot of this Date object to NaN.
|
||||||
|
this_object->set_date_value(js_nan().as_double());
|
||||||
|
|
||||||
|
// b. Return NaN.
|
||||||
|
return year;
|
||||||
}
|
}
|
||||||
|
|
||||||
this_object->set_is_invalid(false);
|
// 5. Let yi be ! ToIntegerOrInfinity(y).
|
||||||
|
auto year_double = MUST(year.to_integer_or_infinity(global_object));
|
||||||
|
|
||||||
return Value(this_object->time());
|
// 6. If 0 ≤ yi ≤ 99, let yyyy be 1900𝔽 + 𝔽(yi).
|
||||||
|
if (0 <= year_double && year_double <= 99)
|
||||||
|
year = Value(1900 + year_double);
|
||||||
|
// 7. Else, let yyyy be y.
|
||||||
|
|
||||||
|
// 8. Let d be MakeDay(yyyy, MonthFromTime(t), DateFromTime(t)).
|
||||||
|
auto day = make_day(global_object, year, Value(month_from_time(time)), Value(date_from_time(time)));
|
||||||
|
|
||||||
|
// 9. Let date be UTC(MakeDate(d, TimeWithinDay(t))).
|
||||||
|
auto date = utc_time(make_date(day, Value(time_within_day(time))).as_double());
|
||||||
|
|
||||||
|
// 10. Set the [[DateValue]] internal slot of this Date object to TimeClip(date).
|
||||||
|
auto new_date = time_clip(global_object, Value(date));
|
||||||
|
this_object->set_date_value(new_date.as_double());
|
||||||
|
|
||||||
|
// 11. Return the value of the [[DateValue]] internal slot of this Date object.
|
||||||
|
return new_date;
|
||||||
}
|
}
|
||||||
|
|
||||||
// B.2.4.3 Date.prototype.toGMTString ( ), https://tc39.es/ecma262/#sec-date.prototype.togmtstring
|
// B.2.4.3 Date.prototype.toGMTString ( ), https://tc39.es/ecma262/#sec-date.prototype.togmtstring
|
||||||
|
|
|
@ -30,18 +30,3 @@ test("Day as argument", () => {
|
||||||
expect(date.getSeconds()).toBe(0);
|
expect(date.getSeconds()).toBe(0);
|
||||||
expect(date.getMilliseconds()).toBe(0);
|
expect(date.getMilliseconds()).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Make Invalid Date valid again", () => {
|
|
||||||
let date = new Date(2021, 0, 1);
|
|
||||||
date.setDate(NaN);
|
|
||||||
expect(date.getTime()).toBe(NaN);
|
|
||||||
|
|
||||||
date.setDate(16);
|
|
||||||
expect(date.getFullYear()).toBe(2021);
|
|
||||||
expect(date.getMonth()).toBe(0);
|
|
||||||
expect(date.getDate()).toBe(16);
|
|
||||||
expect(date.getHours()).toBe(0);
|
|
||||||
expect(date.getMinutes()).toBe(0);
|
|
||||||
expect(date.getSeconds()).toBe(0);
|
|
||||||
expect(date.getMilliseconds()).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
|
@ -60,28 +60,4 @@ test("NaN or undefined in any arguments", () => {
|
||||||
date = new Date(2021, 0, 1);
|
date = new Date(2021, 0, 1);
|
||||||
date.setMonth(2021, undefined);
|
date.setMonth(2021, undefined);
|
||||||
expect(date.getTime()).toBe(NaN);
|
expect(date.getTime()).toBe(NaN);
|
||||||
|
|
||||||
date.setMonth(3, 16);
|
|
||||||
expect(date.getFullYear()).toBe(2021);
|
|
||||||
expect(date.getMonth()).toBe(3);
|
|
||||||
expect(date.getDate()).toBe(16);
|
|
||||||
expect(date.getHours()).toBe(0);
|
|
||||||
expect(date.getMinutes()).toBe(0);
|
|
||||||
expect(date.getSeconds()).toBe(0);
|
|
||||||
expect(date.getMilliseconds()).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Make Invalid Date valid again", () => {
|
|
||||||
let date = new Date(2021, 0, 1);
|
|
||||||
date.setMonth(NaN, 3, 16);
|
|
||||||
expect(date.getTime()).toBe(NaN);
|
|
||||||
|
|
||||||
date.setMonth(3, 16);
|
|
||||||
expect(date.getFullYear()).toBe(2021);
|
|
||||||
expect(date.getMonth()).toBe(3);
|
|
||||||
expect(date.getDate()).toBe(16);
|
|
||||||
expect(date.getHours()).toBe(0);
|
|
||||||
expect(date.getMinutes()).toBe(0);
|
|
||||||
expect(date.getSeconds()).toBe(0);
|
|
||||||
expect(date.getMilliseconds()).toBe(0);
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,12 +15,6 @@ describe("errors", () => {
|
||||||
}).toThrowWithMessage(TypeError, "Cannot convert BigInt to number");
|
}).toThrowWithMessage(TypeError, "Cannot convert BigInt to number");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("time value cannot be clipped", () => {
|
|
||||||
expect(() => {
|
|
||||||
new Date(-8.65e15).toLocaleDateString();
|
|
||||||
}).toThrowWithMessage(RangeError, "Time value must be between -8.64E15 and 8.64E15");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("timeStyle may not be specified", () => {
|
test("timeStyle may not be specified", () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
new Date().toLocaleDateString([], { timeStyle: "short" });
|
new Date().toLocaleDateString([], { timeStyle: "short" });
|
||||||
|
@ -34,6 +28,11 @@ describe("correct behavior", () => {
|
||||||
expect(d.toLocaleDateString()).toBe("Invalid Date");
|
expect(d.toLocaleDateString()).toBe("Invalid Date");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("time clip", () => {
|
||||||
|
const d = new Date(-8.65e15);
|
||||||
|
expect(d.toLocaleDateString()).toBe("Invalid Date");
|
||||||
|
});
|
||||||
|
|
||||||
const d0 = new Date(Date.UTC(2021, 11, 7, 17, 40, 50, 456));
|
const d0 = new Date(Date.UTC(2021, 11, 7, 17, 40, 50, 456));
|
||||||
const d1 = new Date(Date.UTC(1989, 0, 23, 7, 8, 9, 45));
|
const d1 = new Date(Date.UTC(1989, 0, 23, 7, 8, 9, 45));
|
||||||
|
|
||||||
|
|
|
@ -14,12 +14,6 @@ describe("errors", () => {
|
||||||
new Date(1n).toLocaleString();
|
new Date(1n).toLocaleString();
|
||||||
}).toThrowWithMessage(TypeError, "Cannot convert BigInt to number");
|
}).toThrowWithMessage(TypeError, "Cannot convert BigInt to number");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("time value cannot be clipped", () => {
|
|
||||||
expect(() => {
|
|
||||||
new Date(-8.65e15).toLocaleString();
|
|
||||||
}).toThrowWithMessage(RangeError, "Time value must be between -8.64E15 and 8.64E15");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("correct behavior", () => {
|
describe("correct behavior", () => {
|
||||||
|
@ -28,6 +22,11 @@ describe("correct behavior", () => {
|
||||||
expect(d.toLocaleString()).toBe("Invalid Date");
|
expect(d.toLocaleString()).toBe("Invalid Date");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("time clip", () => {
|
||||||
|
const d = new Date(-8.65e15);
|
||||||
|
expect(d.toLocaleString()).toBe("Invalid Date");
|
||||||
|
});
|
||||||
|
|
||||||
const d0 = new Date(Date.UTC(2021, 11, 7, 17, 40, 50, 456));
|
const d0 = new Date(Date.UTC(2021, 11, 7, 17, 40, 50, 456));
|
||||||
const d1 = new Date(Date.UTC(1989, 0, 23, 7, 8, 9, 45));
|
const d1 = new Date(Date.UTC(1989, 0, 23, 7, 8, 9, 45));
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,6 @@ describe("errors", () => {
|
||||||
}).toThrowWithMessage(TypeError, "Cannot convert BigInt to number");
|
}).toThrowWithMessage(TypeError, "Cannot convert BigInt to number");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("time value cannot be clipped", () => {
|
|
||||||
expect(() => {
|
|
||||||
new Date(-8.65e15).toLocaleTimeString();
|
|
||||||
}).toThrowWithMessage(RangeError, "Time value must be between -8.64E15 and 8.64E15");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("dateStyle may not be specified", () => {
|
test("dateStyle may not be specified", () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
new Date().toLocaleTimeString([], { dateStyle: "short" });
|
new Date().toLocaleTimeString([], { dateStyle: "short" });
|
||||||
|
@ -34,6 +28,11 @@ describe("correct behavior", () => {
|
||||||
expect(d.toLocaleTimeString()).toBe("Invalid Date");
|
expect(d.toLocaleTimeString()).toBe("Invalid Date");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("time clip", () => {
|
||||||
|
const d = new Date(-8.65e15);
|
||||||
|
expect(d.toLocaleTimeString()).toBe("Invalid Date");
|
||||||
|
});
|
||||||
|
|
||||||
const d0 = new Date(Date.UTC(2021, 11, 7, 17, 40, 50, 456));
|
const d0 = new Date(Date.UTC(2021, 11, 7, 17, 40, 50, 456));
|
||||||
const d1 = new Date(Date.UTC(1989, 0, 23, 7, 8, 9, 45));
|
const d1 = new Date(Date.UTC(1989, 0, 23, 7, 8, 9, 45));
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue