Bläddra i källkod

LibJS: Implement Temporal.PlainTime.prototype.toZonedDateTime()

Linus Groh 3 år sedan
förälder
incheckning
330ac1e5ad

+ 53 - 0
Userland/Libraries/LibJS/Runtime/Temporal/PlainTimePrototype.cpp

@@ -9,10 +9,13 @@
 #include <LibJS/Runtime/GlobalObject.h>
 #include <LibJS/Runtime/Temporal/Calendar.h>
 #include <LibJS/Runtime/Temporal/Duration.h>
+#include <LibJS/Runtime/Temporal/Instant.h>
 #include <LibJS/Runtime/Temporal/PlainDate.h>
 #include <LibJS/Runtime/Temporal/PlainDateTime.h>
 #include <LibJS/Runtime/Temporal/PlainTime.h>
 #include <LibJS/Runtime/Temporal/PlainTimePrototype.h>
+#include <LibJS/Runtime/Temporal/TimeZone.h>
+#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
 
 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<TypeError>(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<TypeError>(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<TypeError>(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)
 {

+ 1 - 0
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);

+ 55 - 0
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");
+    });
+});