From e9cbeeac457b9684c31f0b35e7fcd424bf1e8c30 Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Sat, 30 Oct 2021 10:26:42 +0200 Subject: [PATCH] LibJS: Implement Temporal.TimeZone.prototype.getNextTransition() --- .../LibJS/Runtime/CommonPropertyNames.h | 1 + .../LibJS/Runtime/Temporal/TimeZone.cpp | 9 +++++++ .../LibJS/Runtime/Temporal/TimeZone.h | 1 + .../Runtime/Temporal/TimeZonePrototype.cpp | 26 +++++++++++++++++++ .../Runtime/Temporal/TimeZonePrototype.h | 1 + .../TimeZone.prototype.getNextTransition.js | 25 ++++++++++++++++++ 6 files changed, 63 insertions(+) create mode 100644 Userland/Libraries/LibJS/Tests/builtins/Temporal/TimeZone/TimeZone.prototype.getNextTransition.js diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h index f59917044a0..ee32feb0799 100644 --- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h +++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h @@ -205,6 +205,7 @@ namespace JS { P(getMilliseconds) \ P(getMinutes) \ P(getMonth) \ + P(getNextTransition) \ P(getOffsetNanosecondsFor) \ P(getOffsetStringFor) \ P(getOwnPropertyDescriptor) \ diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.cpp index 13205ebd06a..1703359caa7 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.cpp @@ -177,6 +177,15 @@ i64 get_iana_time_zone_offset_nanoseconds([[maybe_unused]] BigInt const& epoch_n return 0; } +// 11.6.6 GetIANATimeZoneNextTransition ( epochNanoseconds, timeZoneIdentifier ), https://tc39.es/proposal-temporal/#sec-temporal-getianatimezonenexttransition +BigInt* get_iana_time_zone_next_transition(GlobalObject&, [[maybe_unused]] BigInt const& epoch_nanoseconds, [[maybe_unused]] StringView time_zone_identifier) +{ + // The abstract operation GetIANATimeZoneNextTransition is an implementation-defined algorithm that returns an integer representing the number of nanoseconds since the Unix epoch in UTC that corresponds to the first time zone transition after epochNanoseconds in the IANA time zone identified by timeZoneIdentifier or null if no such transition exists. + + // TODO: Implement this + return nullptr; +} + // https://tc39.es/proposal-temporal/#prod-TimeZoneNumericUTCOffset static bool parse_time_zone_numeric_utc_offset_syntax(String const& offset_string, StringView& sign, StringView& hours, Optional& minutes, Optional& seconds, Optional& fraction) { diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.h b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.h index 93537bfd1e4..0f65f729aff 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.h @@ -41,6 +41,7 @@ ThrowCompletionOr create_temporal_time_zone(GlobalObject&, String con ISODateTime get_iso_parts_from_epoch(BigInt const& epoch_nanoseconds); MarkedValueList get_iana_time_zone_epoch_value(GlobalObject&, StringView time_zone_identifier, i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond); i64 get_iana_time_zone_offset_nanoseconds(BigInt const& epoch_nanoseconds, String const& time_zone_identifier); +BigInt* get_iana_time_zone_next_transition(GlobalObject&, BigInt const& epoch_nanoseconds, StringView time_zone_identifier); ThrowCompletionOr parse_time_zone_offset_string(GlobalObject&, String const&); String format_time_zone_offset_string(double offset_nanoseconds); ThrowCompletionOr to_temporal_time_zone(GlobalObject&, Value temporal_time_zone_like); diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZonePrototype.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZonePrototype.cpp index b76255bdaed..53260273a1c 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZonePrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZonePrototype.cpp @@ -33,6 +33,7 @@ void TimeZonePrototype::initialize(GlobalObject& global_object) define_native_function(vm.names.getOffsetStringFor, get_offset_string_for, 1, attr); define_native_function(vm.names.getPlainDateTimeFor, get_plain_date_time_for, 1, attr); define_native_function(vm.names.getPossibleInstantsFor, get_possible_instants_for, 1, attr); + define_native_function(vm.names.getNextTransition, get_next_transition, 1, attr); define_native_function(vm.names.toString, to_string, 0, attr); define_native_function(vm.names.toJSON, to_json, 0, attr); @@ -140,6 +141,31 @@ JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::get_possible_instants_for) return Array::create_from(global_object, possible_instants); } +// 11.4.9 Temporal.TimeZone.prototype.getNextTransition ( startingPoint ), https://tc39.es/proposal-temporal/#sec-temporal.timezone.prototype.getnexttransition +JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::get_next_transition) +{ + // 1. Let timeZone be the this value. + // 2. Perform ? RequireInternalSlot(timeZone, [[InitializedTemporalTimeZone]]). + auto* time_zone = TRY(typed_this_object(global_object)); + + // 3. Set startingPoint to ? ToTemporalInstant(startingPoint). + auto* starting_point = TRY(to_temporal_instant(global_object, vm.argument(0))); + + // 4. If timeZone.[[OffsetNanoseconds]] is not undefined, return null. + if (!time_zone->offset_nanoseconds().has_value()) + return js_null(); + + // 5. Let transition be ? GetIANATimeZoneNextTransition(startingPoint.[[Nanoseconds]], timeZone.[[Identifier]]). + auto* transition = get_iana_time_zone_next_transition(global_object, starting_point->nanoseconds(), time_zone->identifier()); + + // 6. If transition is null, return null. + if (!transition) + return js_null(); + + // 7. Return ! CreateTemporalInstant(transition). + return MUST(create_temporal_instant(global_object, *transition)); +} + // 11.4.11 Temporal.TimeZone.prototype.toString ( ), https://tc39.es/proposal-temporal/#sec-temporal.timezone.prototype.tostring JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::to_string) { diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZonePrototype.h b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZonePrototype.h index f41e1193baf..aeb30812d15 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZonePrototype.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZonePrototype.h @@ -25,6 +25,7 @@ private: JS_DECLARE_NATIVE_FUNCTION(get_offset_string_for); JS_DECLARE_NATIVE_FUNCTION(get_plain_date_time_for); JS_DECLARE_NATIVE_FUNCTION(get_possible_instants_for); + JS_DECLARE_NATIVE_FUNCTION(get_next_transition); JS_DECLARE_NATIVE_FUNCTION(to_string); JS_DECLARE_NATIVE_FUNCTION(to_json); }; diff --git a/Userland/Libraries/LibJS/Tests/builtins/Temporal/TimeZone/TimeZone.prototype.getNextTransition.js b/Userland/Libraries/LibJS/Tests/builtins/Temporal/TimeZone/TimeZone.prototype.getNextTransition.js new file mode 100644 index 00000000000..d92abb3111d --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Temporal/TimeZone/TimeZone.prototype.getNextTransition.js @@ -0,0 +1,25 @@ +describe("correct behavior", () => { + test("length is 1", () => { + expect(Temporal.TimeZone.prototype.getNextTransition).toHaveLength(1); + }); + + test("basic functionality", () => { + const timeZone = new Temporal.TimeZone("UTC"); + const instant = new Temporal.Instant(0n); + expect(timeZone.getNextTransition(instant)).toBeNull(); + }); + + test("custom offset", () => { + const timeZone = new Temporal.TimeZone("+01:30"); + const instant = new Temporal.Instant(0n); + expect(timeZone.getNextTransition(instant)).toBeNull(); + }); +}); + +describe("errors", () => { + test("this value must be a Temporal.TimeZone object", () => { + expect(() => { + Temporal.TimeZone.prototype.getNextTransition.call("foo"); + }).toThrowWithMessage(TypeError, "Not an object of type Temporal.TimeZone"); + }); +});