From 06593b1894b22ed244ae7e996cfd340bf7b9bac9 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Fri, 22 Nov 2024 17:18:38 -0500 Subject: [PATCH] LibJS: Implement most of GetTemporalRelativeToOption Now that we have the Temporal.PlainDate object, we can flesh out this AO for such relative-to objects. --- .../Runtime/Temporal/AbstractOperations.cpp | 123 +++++++++++++++++- .../Runtime/Temporal/AbstractOperations.h | 5 +- .../LibJS/Runtime/Temporal/PlainDateTime.cpp | 14 ++ .../LibJS/Runtime/Temporal/PlainDateTime.h | 1 + .../LibJS/Runtime/Temporal/PlainTime.cpp | 40 ++++++ Libraries/LibJS/Runtime/Temporal/PlainTime.h | 2 + Libraries/LibJS/Runtime/Temporal/TimeZone.cpp | 8 +- Libraries/LibJS/Runtime/Temporal/TimeZone.h | 1 + 8 files changed, 189 insertions(+), 5 deletions(-) diff --git a/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp b/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp index 6fc6a482d2a..8f01fabd29d 100644 --- a/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp +++ b/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp @@ -409,7 +409,128 @@ ThrowCompletionOr 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 time_zone; + Optional offset_string; + ISODate iso_date; + Variant 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(object)) { + // i. Return the Record { [[PlainRelativeTo]]: value, [[ZonedRelativeTo]]: undefined }. + return RelativeTo { .plain_relative_to = static_cast(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(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 = {} }; } diff --git a/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h b/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h index 316751566bd..7f3e2b21c9b 100644 --- a/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h +++ b/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h @@ -129,9 +129,8 @@ struct SecondsStringPrecision { }; struct RelativeTo { - // FIXME: Make these objects represent their actual types when we re-implement them. - GC::Ptr plain_relative_to; // [[PlainRelativeTo]] - GC::Ptr zoned_relative_to; // [[ZonedRelativeTo]] + GC::Ptr plain_relative_to; // [[PlainRelativeTo]] + GC::Ptr zoned_relative_to; // FIXME: [[ZonedRelativeTo]] }; struct DifferenceSettings { diff --git a/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp b/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp index 6e2467fa83f..a06336892f6 100644 --- a/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp +++ b/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -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 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) { diff --git a/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h b/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h index 134f7fe6c61..5001fcdc131 100644 --- a/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h +++ b/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h @@ -14,6 +14,7 @@ namespace JS::Temporal { ISODateTime combine_iso_date_and_time_record(ISODate, Time); bool iso_date_time_within_limits(ISODateTime); +ThrowCompletionOr 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); } diff --git a/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp b/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp index 6cc5fcff5c9..0f145bd0db5 100644 --- a/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp +++ b/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp @@ -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