diff --git a/Userland/Libraries/LibJS/Runtime/DatePrototype.cpp b/Userland/Libraries/LibJS/Runtime/DatePrototype.cpp index 2f64d16946..f9f8961c6e 100644 --- a/Userland/Libraries/LibJS/Runtime/DatePrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/DatePrototype.cpp @@ -72,8 +72,8 @@ void DatePrototype::initialize(GlobalObject& global_object) define_native_function(vm.names.setMonth, set_month, 2, attr); define_native_function(vm.names.setSeconds, set_seconds, 2, attr); define_native_function(vm.names.setTime, set_time, 1, attr); - define_native_function(vm.names.setUTCDate, set_date, 1, attr); // FIXME: This is a hack, Serenity doesn't currently support timezones other than UTC. - define_native_function(vm.names.setUTCFullYear, set_full_year, 3, attr); // FIXME: see above + define_native_function(vm.names.setUTCDate, set_date, 1, attr); // FIXME: This is a hack, Serenity doesn't currently support timezones other than UTC. + define_native_function(vm.names.setUTCFullYear, set_utc_full_year, 3, attr); define_native_function(vm.names.setUTCHours, set_utc_hours, 4, attr); define_native_function(vm.names.setUTCMilliseconds, set_utc_milliseconds, 1, attr); define_native_function(vm.names.setUTCMinutes, set_utc_minutes, 3, attr); @@ -619,6 +619,41 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_time) return time; } +// 21.4.4.29 Date.prototype.setUTCFullYear ( year [ , month [ , date ] ] ), https://tc39.es/ecma262/#sec-date.prototype.setutcfullyear +JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_utc_full_year) +{ + // 1. Let t be ? thisTimeValue(this value). + auto this_time = TRY(this_time_value(global_object, vm.this_value(global_object))); + + // 2. If t is NaN, set t to +0𝔽. + double time = 0; + if (!this_time.is_nan()) + time = this_time.as_double(); + + // 3. Let y be ? ToNumber(year). + auto year = 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). + auto month = TRY(argument_or_value(global_object, 1, month_from_time(time))); + + // 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))); + + // 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))); + + // 7. Let v be TimeClip(newDate). + new_date = time_clip(global_object, new_date); + + // 8. 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(new_date.as_double()); + + // 9. Return v. + return new_date; +} + // 21.4.4.30 Date.prototype.setUTCHours ( hour [ , min [ , sec [ , ms ] ] ] ), https://tc39.es/ecma262/#sec-date.prototype.setutchours JS_DEFINE_NATIVE_FUNCTION(DatePrototype::set_utc_hours) { diff --git a/Userland/Libraries/LibJS/Runtime/DatePrototype.h b/Userland/Libraries/LibJS/Runtime/DatePrototype.h index 5005ef4570..87df1305fb 100644 --- a/Userland/Libraries/LibJS/Runtime/DatePrototype.h +++ b/Userland/Libraries/LibJS/Runtime/DatePrototype.h @@ -46,6 +46,7 @@ private: JS_DECLARE_NATIVE_FUNCTION(set_month); JS_DECLARE_NATIVE_FUNCTION(set_seconds); JS_DECLARE_NATIVE_FUNCTION(set_time); + JS_DECLARE_NATIVE_FUNCTION(set_utc_full_year); JS_DECLARE_NATIVE_FUNCTION(set_utc_hours); JS_DECLARE_NATIVE_FUNCTION(set_utc_milliseconds); JS_DECLARE_NATIVE_FUNCTION(set_utc_minutes); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setUTCFullYear.js b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setUTCFullYear.js new file mode 100644 index 0000000000..7c6dd79787 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Date/Date.prototype.setUTCFullYear.js @@ -0,0 +1,55 @@ +describe("errors", () => { + test("called on non-Date object", () => { + expect(() => { + Date.prototype.setUTCFullYear(); + }).toThrowWithMessage(TypeError, "Not an object of type Date"); + }); + + test("called with non-numeric parameters", () => { + expect(() => { + new Date().setUTCFullYear(Symbol.hasInstance); + }).toThrowWithMessage(TypeError, "Cannot convert symbol to number"); + + expect(() => { + new Date().setUTCFullYear(1989, Symbol.hasInstance); + }).toThrowWithMessage(TypeError, "Cannot convert symbol to number"); + + expect(() => { + new Date().setUTCFullYear(1989, 0, Symbol.hasInstance); + }).toThrowWithMessage(TypeError, "Cannot convert symbol to number"); + }); +}); + +describe("correct behavior", () => { + const d = new Date(2000, 2, 1); + + test("basic functionality", () => { + d.setUTCFullYear(1989); + expect(d.getUTCFullYear()).toBe(1989); + + d.setUTCFullYear(1990, 1); + expect(d.getUTCFullYear()).toBe(1990); + expect(d.getUTCMonth()).toBe(1); + + d.setUTCFullYear(1991, 2, 15); + expect(d.getUTCFullYear()).toBe(1991); + expect(d.getUTCMonth()).toBe(2); + expect(d.getUTCDate()).toBe(15); + + d.setUTCFullYear(""); + expect(d.getUTCFullYear()).toBe(0); + + d.setUTCFullYear("a"); + expect(d.getUTCFullYear()).toBe(NaN); + }); + + test("NaN", () => { + d.setUTCFullYear(NaN); + expect(d.getUTCFullYear()).toBeNaN(); + }); + + test("time clip", () => { + d.setUTCFullYear(8.65e15); + expect(d.getUTCFullYear()).toBeNaN(); + }); +});