diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h index aaf297bf122..8b7a96a906e 100644 --- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h +++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h @@ -145,6 +145,7 @@ namespace JS { P(fromEntries) \ P(fromEpochMicroseconds) \ P(fromEpochMilliseconds) \ + P(fromEpochNanoseconds) \ P(fromEpochSeconds) \ P(fround) \ P(gc) \ diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/InstantConstructor.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/InstantConstructor.cpp index c708fe876bc..9472bdc00c1 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/InstantConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/InstantConstructor.cpp @@ -30,6 +30,7 @@ void InstantConstructor::initialize(GlobalObject& global_object) define_native_function(vm.names.fromEpochSeconds, from_epoch_seconds, 1, attr); define_native_function(vm.names.fromEpochMilliseconds, from_epoch_milliseconds, 1, attr); define_native_function(vm.names.fromEpochMicroseconds, from_epoch_microseconds, 1, attr); + define_native_function(vm.names.fromEpochNanoseconds, from_epoch_nanoseconds, 1, attr); define_direct_property(vm.names.length, Value(1), Attribute::Configurable); } @@ -139,4 +140,22 @@ JS_DEFINE_NATIVE_FUNCTION(InstantConstructor::from_epoch_microseconds) return create_temporal_instant(global_object, *epoch_nanoseconds); } +// 8.2.6 Temporal.Instant.fromEpochNanoseconds ( epochNanoseconds ) +JS_DEFINE_NATIVE_FUNCTION(InstantConstructor::from_epoch_nanoseconds) +{ + // 1. Set epochNanoseconds to ? ToBigInt(epochNanoseconds). + auto* epoch_nanoseconds = vm.argument(0).to_bigint(global_object); + if (vm.exception()) + return {}; + + // 2. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception. + if (!is_valid_epoch_nanoseconds(*epoch_nanoseconds)) { + vm.throw_exception(global_object, ErrorType::TemporalInvalidEpochNanoseconds); + return {}; + } + + // 3. Return ? CreateTemporalInstant(epochNanoseconds). + return create_temporal_instant(global_object, *epoch_nanoseconds); +} + } diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/InstantConstructor.h b/Userland/Libraries/LibJS/Runtime/Temporal/InstantConstructor.h index 1e9ce0e61fe..44f502d90bb 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/InstantConstructor.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/InstantConstructor.h @@ -27,6 +27,7 @@ private: JS_DECLARE_NATIVE_FUNCTION(from_epoch_seconds); JS_DECLARE_NATIVE_FUNCTION(from_epoch_milliseconds); JS_DECLARE_NATIVE_FUNCTION(from_epoch_microseconds); + JS_DECLARE_NATIVE_FUNCTION(from_epoch_nanoseconds); }; } diff --git a/Userland/Libraries/LibJS/Tests/builtins/Temporal/Instant/Instant.fromEpochNanoseconds.js b/Userland/Libraries/LibJS/Tests/builtins/Temporal/Instant/Instant.fromEpochNanoseconds.js new file mode 100644 index 00000000000..f2107caae83 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Temporal/Instant/Instant.fromEpochNanoseconds.js @@ -0,0 +1,51 @@ +describe("correct behavior", () => { + test("length is 1", () => { + expect(Temporal.Instant.fromEpochNanoseconds).toHaveLength(1); + }); + + test("basic functionality", () => { + expect(Temporal.Instant.fromEpochNanoseconds(0n).epochNanoseconds).toBe(0n); + expect(Temporal.Instant.fromEpochNanoseconds(1n).epochNanoseconds).toBe(1n); + expect(Temporal.Instant.fromEpochNanoseconds(999_999_999n).epochNanoseconds).toBe( + 999_999_999n + ); + expect( + Temporal.Instant.fromEpochNanoseconds(8_640_000_000_000_000_000_000n).epochNanoseconds + ).toBe(8_640_000_000_000_000_000_000n); + + expect(Temporal.Instant.fromEpochNanoseconds(-0n).epochNanoseconds).toBe(0n); + expect(Temporal.Instant.fromEpochNanoseconds(-1n).epochNanoseconds).toBe(-1n); + expect(Temporal.Instant.fromEpochNanoseconds(-999_999_999n).epochNanoseconds).toBe( + -999_999_999n + ); + expect( + Temporal.Instant.fromEpochNanoseconds(-8_640_000_000_000_000_000_000n).epochNanoseconds + ).toBe(-8_640_000_000_000_000_000_000n); + }); +}); + +test("errors", () => { + test("argument must be coercible to BigInt", () => { + expect(() => { + Temporal.Instant.fromEpochNanoseconds(123); + }).toThrowWithMessage(TypeError, "Cannot convert number to BigInt"); + expect(() => { + Temporal.Instant.fromEpochNanoseconds("foo"); + }).toThrowWithMessage(SyntaxError, "Invalid value for BigInt: foo"); + }); + + test("out-of-range epoch nanoseconds value", () => { + expect(() => { + Temporal.Instant.fromEpochNanoseconds(8_640_000_000_000_000_000_001n); + }).toThrowWithMessage( + RangeError, + "Invalid epoch nanoseconds value, must be in range -86400 * 10^17 to 86400 * 10^17" + ); + expect(() => { + Temporal.Instant.fromEpochNanoseconds(-8_640_000_000_000_000_000_001n); + }).toThrowWithMessage( + RangeError, + "Invalid epoch nanoseconds value, must be in range -86400 * 10^17 to 86400 * 10^17" + ); + }); +});