1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-14 09:04:59 +00:00

LibJS: Use TimeZoneMethods in GetOffsetNanosecondsFor

Update to the latest version of the spec which was refactored to use
time zone methods record. This requires updating a whole bunch of
callers to pass through a record too.

This also ends up improving exceptions on a missing
getOffsetNanosecondsFor method.
This commit is contained in:
Shannon Booth 2024-03-02 18:30:08 +13:00 committed by Andreas Kling
parent 230ffc022c
commit f95117f75d
22 changed files with 52 additions and 39 deletions

View file

@ -438,12 +438,13 @@ ThrowCompletionOr<double> calculate_offset_shift(VM& vm, Value relative_to_value
return 0.0;
auto& relative_to = static_cast<ZonedDateTime&>(relative_to_value.as_object());
auto time_zone_record = TRY(create_time_zone_methods_record(vm, NonnullGCPtr<Object> { relative_to.time_zone() }, { { TimeZoneMethod::GetOffsetNanosecondsFor } }));
// 2. Let instant be ! CreateTemporalInstant(relativeTo.[[Nanoseconds]]).
auto* instant = MUST(create_temporal_instant(vm, relative_to.nanoseconds()));
// 3. Let offsetBefore be ? GetOffsetNanosecondsFor(relativeTo.[[TimeZone]], instant).
auto offset_before = TRY(get_offset_nanoseconds_for(vm, &relative_to.time_zone(), *instant));
auto offset_before = TRY(get_offset_nanoseconds_for(vm, time_zone_record, *instant));
// 4. Let after be ? AddZonedDateTime(relativeTo.[[Nanoseconds]], relativeTo.[[TimeZone]], relativeTo.[[Calendar]], y, mon, w, d, 0, 0, 0, 0, 0, 0).
auto* after = TRY(add_zoned_date_time(vm, relative_to.nanoseconds(), &relative_to.time_zone(), relative_to.calendar(), years, months, weeks, days, 0, 0, 0, 0, 0, 0));
@ -452,7 +453,7 @@ ThrowCompletionOr<double> calculate_offset_shift(VM& vm, Value relative_to_value
auto* instant_after = MUST(create_temporal_instant(vm, *after));
// 6. Let offsetAfter be ? GetOffsetNanosecondsFor(relativeTo.[[TimeZone]], instantAfter).
auto offset_after = TRY(get_offset_nanoseconds_for(vm, &relative_to.time_zone(), *instant_after));
auto offset_after = TRY(get_offset_nanoseconds_for(vm, time_zone_record, *instant_after));
// 7. Return offsetAfter - offsetBefore.
return offset_after - offset_before;

View file

@ -300,8 +300,10 @@ ThrowCompletionOr<String> temporal_instant_to_string(VM& vm, Instant& instant, V
}
// 9. Else,
else {
auto time_zone_record = TRY(create_time_zone_methods_record(vm, NonnullGCPtr<Object> { time_zone.as_object() }, { { TimeZoneMethod::GetOffsetNanosecondsFor } }));
// a. Let offsetNs be ? GetOffsetNanosecondsFor(timeZone, instant).
auto offset_ns = TRY(get_offset_nanoseconds_for(vm, time_zone, instant));
auto offset_ns = TRY(get_offset_nanoseconds_for(vm, time_zone_record, instant));
// b. Let timeZoneString be ! FormatISOTimeZoneOffsetString(offsetNs).
time_zone_string = MUST_OR_THROW_OOM(format_iso_time_zone_offset_string(vm, offset_ns));

View file

@ -329,14 +329,15 @@ ThrowCompletionOr<Object*> to_temporal_time_zone(VM& vm, Value temporal_time_zon
return MUST_OR_THROW_OOM(create_temporal_time_zone(vm, *parse_result.offset_string));
}
// 11.6.8 GetOffsetNanosecondsFor ( timeZone, instant ), https://tc39.es/proposal-temporal/#sec-temporal-getoffsetnanosecondsfor
ThrowCompletionOr<double> get_offset_nanoseconds_for(VM& vm, Value time_zone, Instant& instant)
// 11.5.19 GetOffsetNanosecondsFor ( timeZoneRec, instant ), https://tc39.es/proposal-temporal/#sec-temporal-getoffsetnanosecondsfor
ThrowCompletionOr<double> get_offset_nanoseconds_for(VM& vm, TimeZoneMethods const& time_zone_record, Instant const& instant)
{
// 1. Let getOffsetNanosecondsFor be ? GetMethod(timeZone, "getOffsetNanosecondsFor").
auto get_offset_nanoseconds_for = TRY(time_zone.get_method(vm, vm.names.getOffsetNanosecondsFor));
// 1. Let offsetNanoseconds be ? TimeZoneMethodsRecordCall(timeZoneRec, GET-OFFSET-NANOSECONDS-FOR, « instant »).
auto offset_nanoseconds_value = TRY(time_zone_methods_record_call(vm, time_zone_record, TimeZoneMethod::GetOffsetNanosecondsFor, { { &instant } }));
// 2. Let offsetNanoseconds be ? Call(getOffsetNanosecondsFor, timeZone, « instant »).
auto offset_nanoseconds_value = TRY(call(vm, get_offset_nanoseconds_for, time_zone, &instant));
// 2. If TimeZoneMethodsRecordIsBuiltin(timeZoneRec), return (offsetNanoseconds).
if (time_zone_methods_record_is_builtin(time_zone_record))
return offset_nanoseconds_value.as_double();
// 3. If Type(offsetNanoseconds) is not Number, throw a TypeError exception.
if (!offset_nanoseconds_value.is_number())
@ -360,8 +361,10 @@ ThrowCompletionOr<double> get_offset_nanoseconds_for(VM& vm, Value time_zone, In
// 11.6.9 BuiltinTimeZoneGetOffsetStringFor ( timeZone, instant ), https://tc39.es/proposal-temporal/#sec-temporal-builtintimezonegetoffsetstringfor
ThrowCompletionOr<String> builtin_time_zone_get_offset_string_for(VM& vm, Value time_zone, Instant& instant)
{
auto time_zone_record = TRY(create_time_zone_methods_record(vm, NonnullGCPtr<Object> { time_zone.as_object() }, { { TimeZoneMethod::GetOffsetNanosecondsFor } }));
// 1. Let offsetNanoseconds be ? GetOffsetNanosecondsFor(timeZone, instant).
auto offset_nanoseconds = TRY(get_offset_nanoseconds_for(vm, time_zone, instant));
auto offset_nanoseconds = TRY(get_offset_nanoseconds_for(vm, time_zone_record, instant));
// 2. Return ! FormatTimeZoneOffsetString(offsetNanoseconds).
return MUST_OR_THROW_OOM(format_time_zone_offset_string(vm, offset_nanoseconds));
@ -370,10 +373,12 @@ ThrowCompletionOr<String> builtin_time_zone_get_offset_string_for(VM& vm, Value
// 11.6.10 BuiltinTimeZoneGetPlainDateTimeFor ( timeZone, instant, calendar ), https://tc39.es/proposal-temporal/#sec-temporal-builtintimezonegetplaindatetimefor
ThrowCompletionOr<PlainDateTime*> builtin_time_zone_get_plain_date_time_for(VM& vm, Value time_zone, Instant& instant, Object& calendar)
{
auto time_zone_record = TRY(create_time_zone_methods_record(vm, NonnullGCPtr<Object> { time_zone.as_object() }, { { TimeZoneMethod::GetOffsetNanosecondsFor } }));
// 1. Assert: instant has an [[InitializedTemporalInstant]] internal slot.
// 2. Let offsetNanoseconds be ? GetOffsetNanosecondsFor(timeZone, instant).
auto offset_nanoseconds = TRY(get_offset_nanoseconds_for(vm, time_zone, instant));
auto offset_nanoseconds = TRY(get_offset_nanoseconds_for(vm, time_zone_record, instant));
// 3. Let result be ! GetISOPartsFromEpoch((instant.[[Nanoseconds]])).
auto result = get_iso_parts_from_epoch(vm, instant.nanoseconds().big_integer());
@ -465,17 +470,17 @@ ThrowCompletionOr<NonnullGCPtr<Instant>> disambiguate_possible_instants(VM& vm,
// 13. Let dayAfter be ! CreateTemporalInstant(dayAfterNs).
auto* day_after = MUST(create_temporal_instant(vm, day_after_ns));
auto time_zone_record = TRY(create_time_zone_methods_record(vm, NonnullGCPtr<Object> { time_zone.as_object() }, { { TimeZoneMethod::GetOffsetNanosecondsFor, TimeZoneMethod::GetPossibleInstantsFor } }));
// 14. Let offsetBefore be ? GetOffsetNanosecondsFor(timeZone, dayBefore).
auto offset_before = TRY(get_offset_nanoseconds_for(vm, time_zone, *day_before));
auto offset_before = TRY(get_offset_nanoseconds_for(vm, time_zone_record, *day_before));
// 15. Let offsetAfter be ? GetOffsetNanosecondsFor(timeZone, dayAfter).
auto offset_after = TRY(get_offset_nanoseconds_for(vm, time_zone, *day_after));
auto offset_after = TRY(get_offset_nanoseconds_for(vm, time_zone_record, *day_after));
// 16. Let nanoseconds be offsetAfter - offsetBefore.
auto nanoseconds = offset_after - offset_before;
auto time_zone_record = TRY(create_time_zone_methods_record(vm, NonnullGCPtr<Object> { time_zone.as_object() }, { { TimeZoneMethod::GetPossibleInstantsFor } }));
// 17. If disambiguation is "earlier", then
if (disambiguation == "earlier"sv) {
// a. Let earlier be ? AddDateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], dateTime.[[Calendar]], 0, 0, 0, 0, 0, 0, 0, 0, 0, -nanoseconds, undefined).

View file

@ -46,7 +46,7 @@ BigInt* get_named_time_zone_previous_transition(VM&, StringView time_zone_identi
ThrowCompletionOr<String> format_time_zone_offset_string(VM&, double offset_nanoseconds);
ThrowCompletionOr<String> format_iso_time_zone_offset_string(VM&, double offset_nanoseconds);
ThrowCompletionOr<Object*> to_temporal_time_zone(VM&, Value temporal_time_zone_like);
ThrowCompletionOr<double> get_offset_nanoseconds_for(VM&, Value time_zone, Instant&);
ThrowCompletionOr<double> get_offset_nanoseconds_for(VM& vm, TimeZoneMethods const& time_zone_record, Instant const& instant);
ThrowCompletionOr<String> builtin_time_zone_get_offset_string_for(VM&, Value time_zone, Instant&);
ThrowCompletionOr<PlainDateTime*> builtin_time_zone_get_plain_date_time_for(VM&, Value time_zone, Instant&, Object& calendar);
ThrowCompletionOr<NonnullGCPtr<Instant>> builtin_time_zone_get_instant_for(VM&, Value time_zone, PlainDateTime&, StringView disambiguation);

View file

@ -81,13 +81,13 @@ ThrowCompletionOr<BigInt const*> interpret_iso_date_time_offset(VM& vm, i32 year
VERIFY(offset_option.is_one_of("prefer"sv, "reject"sv));
// 7. Let possibleInstants be ? GetPossibleInstantsFor(timeZone, dateTime).
auto time_zone_record = TRY(create_time_zone_methods_record(vm, NonnullGCPtr<Object> { time_zone.as_object() }, { { TimeZoneMethod::GetPossibleInstantsFor } }));
auto time_zone_record = TRY(create_time_zone_methods_record(vm, NonnullGCPtr<Object> { time_zone.as_object() }, { { TimeZoneMethod::GetPossibleInstantsFor, TimeZoneMethod::GetOffsetNanosecondsFor } }));
auto possible_instants = TRY(get_possible_instants_for(vm, time_zone_record, *date_time));
// 8. For each element candidate of possibleInstants, do
for (auto candidate : possible_instants) {
// a. Let candidateNanoseconds be ? GetOffsetNanosecondsFor(timeZone, candidate).
auto candidate_nanoseconds = TRY(get_offset_nanoseconds_for(vm, time_zone, *candidate));
auto candidate_nanoseconds = TRY(get_offset_nanoseconds_for(vm, time_zone_record, *candidate));
// b. If candidateNanoseconds = offsetNanoseconds, then
if (candidate_nanoseconds == offset_nanoseconds) {
@ -329,8 +329,10 @@ ThrowCompletionOr<String> temporal_zoned_date_time_to_string(VM& vm, ZonedDateTi
}
// 11. Else,
else {
auto time_zone_record = TRY(create_time_zone_methods_record(vm, NonnullGCPtr<Object> { time_zone }, { { TimeZoneMethod::GetOffsetNanosecondsFor } }));
// a. Let offsetNs be ? GetOffsetNanosecondsFor(timeZone, instant).
auto offset_ns = TRY(get_offset_nanoseconds_for(vm, &time_zone, *instant));
auto offset_ns = TRY(get_offset_nanoseconds_for(vm, time_zone_record, *instant));
// b. Let offsetString be ! FormatISOTimeZoneOffsetString(offsetNs).
offset_string = MUST_OR_THROW_OOM(format_iso_time_zone_offset_string(vm, offset_ns));

View file

@ -674,13 +674,14 @@ JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::offset_nanoseconds_getter)
auto zoned_date_time = TRY(typed_this_object(vm));
// 3. Let timeZone be zonedDateTime.[[TimeZone]].
auto& time_zone = zoned_date_time->time_zone();
// 3. Let timeZoneRec be ? CreateTimeZoneMethodsRecord(zonedDateTime.[[TimeZone]], « GET-OFFSET-NANOSECONDS-FOR »).
auto time_zone_record = TRY(create_time_zone_methods_record(vm, NonnullGCPtr<Object> { zoned_date_time->time_zone() }, { { TimeZoneMethod::GetOffsetNanosecondsFor } }));
// 4. Let instant be ! CreateTemporalInstant(zonedDateTime.[[Nanoseconds]]).
auto* instant = MUST(create_temporal_instant(vm, zoned_date_time->nanoseconds()));
// 5. Return 𝔽(? GetOffsetNanosecondsFor(timeZone, instant)).
return Value(TRY(get_offset_nanoseconds_for(vm, &time_zone, *instant)));
// 5. Return 𝔽(? GetOffsetNanosecondsFor(timeZoneRec, instant)).
return Value(TRY(get_offset_nanoseconds_for(vm, time_zone_record, *instant)));
}
// 6.3.30 get Temporal.ZonedDateTime.prototype.offset, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.offset
@ -1025,6 +1026,8 @@ JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::round)
// 9. Let timeZone be zonedDateTime.[[TimeZone]].
auto& time_zone = zoned_date_time->time_zone();
auto time_zone_record = TRY(create_time_zone_methods_record(vm, NonnullGCPtr<Object> { time_zone }, { { TimeZoneMethod::GetOffsetNanosecondsFor } }));
// 10. Let instant be ! CreateTemporalInstant(zonedDateTime.[[Nanoseconds]]).
auto* instant = MUST(create_temporal_instant(vm, zoned_date_time->nanoseconds()));
@ -1062,7 +1065,7 @@ JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::round)
auto round_result = round_iso_date_time(temporal_date_time->iso_year(), temporal_date_time->iso_month(), temporal_date_time->iso_day(), temporal_date_time->iso_hour(), temporal_date_time->iso_minute(), temporal_date_time->iso_second(), temporal_date_time->iso_millisecond(), temporal_date_time->iso_microsecond(), temporal_date_time->iso_nanosecond(), rounding_increment, *smallest_unit, rounding_mode, day_length_ns);
// 21. Let offsetNanoseconds be ? GetOffsetNanosecondsFor(timeZone, instant).
auto offset_nanoseconds = TRY(get_offset_nanoseconds_for(vm, &time_zone, *instant));
auto offset_nanoseconds = TRY(get_offset_nanoseconds_for(vm, time_zone_record, *instant));
// 22. Let epochNanoseconds be ? InterpretISODateTimeOffset(roundResult.[[Year]], roundResult.[[Month]], roundResult.[[Day]], roundResult.[[Hour]], roundResult.[[Minute]], roundResult.[[Second]], roundResult.[[Millisecond]], roundResult.[[Microsecond]], roundResult.[[Nanosecond]], option, offsetNanoseconds, timeZone, "compatible", "prefer", match exactly).
auto* epoch_nanoseconds = TRY(interpret_iso_date_time_offset(vm, round_result.year, round_result.month, round_result.day, round_result.hour, round_result.minute, round_result.second, round_result.millisecond, round_result.microsecond, round_result.nanosecond, OffsetBehavior::Option, offset_nanoseconds, &time_zone, "compatible"sv, "prefer"sv, MatchBehavior::MatchExactly));

View file

@ -67,6 +67,6 @@ describe("errors", () => {
const instant = new Temporal.Instant(0n);
expect(() => {
instant.toString({ timeZone: {} });
}).toThrowWithMessage(TypeError, "null is not a function");
}).toThrowWithMessage(TypeError, "getOffsetNanosecondsFor is undefined");
});
});

View file

@ -55,6 +55,6 @@ describe("errors", () => {
test("custom time zone doesn't have a getOffsetNanosecondsFor function", () => {
expect(() => {
Temporal.Now.plainDate({}, {});
}).toThrowWithMessage(TypeError, "null is not a function");
}).toThrowWithMessage(TypeError, "getOffsetNanosecondsFor is undefined");
});
});

View file

@ -52,6 +52,6 @@ describe("errors", () => {
test("custom time zone doesn't have a getOffsetNanosecondsFor function", () => {
expect(() => {
Temporal.Now.plainDateISO({});
}).toThrowWithMessage(TypeError, "null is not a function");
}).toThrowWithMessage(TypeError, "getOffsetNanosecondsFor is undefined");
});
});

View file

@ -104,6 +104,6 @@ describe("errors", () => {
test("custom time zone doesn't have a getOffsetNanosecondsFor function", () => {
expect(() => {
Temporal.Now.plainDateTime({}, {});
}).toThrowWithMessage(TypeError, "null is not a function");
}).toThrowWithMessage(TypeError, "getOffsetNanosecondsFor is undefined");
});
});

View file

@ -94,6 +94,6 @@ describe("errors", () => {
test("custom time zone doesn't have a getOffsetNanosecondsFor function", () => {
expect(() => {
Temporal.Now.plainDateTimeISO({});
}).toThrowWithMessage(TypeError, "null is not a function");
}).toThrowWithMessage(TypeError, "getOffsetNanosecondsFor is undefined");
});
});

