summaryrefslogtreecommitdiff
path: root/Userland/Libraries
diff options
context:
space:
mode:
Diffstat (limited to 'Userland/Libraries')
-rw-r--r--Userland/Libraries/LibJS/Runtime/Date.cpp28
-rw-r--r--Userland/Libraries/LibJS/Runtime/Date.h18
-rw-r--r--Userland/Libraries/LibJS/Runtime/DateConstructor.cpp180
-rw-r--r--Userland/Libraries/LibJS/Runtime/DatePrototype.cpp684
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setDate.js15
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setMonth.js24
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.toLocaleDateString.js11
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.toLocaleString.js11
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.toLocaleTimeString.js11
9 files changed, 499 insertions, 483 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/Date.cpp b/Userland/Libraries/LibJS/Runtime/Date.cpp
index 14683138a9..b3d59f1c3e 100644
--- a/Userland/Libraries/LibJS/Runtime/Date.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Date.cpp
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
+ * Copyright (c) 2022, Tim Flynn <trflynn89@pm.me>
*
* 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());
}
+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)
: Object(prototype)
, 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()
{
}
@@ -84,9 +96,7 @@ String Date::gmt_date_string() const
String Date::iso_date_string() const
{
- auto tm = to_utc_tm();
- int year = tm.tm_year + 1900;
- int month = tm.tm_mon + 1;
+ int year = year_from_time(m_date_value);
StringBuilder builder;
if (year < 0)
@@ -96,17 +106,17 @@ String Date::iso_date_string() const
else
builder.appendff("{:04}", year);
builder.append('-');
- builder.appendff("{:02}", month);
+ builder.appendff("{:02}", month_from_time(m_date_value) + 1);
builder.append('-');
- builder.appendff("{:02}", tm.tm_mday);
+ builder.appendff("{:02}", date_from_time(m_date_value));
builder.append('T');
- builder.appendff("{:02}", tm.tm_hour);
+ builder.appendff("{:02}", hour_from_time(m_date_value));
builder.append(':');
- builder.appendff("{:02}", tm.tm_min);
+ builder.appendff("{:02}", min_from_time(m_date_value));
builder.append(':');
- builder.appendff("{:02}", tm.tm_sec);
+ builder.appendff("{:02}", sec_from_time(m_date_value));
builder.append('.');
- builder.appendff("{:03}", m_milliseconds);
+ builder.appendff("{:03}", ms_from_time(m_date_value));
builder.append('Z');
return builder.build();
diff --git a/Userland/Libraries/LibJS/Runtime/Date.h b/Userland/Libraries/LibJS/Runtime/Date.h
index 1829bcbc48..7072ae4e06 100644
--- a/Userland/Libraries/LibJS/Runtime/Date.h
+++ b/Userland/Libraries/LibJS/Runtime/Date.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
+ * Copyright (c) 2022, Tim Flynn <trflynn89@pm.me>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -19,11 +20,16 @@ public:
static constexpr double time_clip = 8.64e15;
static Date* create(GlobalObject&, Core::DateTime, i16 milliseconds, bool is_invalid);
+ static Date* create(GlobalObject&, double date_value);
static Date* now(GlobalObject&);
Date(Core::DateTime datetime, i16 milliseconds, bool is_invalid, Object& prototype);
+ Date(double date_value, Object& prototype);
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; }
const Core::DateTime& datetime() const { return m_datetime; }
@@ -74,20 +80,14 @@ public:
String locale_string() const { return m_datetime.to_string(); }
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:
tm to_utc_tm() const;
+ double m_date_value { 0 }; // [[DateValue]]
+
Core::DateTime m_datetime;
i16 m_milliseconds;
+
bool m_is_invalid { false };
};
diff --git a/Userland/Libraries/LibJS/Runtime/DateConstructor.cpp b/Userland/Libraries/LibJS/Runtime/DateConstructor.cpp
index a49d74f9b5..7884376ba9 100644
--- a/Userland/Libraries/LibJS/Runtime/DateConstructor.cpp
+++ b/Userland/Libraries/LibJS/Runtime/DateConstructor.cpp
@@ -2,6 +2,7 @@
* Copyright (c) 2020, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2020, Nico Weber <thakis@chromium.org>
* Copyright (c) 2021, Petróczi Zoltán <petroczizoltan@tutanota.com>
+ * Copyright (c) 2022, Tim Flynn <trflynn89@pm.me>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -13,6 +14,7 @@
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/DateConstructor.h>
+#include <LibJS/Runtime/DatePrototype.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/VM.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
ThrowCompletionOr<Value> DateConstructor::call()
{
- auto [datetime, milliseconds] = JS::now();
- auto* date = Date::create(global_object(), datetime, milliseconds, false);
- return js_string(heap(), date->string());
+ // 1. If NewTarget is undefined, then
+ // a. Let now be the time value (UTC) identifying the current time.
+ 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
@@ -184,91 +175,94 @@ ThrowCompletionOr<Object*> DateConstructor::construct(FunctionObject& new_target
auto& vm = this->vm();
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) {
- auto [datetime, milliseconds] = JS::now();
- return TRY(ordinary_create_from_constructor<Date>(global_object, new_target, &GlobalObject::date_prototype, datetime, milliseconds, false));
+ // a. Let dv be the time value (UTC) identifying the current time.
+ auto now = AK::Time::now_realtime().to_milliseconds();
+ date_value = Value(static_cast<double>(now));
}
+ // 4. Else if numberOfArgs = 1, then
+ else if (vm.argument_count() == 1) {
+ // a. Let value be values[0].
+ auto value = vm.argument(0);
+ Value time_value;
- auto create_invalid_date = [&global_object, &new_target]() -> ThrowCompletionOr<Date*> {
- auto datetime = Core::DateTime::create(1970, 1, 1, 0, 0, 0);
- auto milliseconds = static_cast<i16>(0);
- return ordinary_create_from_constructor<Date>(global_object, new_target, &GlobalObject::date_prototype, datetime, milliseconds, true);
- };
+ // b. If Type(value) is Object and value has a [[DateValue]] internal slot, then
+ 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));
+
+ // ii. If Type(v) is String, then
+ if (primitive.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).
+ time_value = parse_date_string(global_object, primitive.as_string().string());
+ }
+ // iii. Else,
+ else {
+ // 1. Let tv be ? ToNumber(v).
+ time_value = TRY(primitive.to_number(global_object));
+ }
+ }
- if (vm.argument_count() == 1) {
- auto value = vm.argument(0);
- if (value.is_string())
- value = parse_date_string(global_object, value.as_string().string());
- else
- value = TRY(value.to_number(global_object));
-
- if (!value.is_finite_number())
- return TRY(create_invalid_date());
-
- // A timestamp since the epoch, in UTC.
- double value_as_double = value.as_double();
- if (value_as_double > Date::time_clip)
- return TRY(create_invalid_date());
- auto datetime = Core::DateTime::from_timestamp(static_cast<time_t>(value_as_double / 1000));
- 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));
+ // 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);
+ }
- // A date/time in components, in local time.
- 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);
- };
+ // 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);
- 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;
+ // l. Let dv be TimeClip(UTC(finalDate)).
+ date_value = time_clip(global_object, Value(utc_time(final_date.as_double())));
}
- 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));
+ // 6. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%Date.prototype%", « [[DateValue]] »).
+ // 7. Set O.[[DateValue]] to dv.
+ // 8. Return O.
+ return TRY(ordinary_create_from_constructor<Date>(global_object, new_target, &GlobalObject::date_prototype, date_value.as_double()));
}
// 21.4.3.1 Date.now ( ), https://tc39.es/ecma262/#sec-date.now
diff --git a/Userland/Libraries/LibJS/Runtime/DatePrototype.cpp b/Userland/Libraries/LibJS/Runtime/DatePrototype.cpp
index 5af3da296e..713d2f4243 100644
--- a/Userland/Libraries/LibJS/Runtime/DatePrototype.cpp
+++ b/Userland/Libraries/LibJS/Runtime/DatePrototype.cpp
@@ -2,6 +2,7 @@
* Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2021, Petróczi Zoltán <petroczizoltan@tutanota.com>
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
+ * Copyright (c) 2022, Tim Flynn <trflynn89@pm.me>
*
* 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
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())
- return js_nan();
+ // 2. If t is NaN, return 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
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())
- return js_nan();
+ // 2. If t is NaN, return 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
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())
- return js_nan();
+ // 2. If t is NaN, return 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
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())
- return js_nan();
+ // 2. If t is NaN, return 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
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())
- return js_nan();
+ // 2. If t is NaN, return 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
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())
- return js_nan();
+ // 2. If t is NaN, return 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
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())
- return js_nan();
+ // 2. If t is NaN, return 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
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())
- return js_nan();
+ // 2. If t is NaN, return 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
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_time)
{
- auto* this_object = TRY(typed_this_object(global_object));
-
- if (this_object->is_invalid())
- return js_nan();
-
- return Value(this_object->time());
+ // 1. Return ? thisTimeValue(this value).
+ return this_time_value(global_object, vm.this_value(global_object));
}
// 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));
- if (this_object->is_invalid())
+ if (!Value(this_object->date_value()).is_finite_number())
return js_nan();
// 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
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())
- return js_nan();
+ // 2. If t is NaN, return 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
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())
- return js_nan();
+ // 2. If t is NaN, return 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
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())
- return js_nan();
+ // 2. If t is NaN, return 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
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())
- return js_nan();
+ // 2. If t is NaN, return 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
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())
- return js_nan();
+ // 2. If t is NaN, return 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
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())
- return js_nan();
+ // 2. If t is NaN, return 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
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())
- return js_nan();
+ // 2. If t is NaN, return 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
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())
- return js_nan();
+ // 2. If t is NaN, return NaN.
+ if (time.is_nan())
+ return time;
+
+ // 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(this_object->utc_seconds());
+ return Value(fallback);
}
// 21.4.4.20 Date.prototype.setDate ( date ), https://tc39.es/ecma262/#sec-date.prototype.setdate
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));
- if (!new_date_value.is_finite_number()) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
- auto new_date = new_date_value.as_i32();
+ // 3. Let newDate be MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t)).
+ auto year = Value(year_from_time(time));
+ auto month = Value(month_from_time(time));
- datetime.set_time(datetime.year(), datetime.month(), new_date, datetime.hour(), datetime.minute(), datetime.second());
- if (this_object->time() > Date::time_clip) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
+ auto day = make_day(global_object, year, month, date);
+ auto new_date = make_date(day, Value(time_within_day(time)));
+
+ // 4. Let u be TimeClip(UTC(newDate)).
+ new_date = time_clip(global_object, Value(utc_time(new_date.as_double())));
- this_object->set_is_invalid(false);
- return Value(this_object->time());
+ // 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
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> {
- return vm.argument_count() > i ? vm.argument(i).to_number(global_object) : Value(fallback);
- };
+ // 3. Let y be ? ToNumber(year).
+ auto year = TRY(vm.argument(0).to_number(global_object));
- auto new_year_value = TRY(vm.argument(0).to_number(global_object));
- if (!new_year_value.is_finite_number()) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
- auto new_year = new_year_value.as_i32();
+ // 4. If month is not present, let m be MonthFromTime(t); otherwise, let m be ? ToNumber(month).
+ auto month = TRY(argument_or_value(global_object, 1, month_from_time(time)));
- auto new_month_value = TRY(arg_or(1, datetime.month() - 1));
- if (!new_month_value.is_finite_number()) {
- 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
+ // 5. If date is not present, let dt be DateFromTime(t); otherwise, let dt be ? ToNumber(date).
+ auto date = TRY(argument_or_value(global_object, 2, date_from_time(time)));
- auto new_day_value = TRY(arg_or(2, datetime.day()));
- if (!new_day_value.is_finite_number()) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
- auto new_day = new_day_value.as_i32();
+ // 6. Let newDate be MakeDate(MakeDay(y, m, dt), TimeWithinDay(t)).
+ auto day = make_day(global_object, year, month, date);
+ auto new_date = make_date(day, Value(time_within_day(time)));
- datetime.set_time(new_year, new_month, new_day, datetime.hour(), datetime.minute(), datetime.second());
- if (this_object->time() > Date::time_clip) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
+ // 7. Let u be TimeClip(UTC(newDate)).
+ new_date = time_clip(global_object, Value(utc_time(new_date.as_double())));
- 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
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> {
- return vm.argument_count() > i ? vm.argument(i).to_number(global_object) : Value(fallback);
- };
+ // 2. Let h be ? ToNumber(hour).
+ 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));
- if (!new_hours_value.is_finite_number()) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
- auto new_hours = new_hours_value.as_i32();
+ // 4. If sec is not present, let s be SecFromTime(t); otherwise, let s be ? ToNumber(sec).
+ auto second = TRY(argument_or_value(global_object, 2, sec_from_time(time)));
- auto new_minutes_value = TRY(arg_or(1, datetime.minute()));
- if (!new_minutes_value.is_finite_number()) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
- auto new_minutes = new_minutes_value.as_i32();
+ // 5. If ms is not present, let milli be msFromTime(t); otherwise, let milli be ? ToNumber(ms).
+ auto millisecond = TRY(argument_or_value(global_object, 3, ms_from_time(time)));
- auto new_seconds_value = TRY(arg_or(2, datetime.second()));
- if (!new_seconds_value.is_finite_number()) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
- auto new_seconds = new_seconds_value.as_i32();
-
- auto new_milliseconds_value = TRY(arg_or(3, this_object->milliseconds()));
- if (!new_milliseconds_value.is_finite_number()) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
- auto new_milliseconds = new_milliseconds_value.as_i32();
+ // 6. Let date be MakeDate(Day(t), MakeTime(h, m, s, milli)).
+ auto new_time = make_time(global_object, hour, minute, second, millisecond);
+ auto date = make_date(Value(day(time)), new_time);
- new_seconds += new_milliseconds / 1000;
- this_object->set_milliseconds(new_milliseconds % 1000);
+ // 7. Let u be TimeClip(UTC(date)).
+ date = time_clip(global_object, Value(utc_time(date.as_double())));
- datetime.set_time(datetime.year(), datetime.month(), datetime.day(), new_hours, new_minutes, new_seconds);
- if (this_object->time() > Date::time_clip) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
+ // 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(date.as_double());
- this_object->set_is_invalid(false);
- return Value(this_object->time());
+ // 9. Return u.
+ return date;
}
// 21.4.4.23 Date.prototype.setMilliseconds ( ms ), https://tc39.es/ecma262/#sec-date.prototype.setmilliseconds
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_milliseconds)
{
- auto* this_object = TRY(typed_this_object(global_object));
-
- auto new_milliseconds_value = TRY(vm.argument(0).to_number(global_object));
-
- if (!new_milliseconds_value.is_finite_number()) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
+ // 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 = new_milliseconds_value.as_i32();
+ // 2. Set ms to ? ToNumber(ms).
+ auto millisecond = TRY(vm.argument(0).to_number(global_object));
- this_object->set_milliseconds(new_milliseconds % 1000);
+ // 3. Let time be MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms).
+ auto hour = Value(hour_from_time(time));
+ auto minute = Value(min_from_time(time));
+ auto second = Value(sec_from_time(time));
- auto added_seconds = new_milliseconds / 1000;
- if (added_seconds > 0) {
- auto& datetime = this_object->datetime();
- datetime.set_time(datetime.year(), datetime.month(), datetime.day(), datetime.hour(), datetime.minute(), datetime.second() + added_seconds);
- }
+ auto new_time = make_time(global_object, hour, minute, second, millisecond);
- if (this_object->time() > Date::time_clip) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
+ // 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())));
- this_object->set_is_invalid(false);
+ // 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(date.as_double());
- return Value(this_object->time());
+ // 6. Return u.
+ return date;
}
// 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)
{
- 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> {
- return vm.argument_count() > i ? vm.argument(i).to_number(global_object) : Value(fallback);
- };
+ // 2. Let m be ? ToNumber(min).
+ 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));
- if (!new_minutes_value.is_finite_number()) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
- auto new_minutes = new_minutes_value.as_i32();
+ // 4. If ms is not present, let milli be msFromTime(t); otherwise, let milli be ? ToNumber(ms).
+ auto millisecond = TRY(argument_or_value(global_object, 2, ms_from_time(time)));
- auto new_seconds_value = TRY(arg_or(1, datetime.second()));
- if (!new_seconds_value.is_finite_number()) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
- auto new_seconds = new_seconds_value.as_i32();
+ // 5. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)).
+ auto hour = Value(hour_from_time(time));
- auto new_milliseconds_value = TRY(arg_or(2, this_object->milliseconds()));
- if (!new_milliseconds_value.is_finite_number()) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
- auto new_milliseconds = new_milliseconds_value.as_i32();
+ auto new_time = make_time(global_object, hour, minute, second, millisecond);
+ auto date = make_date(Value(day(time)), new_time);
- new_seconds += new_milliseconds / 1000;
- this_object->set_milliseconds(new_milliseconds % 1000);
+ // 6. Let u be TimeClip(UTC(date)).
+ 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);
- if (this_object->time() > Date::time_clip) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
+ // 7. 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(date.as_double());
- this_object->set_is_invalid(false);
- return Value(this_object->time());
+ // 8. Return u.
+ return date;
}
// 21.4.4.25 Date.prototype.setMonth ( month [ , date ] ), https://tc39.es/ecma262/#sec-date.prototype.setmonth
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> {
- return vm.argument_count() > i ? vm.argument(i).to_number(global_object) : Value(fallback);
- };
+ // 2. Let m be ? ToNumber(month).
+ 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));
- if (!new_month_value.is_finite_number()) {
- 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
+ // 4. Let newDate be MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t)).
+ auto year = Value(year_from_time(time));
- auto new_date_value = TRY(arg_or(1, this_object->date()));
- if (!new_date_value.is_finite_number()) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
- auto new_date = new_date_value.as_i32();
+ auto day = make_day(global_object, year, month, date);
+ auto new_date = make_date(day, Value(time_within_day(time)));
- datetime.set_time(datetime.year(), new_month, new_date, datetime.hour(), datetime.minute(), datetime.second());
- if (this_object->time() > Date::time_clip) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
+ // 5. Let u be TimeClip(UTC(newDate)).
+ new_date = time_clip(global_object, Value(utc_time(new_date.as_double())));
- this_object->set_is_invalid(false);
- return Value(this_object->time());
+ // 6. 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());
+
+ // 7. Return u.
+ return new_date;
}
// 21.4.4.26 Date.prototype.setSeconds ( sec [ , ms ] ), https://tc39.es/ecma262/#sec-date.prototype.setseconds
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> {
- return vm.argument_count() > i ? vm.argument(i).to_number(global_object) : Value(fallback);
- };
+ // 2. Let s be ? ToNumber(sec).
+ 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));
- if (!new_seconds_value.is_finite_number()) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
- auto new_seconds = new_seconds_value.as_i32();
+ // 4. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli)).
+ auto hour = Value(hour_from_time(time));
+ auto minute = Value(min_from_time(time));
- auto new_milliseconds_value = TRY(arg_or(1, this_object->milliseconds()));
- if (!new_milliseconds_value.is_finite_number()) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
- auto new_milliseconds = new_milliseconds_value.as_i32();
+ auto new_time = make_time(global_object, hour, minute, second, millisecond);
+ auto new_date = make_date(Value(day(time)), new_time);
- new_seconds += new_milliseconds / 1000;
- this_object->set_milliseconds(new_milliseconds % 1000);
+ // 5. Let u be TimeClip(UTC(date)).
+ 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);
- if (this_object->time() > Date::time_clip) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
+ // 6. 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());
- this_object->set_is_invalid(false);
- return Value(this_object->time());
+ // 7. Return u.
+ return new_date;
}
// 21.4.4.27 Date.prototype.setTime ( time ), https://tc39.es/ecma262/#sec-date.prototype.settime
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));
- if (!new_time_value.is_finite_number()) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
- auto new_time = new_time_value.as_double();
+ // 2. Let t be ? ToNumber(time).
+ time = TRY(vm.argument(0).to_number(global_object));
- if (new_time > Date::time_clip) {
- this_object->set_is_invalid(true);
- return js_nan();
- }
+ // 3. Let v be TimeClip(t).
+ time = time_clip(global_object, time);
- auto new_date_time = Core::DateTime::from_timestamp(static_cast<time_t>(new_time / 1000));
- 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());
- this_object->set_milliseconds(static_cast<i16>(fmod(new_time, 1000)));
+ // 4. Set the [[DateValue]] internal slot of this Date object to v.
+ auto* this_object = MUST(typed_this_object(global_object));
+ this_object->set_date_value(time.as_double());
- this_object->set_is_invalid(false);
- return Value(this_object->time());
+ // 5. Return v.
+ return time;
}
// 21.4.4.35 Date.prototype.toDateString ( ), https://tc39.es/ecma262/#sec-date.prototype.todatestring
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())
- return js_string(vm, "Invalid Date");
+ // 3. If tv is NaN, return "Invalid Date".
+ if (time.is_nan())
+ return js_string(vm, "Invalid Date"sv);
- auto string = this_object->date_string();
- return js_string(vm, move(string));
+ // 4. Let t be LocalTime(tv).
+ // 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
@@ -632,7 +638,7 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_iso_string)
{
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);
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
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_string)
{
- auto* this_object = TRY(typed_this_object(global_object));
-
- if (this_object->is_invalid())
- return js_string(vm, "Invalid Date");
+ // 1. Let tv be ? thisTimeValue(this value).
+ auto time = TRY(this_time_value(global_object, vm.this_value(global_object)));
- auto string = this_object->string();
- return js_string(vm, move(string));
+ // 2. Return ToDateString(tv).
+ return js_string(vm, JS::to_date_string(time.as_double()));
}
// 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
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())
- return js_string(vm, "Invalid Date");
+ // 3. If tv is NaN, return "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));
}
// 21.4.4.43 Date.prototype.toUTCString ( ), https://tc39.es/ecma262/#sec-date.prototype.toutcstring
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())
- return js_string(vm, "Invalid Date");
+ // 3. If tv is NaN, return "Invalid Date".
+ if (time.is_nan())
+ return js_string(vm, "Invalid Date"sv);
- // HTTP dates are always expressed in GMT.
- auto string = this_object->gmt_date_string();
+ // 4. Let weekday be the Name of the entry in Table 62 with the Number WeekDay(tv).
+ 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));
}
@@ -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
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())
- return js_nan();
+ // 2. If t is NaN, return 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
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));
- if (!new_year_value.is_finite_number()) {
- 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;
+ // 3. Let y be ? ToNumber(year).
+ auto year = TRY(vm.argument(0).to_number(global_object));
- datetime.set_time(new_year, datetime.month(), datetime.day(), datetime.hour(), datetime.minute(), datetime.second());
- if (this_object->time() > Date::time_clip) {
- this_object->set_is_invalid(true);
- return js_nan();
+ auto* this_object = MUST(typed_this_object(global_object));
+
+ // 4. If y is NaN, then
+ 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));
+
+ // 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());
- return Value(this_object->time());
+ // 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
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setDate.js b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setDate.js
index b8cd57710f..fc7807eaa4 100644
--- a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setDate.js
+++ b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setDate.js
@@ -30,18 +30,3 @@ test("Day as argument", () => {
expect(date.getSeconds()).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);
-});
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setMonth.js b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setMonth.js
index a7ffee3c01..1ae040d833 100644
--- a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setMonth.js
+++ b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setMonth.js
@@ -60,28 +60,4 @@ test("NaN or undefined in any arguments", () => {
date = new Date(2021, 0, 1);
date.setMonth(2021, undefined);
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);
});
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.toLocaleDateString.js b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.toLocaleDateString.js
index 8eb8c324b2..387f5b0291 100644
--- a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.toLocaleDateString.js
+++ b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.toLocaleDateString.js
@@ -15,12 +15,6 @@ describe("errors", () => {
}).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", () => {
expect(() => {
new Date().toLocaleDateString([], { timeStyle: "short" });
@@ -34,6 +28,11 @@ describe("correct behavior", () => {
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 d1 = new Date(Date.UTC(1989, 0, 23, 7, 8, 9, 45));
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.toLocaleString.js b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.toLocaleString.js
index dc76c7f6a9..bb2bc638be 100644
--- a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.toLocaleString.js
+++ b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.toLocaleString.js
@@ -14,12 +14,6 @@ describe("errors", () => {
new Date(1n).toLocaleString();
}).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", () => {
@@ -28,6 +22,11 @@ describe("correct behavior", () => {
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 d1 = new Date(Date.UTC(1989, 0, 23, 7, 8, 9, 45));
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.toLocaleTimeString.js b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.toLocaleTimeString.js
index 6d85632f8e..d188994039 100644
--- a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.toLocaleTimeString.js
+++ b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.toLocaleTimeString.js
@@ -15,12 +15,6 @@ describe("errors", () => {
}).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", () => {
expect(() => {
new Date().toLocaleTimeString([], { dateStyle: "short" });
@@ -34,6 +28,11 @@ describe("correct behavior", () => {
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 d1 = new Date(Date.UTC(1989, 0, 23, 7, 8, 9, 45));