From 70ad66d3c0eb05e536de6eb1b4d3142c162b99b9 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Fri, 22 Nov 2024 18:16:57 -0500 Subject: [PATCH] LibJS: Begin implementing the relativeTo option of Duration.round --- .../Runtime/Temporal/DurationPrototype.cpp | 33 +++++-- .../LibJS/Runtime/Temporal/PlainDateTime.cpp | 88 +++++++++++++++++- .../LibJS/Runtime/Temporal/PlainDateTime.h | 4 + .../LibJS/Runtime/Temporal/PlainTime.cpp | 91 +++++++++++++++++++ Libraries/LibJS/Runtime/Temporal/PlainTime.h | 3 + .../Duration/Duration.prototype.round.js | 9 ++ 6 files changed, 219 insertions(+), 9 deletions(-) diff --git a/Libraries/LibJS/Runtime/Temporal/DurationPrototype.cpp b/Libraries/LibJS/Runtime/Temporal/DurationPrototype.cpp index 2fdeba377c5..9abef69a32f 100644 --- a/Libraries/LibJS/Runtime/Temporal/DurationPrototype.cpp +++ b/Libraries/LibJS/Runtime/Temporal/DurationPrototype.cpp @@ -5,8 +5,12 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include +#include +#include +#include namespace JS::Temporal { @@ -272,7 +276,7 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::round) // 10. Let relativeToRecord be ? GetTemporalRelativeToOption(roundTo). // 11. Let zonedRelativeTo be relativeToRecord.[[ZonedRelativeTo]]. // 12. Let plainRelativeTo be relativeToRecord.[[PlainRelativeTo]]. - auto [zoned_relative_to, plain_relative_to] = TRY(get_temporal_relative_to_option(vm, *round_to)); + auto [plain_relative_to, zoned_relative_to] = TRY(get_temporal_relative_to_option(vm, *round_to)); // 13. Let roundingIncrement be ? GetRoundingIncrementOption(roundTo). auto rounding_increment = TRY(get_rounding_increment_option(vm, *round_to)); @@ -362,13 +366,26 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::round) // a. Let internalDuration be ToInternalDurationRecordWith24HourDays(duration). auto internal_duration = to_internal_duration_record_with_24_hour_days(vm, duration); - // FIXME: b. Let targetTime be AddTime(MidnightTimeRecord(), internalDuration.[[Time]]). - // FIXME: c. Let calendar be plainRelativeTo.[[Calendar]]. - // FIXME: d. Let dateDuration be ! AdjustDateDurationRecord(internalDuration.[[Date]], targetTime.[[Days]]). - // FIXME: e. Let targetDate be ? CalendarDateAdd(calendar, plainRelativeTo.[[ISODate]], dateDuration, constrain). - // FIXME: f. Let isoDateTime be CombineISODateAndTimeRecord(plainRelativeTo.[[ISODate]], MidnightTimeRecord()). - // FIXME: g. Let targetDateTime be CombineISODateAndTimeRecord(targetDate, targetTime). - // FIXME: h. Set internalDuration to ? DifferencePlainDateTimeWithRounding(isoDateTime, targetDateTime, calendar, largestUnit, roundingIncrement, smallestUnit, roundingMode). + // b. Let targetTime be AddTime(MidnightTimeRecord(), internalDuration.[[Time]]). + auto target_time = add_time(midnight_time_record(), internal_duration.time); + + // c. Let calendar be plainRelativeTo.[[Calendar]]. + auto const& calendar = plain_relative_to->calendar(); + + // d. Let dateDuration be ! AdjustDateDurationRecord(internalDuration.[[Date]], targetTime.[[Days]]). + auto date_duration = MUST(adjust_date_duration_record(vm, internal_duration.date, target_time.days)); + + // e. Let targetDate be ? CalendarDateAdd(calendar, plainRelativeTo.[[ISODate]], dateDuration, CONSTRAIN). + auto target_date = TRY(calendar_date_add(vm, calendar, plain_relative_to->iso_date(), date_duration, Overflow::Constrain)); + + // f. Let isoDateTime be CombineISODateAndTimeRecord(plainRelativeTo.[[ISODate]], MidnightTimeRecord()). + auto iso_date_time = combine_iso_date_and_time_record(plain_relative_to->iso_date(), midnight_time_record()); + + // g. Let targetDateTime be CombineISODateAndTimeRecord(targetDate, targetTime). + auto target_date_time = combine_iso_date_and_time_record(target_date, target_time); + + // h. Set internalDuration to ? DifferencePlainDateTimeWithRounding(isoDateTime, targetDateTime, calendar, largestUnit, roundingIncrement, smallestUnit, roundingMode). + internal_duration = TRY(difference_plain_date_time_with_rounding(vm, iso_date_time, target_date_time, calendar, largest_unit_value, rounding_increment, smallest_unit_value, rounding_mode)); // i. Return ? TemporalDurationFromInternal(internalDuration, largestUnit). return TRY(temporal_duration_from_internal(vm, internal_duration, largest_unit_value)); diff --git a/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp b/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp index a06336892f6..807c144575e 100644 --- a/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp +++ b/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp @@ -6,8 +6,8 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include #include +#include #include #include #include @@ -81,4 +81,90 @@ ISODateTime balance_iso_date_time(double year, double month, double day, double return combine_iso_date_and_time_record(balanced_date, balanced_time); } +// 5.5.10 CompareISODateTime ( isoDateTime1, isoDateTime2 ), https://tc39.es/proposal-temporal/#sec-temporal-compareisodatetime +i8 compare_iso_date_time(ISODateTime const& iso_date_time1, ISODateTime const& iso_date_time2) +{ + // 1. Let dateResult be CompareISODate(isoDateTime1.[[ISODate]], isoDateTime2.[[ISODate]]). + auto date_result = compare_iso_date(iso_date_time1.iso_date, iso_date_time2.iso_date); + + // 2. If dateResult ≠ 0, return dateResult. + if (date_result != 0) + return date_result; + + // 3. Return CompareTimeRecord(isoDateTime1.[[Time]], isoDateTime2.[[Time]]). + return compare_time_record(iso_date_time1.time, iso_date_time2.time); +} + +// 5.5.12 DifferenceISODateTime ( isoDateTime1, isoDateTime2, calendar, largestUnit ), https://tc39.es/proposal-temporal/#sec-temporal-differenceisodatetime +ThrowCompletionOr difference_iso_date_time(VM& vm, ISODateTime const& iso_date_time1, ISODateTime const& iso_date_time2, StringView calendar, Unit largest_unit) +{ + // 1. Assert: ISODateTimeWithinLimits(isoDateTime1) is true. + VERIFY(iso_date_time_within_limits(iso_date_time1)); + + // 2. Assert: ISODateTimeWithinLimits(isoDateTime2) is true. + VERIFY(iso_date_time_within_limits(iso_date_time2)); + + // 3. Let timeDuration be DifferenceTime(isoDateTime1.[[Time]], isoDateTime2.[[Time]]). + auto time_duration = difference_time(iso_date_time1.time, iso_date_time2.time); + + // 4. Let timeSign be TimeDurationSign(timeDuration). + auto time_sign = time_duration_sign(time_duration); + + // 5. Let dateSign be CompareISODate(isoDateTime2.[[ISODate]], isoDateTime1.[[ISODate]]). + auto date_sign = compare_iso_date(iso_date_time2.iso_date, iso_date_time1.iso_date); + + // 6. Let adjustedDate be isoDateTime2.[[ISODate]]. + auto adjusted_date = iso_date_time2.iso_date; + + // 7. If timeSign = -dateSign, then + if (time_sign == -date_sign) { + // a. Set adjustedDate to BalanceISODate(adjustedDate.[[Year]], adjustedDate.[[Month]], adjustedDate.[[Day]] + timeSign). + adjusted_date = balance_iso_date(adjusted_date.year, adjusted_date.month, static_cast(adjusted_date.day) + time_sign); + + // b. Set timeDuration to ? Add24HourDaysToTimeDuration(timeDuration, -timeSign). + time_duration = TRY(add_24_hour_days_to_time_duration(vm, time_duration, -time_sign)); + } + + // 8. Let dateLargestUnit be LargerOfTwoTemporalUnits(DAY, largestUnit). + auto date_largest_unit = larger_of_two_temporal_units(Unit::Day, largest_unit); + + // 9. Let dateDifference be CalendarDateUntil(calendar, isoDateTime1.[[ISODate]], adjustedDate, dateLargestUnit). + auto date_difference = calendar_date_until(vm, calendar, iso_date_time1.iso_date, adjusted_date, date_largest_unit); + + // 10. If largestUnit is not dateLargestUnit, then + if (largest_unit != date_largest_unit) { + // a. Set timeDuration to ? Add24HourDaysToTimeDuration(timeDuration, dateDifference.[[Days]]). + time_duration = TRY(add_24_hour_days_to_time_duration(vm, time_duration, date_difference.days)); + + // b. Set dateDifference.[[Days]] to 0. + date_difference.days = 0; + } + + // 11. Return ? CombineDateAndTimeDuration(dateDifference, timeDuration). + return TRY(combine_date_and_time_duration(vm, date_difference, move(time_duration))); +} + +// 5.5.13 DifferencePlainDateTimeWithRounding ( isoDateTime1, isoDateTime2, calendar, largestUnit, roundingIncrement, smallestUnit, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-differenceplaindatetimewithrounding +ThrowCompletionOr difference_plain_date_time_with_rounding(VM& vm, ISODateTime const& iso_date_time1, ISODateTime const& iso_date_time2, StringView calendar, Unit largest_unit, u64 rounding_increment, Unit smallest_unit, RoundingMode rounding_mode) +{ + // 1. If CompareISODateTime(isoDateTime1, isoDateTime2) = 0, then + if (compare_iso_date_time(iso_date_time1, iso_date_time2) == 0) { + // a. Return ! CombineDateAndTimeDuration(ZeroDateDuration(), 0). + return MUST(combine_date_and_time_duration(vm, zero_date_duration(vm), TimeDuration { 0 })); + } + + // 2. Let diff be ? DifferenceISODateTime(isoDateTime1, isoDateTime2, calendar, largestUnit). + auto diff = TRY(difference_iso_date_time(vm, iso_date_time1, iso_date_time2, calendar, largest_unit)); + + // 3. If smallestUnit is NANOSECOND and roundingIncrement = 1, return diff. + if (smallest_unit == Unit::Nanosecond && rounding_increment == 1) + return diff; + + // 4. Let destEpochNs be GetUTCEpochNanoseconds(isoDateTime2). + auto dest_epoch_ns = get_utc_epoch_nanoseconds(iso_date_time2); + + // 5. Return ? RoundRelativeDuration(diff, destEpochNs, isoDateTime1, UNSET, calendar, largestUnit, roundingIncrement, smallestUnit, roundingMode). + return TRY(round_relative_duration(vm, diff, dest_epoch_ns, iso_date_time1, {}, calendar, largest_unit, rounding_increment, smallest_unit, rounding_mode)); +} + } diff --git a/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h b/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h index 5001fcdc131..1e6d1bfde11 100644 --- a/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h +++ b/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h @@ -8,6 +8,7 @@ #pragma once +#include #include namespace JS::Temporal { @@ -16,5 +17,8 @@ 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); +i8 compare_iso_date_time(ISODateTime const&, ISODateTime const&); +ThrowCompletionOr difference_iso_date_time(VM&, ISODateTime const&, ISODateTime const&, StringView calendar, Unit largest_unit); +ThrowCompletionOr difference_plain_date_time_with_rounding(VM&, ISODateTime const&, ISODateTime const&, StringView calendar, Unit largest_unit, u64 rounding_increment, Unit smallest_unit, RoundingMode); } diff --git a/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp b/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp index 0f145bd0db5..334383f41b7 100644 --- a/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp +++ b/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp @@ -8,6 +8,8 @@ #include #include +#include +#include #include #include @@ -46,6 +48,37 @@ Time noon_time_record() return { .days = 0, .hour = 12, .minute = 0, .second = 0, .millisecond = 0, .microsecond = 0, .nanosecond = 0 }; } +// 4.5.5 DifferenceTime ( time1, time2 ), https://tc39.es/proposal-temporal/#sec-temporal-differencetime +TimeDuration difference_time(Time const& time1, Time const& time2) +{ + // 1. Let hours be time2.[[Hour]] - time1.[[Hour]]. + auto hours = static_cast(time2.hour) - static_cast(time1.hour); + + // 2. Let minutes be time2.[[Minute]] - time1.[[Minute]]. + auto minutes = static_cast(time2.minute) - static_cast(time1.minute); + + // 3. Let seconds be time2.[[Second]] - time1.[[Second]]. + auto seconds = static_cast(time2.second) - static_cast(time1.second); + + // 4. Let milliseconds be time2.[[Millisecond]] - time1.[[Millisecond]]. + auto milliseconds = static_cast(time2.millisecond) - static_cast(time1.millisecond); + + // 5. Let microseconds be time2.[[Microsecond]] - time1.[[Microsecond]]. + auto microseconds = static_cast(time2.microsecond) - static_cast(time1.microsecond); + + // 6. Let nanoseconds be time2.[[Nanosecond]] - time1.[[Nanosecond]]. + auto nanoseconds = static_cast(time2.nanosecond) - static_cast(time1.nanosecond); + + // 7. Let timeDuration be TimeDurationFromComponents(hours, minutes, seconds, milliseconds, microseconds, nanoseconds). + auto time_duration = time_duration_from_components(hours, minutes, seconds, milliseconds, microseconds, nanoseconds); + + // 8. Assert: abs(timeDuration) < nsPerDay. + VERIFY(time_duration.unsigned_value() < NANOSECONDS_PER_DAY); + + // 9. Return timeDuration. + return time_duration; +} + // 4.5.8 RegulateTime ( hour, minute, second, millisecond, microsecond, nanosecond, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-regulatetime ThrowCompletionOr