View file

@ -39,6 +39,6 @@ describe("errors", () => {
test("custom time zone doesn't have a getOffsetNanosecondsFor function", () => {
expect(() => {
Temporal.Now.plainTimeISO({});
}).toThrowWithMessage(TypeError, "null is not a function");
}).toThrowWithMessage(TypeError, "getOffsetNanosecondsFor is undefined");
});
});

View file

@ -41,7 +41,7 @@ describe("errors", () => {
const zonedDateTime = new Temporal.ZonedDateTime(0n, {});
expect(() => {
Temporal.PlainDate.from(zonedDateTime);
}).toThrowWithMessage(TypeError, "null is not a function");
}).toThrowWithMessage(TypeError, "getOffsetNanosecondsFor is undefined");
});
test("invalid date time string", () => {

View file

@ -175,7 +175,7 @@ describe("errors", () => {
const zonedDateTime = new Temporal.ZonedDateTime(0n, {});
expect(() => {
Temporal.PlainDateTime.from(zonedDateTime);
}).toThrowWithMessage(TypeError, "null is not a function");
}).toThrowWithMessage(TypeError, "getOffsetNanosecondsFor is undefined");
});
test("string must not contain a UTC designator", () => {

View file

@ -53,7 +53,7 @@ describe("errors", () => {
const zonedDateTime = new Temporal.ZonedDateTime(0n, {});
expect(() => {
Temporal.PlainTime.from(zonedDateTime);
}).toThrowWithMessage(TypeError, "null is not a function");
}).toThrowWithMessage(TypeError, "getOffsetNanosecondsFor is undefined");
});
test("string must not contain a UTC designator", () => {

View file

@ -35,7 +35,7 @@ describe("errors", () => {
const instant = new Temporal.Instant(1n);
expect(() => {
timeZone.getPlainDateTimeFor(instant);
}).toThrowWithMessage(TypeError, "null is not a function");
}).toThrowWithMessage(TypeError, "getOffsetNanosecondsFor is undefined");
});
test("this value must be a Temporal.TimeZone object", () => {

View file

@ -51,6 +51,6 @@ describe("errors", () => {
const zonedDateTime = new Temporal.ZonedDateTime(0n, {});
expect(() => {
zonedDateTime.getISOFields();
}).toThrowWithMessage(TypeError, "null is not a function");
}).toThrowWithMessage(TypeError, "getOffsetNanosecondsFor is undefined");
});
});

