1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 05:07:45 +00:00

LibJS: Implement Temporal.TimeZone.prototype.getPossibleInstantsFor()

This commit is contained in:
Linus Groh 2021-10-30 10:22:19 +02:00
parent 82792a6815
commit 5fde02184d
6 changed files with 92 additions and 0 deletions

View file

@ -212,6 +212,7 @@ namespace JS {
P(getOwnPropertyNames) \ P(getOwnPropertyNames) \
P(getOwnPropertySymbols) \ P(getOwnPropertySymbols) \
P(getPlainDateTimeFor) \ P(getPlainDateTimeFor) \
P(getPossibleInstantsFor) \
P(getPrototypeOf) \ P(getPrototypeOf) \
P(getSeconds) \ P(getSeconds) \
P(getTime) \ P(getTime) \

View file

@ -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 }; 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 // 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) i64 get_iana_time_zone_offset_nanoseconds([[maybe_unused]] BigInt const& epoch_nanoseconds, [[maybe_unused]] String const& time_zone_identifier)
{ {

View file

@ -7,6 +7,7 @@
#pragma once #pragma once
#include <AK/Optional.h> #include <AK/Optional.h>
#include <LibJS/Runtime/MarkedValueList.h>
#include <LibJS/Runtime/Object.h> #include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.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<String> parse_temporal_time_zone(GlobalObject&, String const&);
ThrowCompletionOr<TimeZone*> create_temporal_time_zone(GlobalObject&, String const& identifier, FunctionObject const* new_target = nullptr); 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); 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); 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&); ThrowCompletionOr<double> parse_time_zone_offset_string(GlobalObject&, String const&);
String format_time_zone_offset_string(double offset_nanoseconds); String format_time_zone_offset_string(double offset_nanoseconds);

View file

@ -5,6 +5,7 @@
*/ */
#include <AK/TypeCasts.h> #include <AK/TypeCasts.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/GlobalObject.h> #include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/Calendar.h> #include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/Instant.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.getOffsetNanosecondsFor, get_offset_nanoseconds_for, 1, attr);
define_native_function(vm.names.getOffsetStringFor, get_offset_string_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.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.toString, to_string, 0, attr);
define_native_function(vm.names.toJSON, to_json, 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)); 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 // 11.4.11 Temporal.TimeZone.prototype.toString ( ), https://tc39.es/proposal-temporal/#sec-temporal.timezone.prototype.tostring
JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::to_string) JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::to_string)
{ {

View file

@ -24,6 +24,7 @@ private:
JS_DECLARE_NATIVE_FUNCTION(get_offset_nanoseconds_for); JS_DECLARE_NATIVE_FUNCTION(get_offset_nanoseconds_for);
JS_DECLARE_NATIVE_FUNCTION(get_offset_string_for); JS_DECLARE_NATIVE_FUNCTION(get_offset_string_for);
JS_DECLARE_NATIVE_FUNCTION(get_plain_date_time_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_string);
JS_DECLARE_NATIVE_FUNCTION(to_json); JS_DECLARE_NATIVE_FUNCTION(to_json);
}; };

View file

@ -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");
});
});