From 330ac1e5ad47857564e31f3fef2227e2c951c778 Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Tue, 2 Nov 2021 17:47:58 +0100 Subject: [PATCH] LibJS: Implement Temporal.PlainTime.prototype.toZonedDateTime() --- .../Runtime/Temporal/PlainTimePrototype.cpp | 53 ++++++++++++++++++ .../Runtime/Temporal/PlainTimePrototype.h | 1 + .../PlainTime.prototype.toZonedDateTime.js | 55 +++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainTime/PlainTime.prototype.toZonedDateTime.js diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTimePrototype.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTimePrototype.cpp index fc7fbffb87..b5badab3fe 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTimePrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTimePrototype.cpp @@ -9,10 +9,13 @@ #include #include #include +#include #include #include #include #include +#include +#include namespace JS::Temporal { @@ -45,6 +48,7 @@ void PlainTimePrototype::initialize(GlobalObject& global_object) define_native_function(vm.names.with, with, 1, attr); define_native_function(vm.names.equals, equals, 1, attr); define_native_function(vm.names.toPlainDateTime, to_plain_date_time, 1, attr); + define_native_function(vm.names.toZonedDateTime, to_zoned_date_time, 1, attr); define_native_function(vm.names.getISOFields, get_iso_fields, 0, attr); define_native_function(vm.names.toString, to_string, 0, attr); define_native_function(vm.names.toLocaleString, to_locale_string, 0, attr); @@ -315,6 +319,55 @@ JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::to_plain_date_time) return TRY(create_temporal_date_time(global_object, temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day(), temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond(), temporal_date->calendar())); } +// 4.3.18 Temporal.PlainTime.prototype.toZonedDateTime ( item ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.tozoneddatetime +JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::to_zoned_date_time) +{ + auto item = vm.argument(0); + + // 1. Let temporalTime be the this value. + // 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]). + auto* temporal_time = TRY(typed_this_object(global_object)); + + // 3. If Type(item) is not Object, then + if (!item.is_object()) { + // a. Throw a TypeError exception. + return vm.throw_completion(global_object, ErrorType::NotAnObject, item); + } + + // 4. Let temporalDateLike be ? Get(item, "plainDate"). + auto temporal_date_like = TRY(item.as_object().get(vm.names.plainDate)); + + // 5. If temporalDateLike is undefined, then + if (temporal_date_like.is_undefined()) { + // a. Throw a TypeError exception. + return vm.throw_completion(global_object, ErrorType::MissingRequiredProperty, vm.names.plainDate.as_string()); + } + + // 6. Let temporalDate be ? ToTemporalDate(temporalDateLike). + auto* temporal_date = TRY(to_temporal_date(global_object, temporal_date_like)); + + // 7. Let temporalTimeZoneLike be ? Get(item, "timeZone"). + auto temporal_time_zone_like = TRY(item.as_object().get(vm.names.timeZone)); + + // 8. If temporalTimeZoneLike is undefined, then + if (temporal_time_zone_like.is_undefined()) { + // a. Throw a TypeError exception. + return vm.throw_completion(global_object, ErrorType::MissingRequiredProperty, vm.names.timeZone.as_string()); + } + + // 9. Let timeZone be ? ToTemporalTimeZone(temporalTimeZoneLike). + auto* time_zone = TRY(to_temporal_time_zone(global_object, temporal_time_zone_like)); + + // 10. Let temporalDateTime be ? CreateTemporalDateTime(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]], temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], temporalDate.[[Calendar]]). + auto* temporal_date_time = TRY(create_temporal_date_time(global_object, temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day(), temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond(), temporal_date->calendar())); + + // 11. Let instant be ? BuiltinTimeZoneGetInstantFor(timeZone, temporalDateTime, "compatible"). + auto* instant = TRY(builtin_time_zone_get_instant_for(global_object, time_zone, *temporal_date_time, "compatible"sv)); + + // 12. Return ! CreateTemporalZonedDateTime(instant.[[Nanoseconds]], timeZone, temporalDate.[[Calendar]]). + return MUST(create_temporal_zoned_date_time(global_object, instant->nanoseconds(), *time_zone, temporal_date->calendar())); +} + // 4.3.19 Temporal.PlainTime.prototype.getISOFields ( ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.getisofields JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::get_iso_fields) { diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTimePrototype.h b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTimePrototype.h index c08f7dcaa9..4f091c508f 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTimePrototype.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTimePrototype.h @@ -32,6 +32,7 @@ private: JS_DECLARE_NATIVE_FUNCTION(with); JS_DECLARE_NATIVE_FUNCTION(equals); JS_DECLARE_NATIVE_FUNCTION(to_plain_date_time); + JS_DECLARE_NATIVE_FUNCTION(to_zoned_date_time); JS_DECLARE_NATIVE_FUNCTION(get_iso_fields); JS_DECLARE_NATIVE_FUNCTION(to_string); JS_DECLARE_NATIVE_FUNCTION(to_locale_string); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainTime/PlainTime.prototype.toZonedDateTime.js b/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainTime/PlainTime.prototype.toZonedDateTime.js new file mode 100644 index 0000000000..ebbc50937b --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainTime/PlainTime.prototype.toZonedDateTime.js @@ -0,0 +1,55 @@ +describe("correct behavior", () => { + test("length is 1", () => { + expect(Temporal.PlainTime.prototype.toZonedDateTime).toHaveLength(1); + }); + + test("basic functionality", () => { + const plainTime = new Temporal.PlainTime(18, 14, 47, 123, 456, 789); + const plainDate = new Temporal.PlainDate(2021, 7, 6); + const timeZone = new Temporal.TimeZone("UTC"); + const zonedDateTime = plainTime.toZonedDateTime({ plainDate, timeZone }); + expect(zonedDateTime.year).toBe(2021); + expect(zonedDateTime.month).toBe(7); + expect(zonedDateTime.day).toBe(6); + expect(zonedDateTime.hour).toBe(18); + expect(zonedDateTime.minute).toBe(14); + expect(zonedDateTime.second).toBe(47); + expect(zonedDateTime.millisecond).toBe(123); + expect(zonedDateTime.microsecond).toBe(456); + expect(zonedDateTime.nanosecond).toBe(789); + expect(zonedDateTime.calendar).toBe(plainDate.calendar); + expect(zonedDateTime.timeZone).toBe(timeZone); + }); +}); + +describe("errors", () => { + test("this value must be a Temporal.PlainTime object", () => { + expect(() => { + Temporal.PlainTime.prototype.toZonedDateTime.call("foo"); + }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainTime"); + }); + + test("item argument must be an object", () => { + const plainTime = new Temporal.PlainTime(); + for (const value of [123, NaN, Infinity, true, false, null, undefined]) { + expect(() => { + plainTime.toZonedDateTime(value); + }).toThrowWithMessage(TypeError, `${value} is not an object`); + } + }); + + test("item argument must have a 'plainDate' property", () => { + const plainTime = new Temporal.PlainTime(); + expect(() => { + plainTime.toZonedDateTime({}); + }).toThrowWithMessage(TypeError, "Required property plainDate is missing or undefined"); + }); + + test("item argument must have a 'timeZone' property", () => { + const plainDate = new Temporal.PlainDate(1970, 1, 1); + const plainTime = new Temporal.PlainTime(); + expect(() => { + plainTime.toZonedDateTime({ plainDate }); + }).toThrowWithMessage(TypeError, "Required property timeZone is missing or undefined"); + }); +});