View file

@ -23,6 +23,6 @@ describe("errors", () => {
const zonedDateTime = new Temporal.ZonedDateTime(0n, {});
expect(() => {
zonedDateTime.offset;
}).toThrowWithMessage(TypeError, "null is not a function");
}).toThrowWithMessage(TypeError, "getOffsetNanosecondsFor is undefined");
});
});

View file

@ -23,6 +23,6 @@ describe("errors", () => {
const zonedDateTime = new Temporal.ZonedDateTime(0n, {});
expect(() => {
zonedDateTime.offsetNanoseconds;
}).toThrowWithMessage(TypeError, "null is not a function");
}).toThrowWithMessage(TypeError, "getOffsetNanosecondsFor is undefined");
});
});

View file

@ -22,6 +22,6 @@ describe("errors", () => {
const zonedDateTime = new Temporal.ZonedDateTime(0n, {});
expect(() => {
zonedDateTime.toJSON();
}).toThrowWithMessage(TypeError, "null is not a function");
}).toThrowWithMessage(TypeError, "getOffsetNanosecondsFor is undefined");
});
});

View file

@ -22,6 +22,6 @@ describe("errors", () => {
const zonedDateTime = new Temporal.ZonedDateTime(0n, {});
expect(() => {
zonedDateTime.toLocaleString();
}).toThrowWithMessage(TypeError, "null is not a function");
}).toThrowWithMessage(TypeError, "getOffsetNanosecondsFor is undefined");
});
});

View file

@ -156,7 +156,7 @@ describe("errors", () => {
const zonedDateTime = new Temporal.ZonedDateTime(0n, {});
expect(() => {
zonedDateTime.toString();
}).toThrowWithMessage(TypeError, "null is not a function");
}).toThrowWithMessage(TypeError, "getOffsetNanosecondsFor is undefined");
});
test("calendarName option must be one of 'auto', 'always', 'never', 'critical'", () => {