LibJS: Implement most of GetTemporalRelativeToOption
Now that we have the Temporal.PlainDate object, we can flesh out this AO for such relative-to objects.
This commit is contained in:
parent
521638642f
commit
06593b1894
Notes:
github-actions[bot]
2024-11-23 13:47:14 +00:00
Author: https://github.com/trflynn89 Commit: https://github.com/LadybirdBrowser/ladybird/commit/06593b1894b Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2513 Reviewed-by: https://github.com/shannonbooth ✅
8 changed files with 189 additions and 5 deletions
|
@ -409,7 +409,128 @@ ThrowCompletionOr<RelativeTo> get_temporal_relative_to_option(VM& vm, Object con
|
|||
if (value.is_undefined())
|
||||
return RelativeTo { .plain_relative_to = {}, .zoned_relative_to = {} };
|
||||
|
||||
// FIXME: Implement the remaining steps of this AO when we have implemented PlainRelativeTo and ZonedRelativeTo.
|
||||
// FIXME: 3. Let offsetBehaviour be OPTION.
|
||||
// FIXME: 4. Let matchBehaviour be MATCH-EXACTLY.
|
||||
|
||||
String calendar;
|
||||
Optional<String> time_zone;
|
||||
Optional<String> offset_string;
|
||||
ISODate iso_date;
|
||||
Variant<ParsedISODateTime::StartOfDay, Time> time { Time {} };
|
||||
|
||||
// 5. If value is an Object, then
|
||||
if (value.is_object()) {
|
||||
auto& object = value.as_object();
|
||||
|
||||
// FIXME: a. If value has an [[InitializedTemporalZonedDateTime]] internal slot, then
|
||||
// FIXME: i. Return the Record { [[PlainRelativeTo]]: undefined, [[ZonedRelativeTo]]: value }.
|
||||
|
||||
// b. If value has an [[InitializedTemporalDate]] internal slot, then
|
||||
if (is<PlainDate>(object)) {
|
||||
// i. Return the Record { [[PlainRelativeTo]]: value, [[ZonedRelativeTo]]: undefined }.
|
||||
return RelativeTo { .plain_relative_to = static_cast<PlainDate&>(object), .zoned_relative_to = {} };
|
||||
}
|
||||
|
||||
// FIXME: c. If value has an [[InitializedTemporalDateTime]] internal slot, then
|
||||
// FIXME: i. Let plainDate be ! CreateTemporalDate(value.[[ISODateTime]].[[ISODate]], value.[[Calendar]]).
|
||||
// FIXME: ii. Return the Record { [[PlainRelativeTo]]: plainDate, [[ZonedRelativeTo]]: undefined }.
|
||||
|
||||
// FIXME: d. Let calendar be ? GetTemporalCalendarIdentifierWithISODefault(value).
|
||||
calendar = TRY(get_temporal_calendar_identifier_with_iso_default(vm, object));
|
||||
|
||||
// e. Let fields be ? PrepareCalendarFields(calendar, value, « YEAR, MONTH, MONTH-CODE, DAY », « HOUR, MINUTE, SECOND, MILLISECOND, MICROSECOND, NANOSECOND, OFFSET, TIME-ZONE », «»).
|
||||
static constexpr auto calendar_field_names = to_array({ CalendarField::Year, CalendarField::Month, CalendarField::MonthCode, CalendarField::Day });
|
||||
static constexpr auto non_calendar_field_names = to_array({ CalendarField::Hour, CalendarField::Minute, CalendarField::Second, CalendarField::Millisecond, CalendarField::Microsecond, CalendarField::Nanosecond, CalendarField::Offset, CalendarField::TimeZone });
|
||||
auto fields = TRY(prepare_calendar_fields(vm, calendar, object, calendar_field_names, non_calendar_field_names, CalendarFieldList {}));
|
||||
|
||||
// f. Let result be ? InterpretTemporalDateTimeFields(calendar, fields, CONSTRAIN).
|
||||
auto result = TRY(interpret_temporal_date_time_fields(vm, calendar, fields, Overflow::Constrain));
|
||||
|
||||
// g. Let timeZone be fields.[[TimeZone]].
|
||||
time_zone = move(fields.time_zone);
|
||||
|
||||
// h. Let offsetString be fields.[[Offset]].
|
||||
offset_string = move(fields.offset);
|
||||
|
||||
// i. If offsetString is UNSET, then
|
||||
if (!offset_string.has_value()) {
|
||||
// FIXME: i. Set offsetBehaviour to WALL.
|
||||
}
|
||||
|
||||
// j. Let isoDate be result.[[ISODate]].
|
||||
iso_date = result.iso_date;
|
||||
|
||||
// k. Let time be result.[[Time]].
|
||||
time = result.time;
|
||||
}
|
||||
// 6. Else,
|
||||
else {
|
||||
// a. If value is not a String, throw a TypeError exception.
|
||||
if (!value.is_string())
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAString, vm.names.relativeTo);
|
||||
|
||||
// b. Let result be ? ParseISODateTime(value, « TemporalDateTimeString[+Zoned], TemporalDateTimeString[~Zoned] »).
|
||||
auto result = TRY(parse_iso_date_time(vm, value.as_string().utf8_string_view(), { { Production::TemporalZonedDateTimeString, Production::TemporalDateTimeString } }));
|
||||
|
||||
// c. Let offsetString be result.[[TimeZone]].[[OffsetString]].
|
||||
offset_string = move(result.time_zone.offset_string);
|
||||
|
||||
// d. Let annotation be result.[[TimeZone]].[[TimeZoneAnnotation]].
|
||||
auto annotation = move(result.time_zone.time_zone_annotation);
|
||||
|
||||
// e. If annotation is EMPTY, then
|
||||
if (!annotation.has_value()) {
|
||||
// i. Let timeZone be UNSET.
|
||||
time_zone = {};
|
||||
}
|
||||
// f. Else,
|
||||
else {
|
||||
// i. Let timeZone be ? ToTemporalTimeZoneIdentifier(annotation).
|
||||
time_zone = TRY(to_temporal_time_zone_identifier(vm, *annotation));
|
||||
|
||||
// ii. If result.[[TimeZone]].[[Z]] is true, then
|
||||
if (result.time_zone.z_designator) {
|
||||
// FIXME: 1. Set offsetBehaviour to EXACT.
|
||||
}
|
||||
// iii. Else if offsetString is EMPTY, then
|
||||
else if (!offset_string.has_value()) {
|
||||
// FIXME: 1. Set offsetBehaviour to WALL.
|
||||
}
|
||||
|
||||
// FIXME: iv. Set matchBehaviour to MATCH-MINUTES.
|
||||
}
|
||||
|
||||
// g. Let calendar be result.[[Calendar]].
|
||||
// h. If calendar is EMPTY, set calendar to "iso8601".
|
||||
calendar = result.calendar.value_or("iso8601"_string);
|
||||
|
||||
// i. Set calendar to ? CanonicalizeCalendar(calendar).
|
||||
calendar = TRY(canonicalize_calendar(vm, calendar));
|
||||
|
||||
// j. Let isoDate be CreateISODateRecord(result.[[Year]], result.[[Month]], result.[[Day]]).
|
||||
iso_date = create_iso_date_record(*result.year, result.month, result.day);
|
||||
|
||||
// k. Let time be result.[[Time]].
|
||||
time = result.time;
|
||||
}
|
||||
|
||||
// 7. If timeZone is UNSET, then
|
||||
if (!time_zone.has_value()) {
|
||||
// a. Let plainDate be ? CreateTemporalDate(isoDate, calendar).
|
||||
auto plain_date = TRY(create_temporal_date(vm, iso_date, move(calendar)));
|
||||
|
||||
// b. Return the Record { [[PlainRelativeTo]]: plainDate, [[ZonedRelativeTo]]: undefined }.
|
||||
return RelativeTo { .plain_relative_to = plain_date, .zoned_relative_to = {} };
|
||||
}
|
||||
|
||||
// FIXME: 8. If offsetBehaviour is OPTION, then
|
||||
// FIXME: a. Let offsetNs be ! ParseDateTimeUTCOffset(offsetString).
|
||||
// FIXME: 9. Else,
|
||||
// FIXME: a. Let offsetNs be 0.
|
||||
// FIXME: 10. Let epochNanoseconds be ? InterpretISODateTimeOffset(isoDate, time, offsetBehaviour, offsetNs, timeZone, compatible, reject, matchBehaviour).
|
||||
// FIXME: 11. Let zonedRelativeTo be ! CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar).
|
||||
// FIXME: 12. Return the Record { [[PlainRelativeTo]]: undefined, [[ZonedRelativeTo]]: zonedRelativeTo }.
|
||||
|
||||
return RelativeTo { .plain_relative_to = {}, .zoned_relative_to = {} };
|
||||
}
|
||||
|
||||
|
|
|
@ -129,9 +129,8 @@ struct SecondsStringPrecision {
|
|||
};
|
||||
|
||||
struct RelativeTo {
|
||||
// FIXME: Make these objects represent their actual types when we re-implement them.
|
||||
GC::Ptr<JS::Object> plain_relative_to; // [[PlainRelativeTo]]
|
||||
GC::Ptr<JS::Object> zoned_relative_to; // [[ZonedRelativeTo]]
|
||||
GC::Ptr<PlainDate> plain_relative_to; // [[PlainRelativeTo]]
|
||||
GC::Ptr<Object> zoned_relative_to; // FIXME: [[ZonedRelativeTo]]
|
||||
};
|
||||
|
||||
struct DifferenceSettings {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Temporal/Calendar.h>
|
||||
#include <LibJS/Runtime/Temporal/Instant.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDate.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
|
||||
|
@ -54,6 +55,19 @@ bool iso_date_time_within_limits(ISODateTime iso_date_time)
|
|||
return true;
|
||||
}
|
||||
|
||||
// 5.5.5 InterpretTemporalDateTimeFields ( calendar, fields, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-interprettemporaldatetimefields
|
||||
ThrowCompletionOr<ISODateTime> interpret_temporal_date_time_fields(VM& vm, StringView calendar, CalendarFields& fields, Overflow overflow)
|
||||
{
|
||||
// 1. Let isoDate be ? CalendarDateFromFields(calendar, fields, overflow).
|
||||
auto iso_date = TRY(calendar_date_from_fields(vm, calendar, fields, overflow));
|
||||
|
||||
// 2. Let time be ? RegulateTime(fields.[[Hour]], fields.[[Minute]], fields.[[Second]], fields.[[Millisecond]], fields.[[Microsecond]], fields.[[Nanosecond]], overflow).
|
||||
auto time = TRY(regulate_time(vm, *fields.hour, *fields.minute, *fields.second, *fields.millisecond, *fields.microsecond, *fields.nanosecond, overflow));
|
||||
|
||||
// 3. Return CombineISODateAndTimeRecord(isoDate, time).
|
||||
return combine_iso_date_and_time_record(iso_date, time);
|
||||
}
|
||||
|
||||
// 5.5.7 BalanceISODateTime ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/proposal-temporal/#sec-temporal-balanceisodatetime
|
||||
ISODateTime balance_iso_date_time(double year, double month, double day, double hour, double minute, double second, double millisecond, double microsecond, double nanosecond)
|
||||
{
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace JS::Temporal {
|
|||
|
||||
ISODateTime combine_iso_date_and_time_record(ISODate, Time);
|
||||
bool iso_date_time_within_limits(ISODateTime);
|
||||
ThrowCompletionOr<ISODateTime> interpret_temporal_date_time_fields(VM&, StringView calendar, CalendarFields&, Overflow);
|
||||
ISODateTime balance_iso_date_time(double year, double month, double day, double hour, double minute, double second, double millisecond, double microsecond, double nanosecond);
|
||||
|
||||
}
|
||||
|
|
|
@ -46,6 +46,46 @@ Time noon_time_record()
|
|||
return { .days = 0, .hour = 12, .minute = 0, .second = 0, .millisecond = 0, .microsecond = 0, .nanosecond = 0 };
|
||||
}
|
||||
|
||||
// 4.5.8 RegulateTime ( hour, minute, second, millisecond, microsecond, nanosecond, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-regulatetime
|
||||
ThrowCompletionOr<Time> regulate_time(VM& vm, double hour, double minute, double second, double millisecond, double microsecond, double nanosecond, Overflow overflow)
|
||||
{
|
||||
switch (overflow) {
|
||||
// 1. If overflow is CONSTRAIN, then
|
||||
case Overflow::Constrain:
|
||||
// a. Set hour to the result of clamping hour between 0 and 23.
|
||||
hour = clamp(hour, 0, 23);
|
||||
|
||||
// b. Set minute to the result of clamping minute between 0 and 59.
|
||||
minute = clamp(minute, 0, 59);
|
||||
|
||||
// c. Set second to the result of clamping second between 0 and 59.
|
||||
second = clamp(second, 0, 59);
|
||||
|
||||
// d. Set millisecond to the result of clamping millisecond between 0 and 999.
|
||||
millisecond = clamp(millisecond, 0, 999);
|
||||
|
||||
// e. Set microsecond to the result of clamping microsecond between 0 and 999.
|
||||
microsecond = clamp(microsecond, 0, 999);
|
||||
|
||||
// f. Set nanosecond to the result of clamping nanosecond between 0 and 999.
|
||||
nanosecond = clamp(nanosecond, 0, 999);
|
||||
|
||||
break;
|
||||
|
||||
// 2. Else,
|
||||
case Overflow::Reject:
|
||||
// a. Assert: overflow is REJECT.
|
||||
// b. If IsValidTime(hour, minute, second, millisecond, microsecond, nanosecond) is false, throw a RangeError exception.
|
||||
if (!is_valid_time(hour, minute, second, millisecond, microsecond, nanosecond))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainTime);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// 3. Return CreateTimeRecord(hour, minute, second, millisecond, microsecond,nanosecond).
|
||||
return create_time_record(hour, minute, second, millisecond, microsecond, nanosecond);
|
||||
}
|
||||
|
||||
// 4.5.9 IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidtime
|
||||
bool is_valid_time(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond)
|
||||
{
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Temporal/ISORecords.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
@ -15,6 +16,7 @@ namespace JS::Temporal {
|
|||
Time create_time_record(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond, double delta_days = 0);
|
||||
Time midnight_time_record();
|
||||
Time noon_time_record();
|
||||
ThrowCompletionOr<Time> regulate_time(VM&, double hour, double minute, double second, double millisecond, double microsecond, double nanosecond, Overflow);
|
||||
bool is_valid_time(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond);
|
||||
Time balance_time(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond);
|
||||
|
||||
|
|
|
@ -53,8 +53,14 @@ ThrowCompletionOr<String> to_temporal_time_zone_identifier(VM& vm, Value tempora
|
|||
if (!temporal_time_zone_like.is_string())
|
||||
return vm.throw_completion<TypeError>(ErrorType::TemporalInvalidTimeZoneName, temporal_time_zone_like);
|
||||
|
||||
return to_temporal_time_zone_identifier(vm, temporal_time_zone_like.as_string().utf8_string_view());
|
||||
}
|
||||
|
||||
// 11.1.8 ToTemporalTimeZoneIdentifier ( temporalTimeZoneLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaltimezoneidentifier
|
||||
ThrowCompletionOr<String> to_temporal_time_zone_identifier(VM& vm, StringView temporal_time_zone_like)
|
||||
{
|
||||
// 3. Let parseResult be ? ParseTemporalTimeZoneString(temporalTimeZoneLike).
|
||||
auto parse_result = TRY(parse_temporal_time_zone_string(vm, temporal_time_zone_like.as_string().utf8_string_view()));
|
||||
auto parse_result = TRY(parse_temporal_time_zone_string(vm, temporal_time_zone_like));
|
||||
|
||||
// 4. Let offsetMinutes be parseResult.[[OffsetMinutes]].
|
||||
// 5. If offsetMinutes is not empty, return FormatOffsetTimeZoneIdentifier(offsetMinutes).
|
||||
|
|
|
@ -30,6 +30,7 @@ enum class Disambiguation {
|
|||
|
||||
String format_offset_time_zone_identifier(i64 offset_minutes, Optional<TimeStyle> = {});
|
||||
ThrowCompletionOr<String> to_temporal_time_zone_identifier(VM&, Value temporal_time_zone_like);
|
||||
ThrowCompletionOr<String> to_temporal_time_zone_identifier(VM&, StringView temporal_time_zone_like);
|
||||
ThrowCompletionOr<Crypto::SignedBigInteger> get_epoch_nanoseconds_for(VM&, StringView time_zone, ISODateTime const&, Disambiguation);
|
||||
ThrowCompletionOr<Crypto::SignedBigInteger> disambiguate_possible_epoch_nanoseconds(VM&, Vector<Crypto::SignedBigInteger> possible_epoch_ns, StringView time_zone, ISODateTime const&, Disambiguation);
|
||||
ThrowCompletionOr<Vector<Crypto::SignedBigInteger>> get_possible_epoch_nanoseconds(VM&, StringView time_zone, ISODateTime const&);
|
||||
|
|
Loading…
Add table
Reference in a new issue