summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Groh <mail@linusgroh.de>2021-10-30 10:22:19 +0200
committerLinus Groh <mail@linusgroh.de>2021-10-30 16:32:20 +0200
commit5fde02184d20649a7da1ba0c8792927328037a0d (patch)
tree4db7a22d0787de0ec08aa0e7ba7ac357b25018e3
parent82792a6815ce22ebc225ade8cecfe0b2c8e71cd7 (diff)
downloadserenity-5fde02184d20649a7da1ba0c8792927328037a0d.zip
LibJS: Implement Temporal.TimeZone.prototype.getPossibleInstantsFor()
-rw-r--r--Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.cpp14
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.h2
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/TimeZonePrototype.cpp43
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/TimeZonePrototype.h1
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Temporal/TimeZone/TimeZone.prototype.getPossibleInstantsFor.js31
6 files changed, 92 insertions, 0 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h
index 069c356071..f59917044a 100644
--- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h
+++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h
@@ -212,6 +212,7 @@ namespace JS {
P(getOwnPropertyNames) \
P(getOwnPropertySymbols) \
P(getPlainDateTimeFor) \
+ P(getPossibleInstantsFor) \
P(getPrototypeOf) \
P(getSeconds) \
P(getTime) \
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.cpp
index fc5e801678..13205ebd06 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.cpp
@@ -154,6 +154,20 @@ ISODateTime get_iso_parts_from_epoch(BigInt const& epoch_nanoseconds)
return { .year = year, .month = month, .day = day, .hour = hour, .minute = minute, .second = second, .millisecond = millisecond, .microsecond = microsecond, .nanosecond = nanosecond };
}
+// 11.6.4 GetIANATimeZoneEpochValue ( timeZoneIdentifier, year, month, day, hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/proposal-temporal/#sec-temporal-getianatimezoneepochvalue
+MarkedValueList get_iana_time_zone_epoch_value(GlobalObject& global_object, StringView time_zone_identifier, i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond)
+{
+ // The abstract operation GetIANATimeZoneEpochValue is an implementation-defined algorithm that returns a List of integers. Each integer in the List represents a number of nanoseconds since the Unix epoch in UTC that may correspond to the given calendar date and wall-clock time in the IANA time zone identified by timeZoneIdentifier.
+ // When the input represents a local time repeating multiple times at a negative time zone transition (e.g. when the daylight saving time ends or the time zone offset is decreased due to a time zone rule change), the returned List will have more than one element. When the input represents a skipped local time at a positive time zone transition (e.g. when the daylight saving time starts or the time zone offset is increased due to a time zone rule change), the returned List will be empty. Otherwise, the returned List will have one element.
+
+ VERIFY(time_zone_identifier == "UTC"sv);
+ // FIXME: MarkedValueList<T> for T != Value would still be nice.
+ auto& vm = global_object.vm();
+ auto list = MarkedValueList { vm.heap() };
+ list.append(get_epoch_from_iso_parts(global_object, year, month, day, hour, minute, second, millisecond, microsecond, nanosecond));
+ return list;
+}
+
// 11.6.5 GetIANATimeZoneOffsetNanoseconds ( epochNanoseconds, timeZoneIdentifier ), https://tc39.es/proposal-temporal/#sec-temporal-getianatimezoneoffsetnanoseconds
i64 get_iana_time_zone_offset_nanoseconds([[maybe_unused]] BigInt const& epoch_nanoseconds, [[maybe_unused]] String const& time_zone_identifier)
{
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.h b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.h
index 8923d7fc93..93537bfd1e 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.h
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.h
@@ -7,6 +7,7 @@
#pragma once
#include <AK/Optional.h>
+#include <LibJS/Runtime/MarkedValueList.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
@@ -38,6 +39,7 @@ String default_time_zone();
ThrowCompletionOr<String> parse_temporal_time_zone(GlobalObject&, String const&);
ThrowCompletionOr<TimeZone*> create_temporal_time_zone(GlobalObject&, String const& identifier, FunctionObject const* new_target = nullptr);
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);
ThrowCompletionOr<double> parse_time_zone_offset_string(GlobalObject&, String const&);
String format_time_zone_offset_string(double offset_nanoseconds);
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZonePrototype.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZonePrototype.cpp
index 9e11fe628f..b76255bdae 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZonePrototype.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZonePrototype.cpp
@@ -5,6 +5,7 @@
*/
#include <AK/TypeCasts.h>
+#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/Instant.h>
@@ -31,6 +32,7 @@ void TimeZonePrototype::initialize(GlobalObject& global_object)
define_native_function(vm.names.getOffsetNanosecondsFor, get_offset_nanoseconds_for, 1, attr);
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.toString, to_string, 0, attr);
define_native_function(vm.names.toJSON, to_json, 0, attr);
@@ -97,6 +99,47 @@ JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::get_plain_date_time_for)
return TRY(builtin_time_zone_get_plain_date_time_for(global_object, time_zone, *instant, *calendar));
}
+// 11.4.8 Temporal.TimeZone.prototype.getPossibleInstantsFor ( dateTime ), https://tc39.es/proposal-temporal/#sec-temporal.timezone.prototype.getpossibleinstantsfor
+JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::get_possible_instants_for)
+{
+ // 1. Let timeZone be the this value.
+ // 2. Perform ? RequireInternalSlot(timeZone, [[InitializedTemporalTimezone]]).
+ auto* time_zone = TRY(typed_this_object(global_object));
+
+ // 3. Set dateTime to ? ToTemporalDateTime(dateTime).
+ auto* date_time = TRY(to_temporal_date_time(global_object, vm.argument(0)));
+
+ // 4. If timeZone.[[OffsetNanoseconds]] is not undefined, then
+ if (time_zone->offset_nanoseconds().has_value()) {
+ // a. Let epochNanoseconds be ! GetEpochFromISOParts(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]]).
+ auto* epoch_nanoseconds = get_epoch_from_iso_parts(global_object, date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond());
+
+ // b. Let instant be ! CreateTemporalInstant(โ„ค(epochNanoseconds โˆ’ timeZone.[[OffsetNanoseconds]])).
+ auto* instant = MUST(create_temporal_instant(global_object, *js_bigint(vm, epoch_nanoseconds->big_integer().minus(Crypto::SignedBigInteger::create_from(*time_zone->offset_nanoseconds())))));
+
+ // c. Return ! CreateArrayFromList(ยซ instant ยป).
+ return Array::create_from(global_object, { instant });
+ }
+
+ // 5. Let possibleEpochNanoseconds be ? GetIANATimeZoneEpochValue(timeZone.[[Identifier]], dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]]).
+ auto possible_epoch_nanoseconds = get_iana_time_zone_epoch_value(global_object, time_zone->identifier(), date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond());
+
+ // 6. Let possibleInstants be a new empty List.
+ auto possible_instants = MarkedValueList { vm.heap() };
+
+ // 7. For each value epochNanoseconds in possibleEpochNanoseconds, do
+ for (auto& epoch_nanoseconds : possible_epoch_nanoseconds) {
+ // a. Let instant be ! CreateTemporalInstant(epochNanoseconds).
+ auto* instant = MUST(create_temporal_instant(global_object, epoch_nanoseconds.as_bigint()));
+
+ // b. Append instant to possibleInstants.
+ possible_instants.append(instant);
+ }
+
+ // 8. Return ! CreateArrayFromList(possibleInstants).
+ return Array::create_from(global_object, possible_instants);
+}
+
// 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 3137cb5ee3..f41e1193ba 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZonePrototype.h
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZonePrototype.h
@@ -24,6 +24,7 @@ private:
JS_DECLARE_NATIVE_FUNCTION(get_offset_nanoseconds_for);
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(to_string);
JS_DECLARE_NATIVE_FUNCTION(to_json);
};
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Temporal/TimeZone/TimeZone.prototype.getPossibleInstantsFor.js b/Userland/Libraries/LibJS/Tests/builtins/Temporal/TimeZone/TimeZone.prototype.getPossibleInstantsFor.js
new file mode 100644
index 0000000000..d9587c9d20
--- /dev/null
+++ b/Userland/Libraries/LibJS/Tests/builtins/Temporal/TimeZone/TimeZone.prototype.getPossibleInstantsFor.js
@@ -0,0 +1,31 @@
+describe("correct behavior", () => {
+ test("length is 1", () => {
+ expect(Temporal.TimeZone.prototype.getPossibleInstantsFor).toHaveLength(1);
+ });
+
+ test("basic functionality", () => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const plainDateTime = new Temporal.PlainDateTime(2021, 7, 6, 18, 14, 47);
+ const possibleInstants = timeZone.getPossibleInstantsFor(plainDateTime);
+ expect(possibleInstants).toBeInstanceOf(Array);
+ expect(possibleInstants).toHaveLength(1);
+ expect(possibleInstants[0].epochNanoseconds).toBe(1625595287000000000n);
+ });
+
+ test("custom offset", () => {
+ const timeZone = new Temporal.TimeZone("+01:30");
+ const plainDateTime = new Temporal.PlainDateTime(2021, 7, 6, 18, 14, 47);
+ const possibleInstants = timeZone.getPossibleInstantsFor(plainDateTime);
+ expect(possibleInstants).toBeInstanceOf(Array);
+ expect(possibleInstants).toHaveLength(1);
+ expect(possibleInstants[0].epochNanoseconds).toBe(1625589887000000000n);
+ });
+});
+
+describe("errors", () => {
+ test("this value must be a Temporal.TimeZone object", () => {
+ expect(() => {
+ Temporal.TimeZone.prototype.getPossibleInstantsFor.call("foo");
+ }).toThrowWithMessage(TypeError, "Not an object of type Temporal.TimeZone");
+ });
+});