Преглед изворни кода

LibJS: Begin implementing the relativeTo option of Duration.round

Timothy Flynn пре 8 месеци
родитељ
комит
70ad66d3c0

+ 25 - 8
Libraries/LibJS/Runtime/Temporal/DurationPrototype.cpp

@@ -5,8 +5,12 @@
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
+#include <LibJS/Runtime/Temporal/Calendar.h>
 #include <LibJS/Runtime/Temporal/Duration.h>
 #include <LibJS/Runtime/Temporal/DurationPrototype.h>
+#include <LibJS/Runtime/Temporal/PlainDate.h>
+#include <LibJS/Runtime/Temporal/PlainDateTime.h>
+#include <LibJS/Runtime/Temporal/PlainTime.h>
 
 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));

+ 87 - 1
Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp

@@ -6,8 +6,8 @@
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
-#include <LibJS/Runtime/Temporal/AbstractOperations.h>
 #include <LibJS/Runtime/Temporal/Calendar.h>
+#include <LibJS/Runtime/Temporal/Duration.h>
 #include <LibJS/Runtime/Temporal/Instant.h>
 #include <LibJS/Runtime/Temporal/PlainDate.h>
 #include <LibJS/Runtime/Temporal/PlainDateTime.h>
@@ -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<InternalDuration> 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<double>(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<InternalDuration> 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));
+}
+
 }

+ 4 - 0
Libraries/LibJS/Runtime/Temporal/PlainDateTime.h

@@ -8,6 +8,7 @@
 
 #pragma once
 
+#include <LibJS/Runtime/Temporal/AbstractOperations.h>
 #include <LibJS/Runtime/Temporal/ISORecords.h>
 
 namespace JS::Temporal {
@@ -16,5 +17,8 @@ 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);
+i8 compare_iso_date_time(ISODateTime const&, ISODateTime const&);
+ThrowCompletionOr<InternalDuration> difference_iso_date_time(VM&, ISODateTime const&, ISODateTime const&, StringView calendar, Unit largest_unit);
+ThrowCompletionOr<InternalDuration> difference_plain_date_time_with_rounding(VM&, ISODateTime const&, ISODateTime const&, StringView calendar, Unit largest_unit, u64 rounding_increment, Unit smallest_unit, RoundingMode);
 
 }

+ 91 - 0
Libraries/LibJS/Runtime/Temporal/PlainTime.cpp

@@ -8,6 +8,8 @@
 
 #include <AK/Assertions.h>
 #include <LibJS/Runtime/AbstractOperations.h>
+#include <LibJS/Runtime/Temporal/Duration.h>
+#include <LibJS/Runtime/Temporal/Instant.h>
 #include <LibJS/Runtime/Temporal/PlainTime.h>
 #include <math.h>
 
@@ -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<double>(time2.hour) - static_cast<double>(time1.hour);
+
+    // 2. Let minutes be time2.[[Minute]] - time1.[[Minute]].
+    auto minutes = static_cast<double>(time2.minute) - static_cast<double>(time1.minute);
+
+    // 3. Let seconds be time2.[[Second]] - time1.[[Second]].
+    auto seconds = static_cast<double>(time2.second) - static_cast<double>(time1.second);
+
+    // 4. Let milliseconds be time2.[[Millisecond]] - time1.[[Millisecond]].
+    auto milliseconds = static_cast<double>(time2.millisecond) - static_cast<double>(time1.millisecond);
+
+    // 5. Let microseconds be time2.[[Microsecond]] - time1.[[Microsecond]].
+    auto microseconds = static_cast<double>(time2.microsecond) - static_cast<double>(time1.microsecond);
+
+    // 6. Let nanoseconds be time2.[[Nanosecond]] - time1.[[Nanosecond]].
+    auto nanoseconds = static_cast<double>(time2.nanosecond) - static_cast<double>(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<Time> regulate_time(VM& vm, double hour, double minute, double second, double millisecond, double microsecond, double nanosecond, Overflow overflow)
 {
@@ -172,4 +205,62 @@ Time balance_time(double hour, double minute, double second, double millisecond,
     return create_time_record(hour, minute, second, millisecond, microsecond, nanosecond, delta_days);
 }
 
+// 4.5.14 CompareTimeRecord ( time1, time2 ), https://tc39.es/proposal-temporal/#sec-temporal-comparetimerecord
+i8 compare_time_record(Time const& time1, Time const& time2)
+{
+    // 1. If time1.[[Hour]] > time2.[[Hour]], return 1.
+    if (time1.hour > time2.hour)
+        return 1;
+    // 2. If time1.[[Hour]] < time2.[[Hour]], return -1.
+    if (time1.hour < time2.hour)
+        return -1;
+
+    // 3. If time1.[[Minute]] > time2.[[Minute]], return 1.
+    if (time1.minute > time2.minute)
+        return 1;
+    // 4. If time1.[[Minute]] < time2.[[Minute]], return -1.
+    if (time1.minute < time2.minute)
+        return -1;
+
+    // 5. If time1.[[Second]] > time2.[[Second]], return 1.
+    if (time1.second > time2.second)
+        return 1;
+    // 6. If time1.[[Second]] < time2.[[Second]], return -1.
+    if (time1.second < time2.second)
+        return -1;
+
+    // 7. If time1.[[Millisecond]] > time2.[[Millisecond]], return 1.
+    if (time1.millisecond > time2.millisecond)
+        return 1;
+    // 8. If time1.[[Millisecond]] < time2.[[Millisecond]], return -1.
+    if (time1.millisecond < time2.millisecond)
+        return -1;
+
+    // 9. If time1.[[Microsecond]] > time2.[[Microsecond]], return 1.
+    if (time1.microsecond > time2.microsecond)
+        return 1;
+    // 10. If time1.[[Microsecond]] < time2.[[Microsecond]], return -1.
+    if (time1.microsecond < time2.microsecond)
+        return -1;
+
+    // 11. If time1.[[Nanosecond]] > time2.[[Nanosecond]], return 1.
+    if (time1.nanosecond > time2.nanosecond)
+        return 1;
+    // 12. If time1.[[Nanosecond]] < time2.[[Nanosecond]], return -1.
+    if (time1.nanosecond < time2.nanosecond)
+        return -1;
+
+    // 13. Return 0.
+    return 0;
+}
+
+// 4.5.15 AddTime ( time, timeDuration ), https://tc39.es/proposal-temporal/#sec-temporal-addtime
+Time add_time(Time const& time, TimeDuration const& time_duration)
+{
+    auto nanoseconds = time_duration.plus(TimeDuration { static_cast<i64>(time.nanosecond) });
+
+    // 1. Return BalanceTime(time.[[Hour]], time.[[Minute]], time.[[Second]], time.[[Millisecond]], time.[[Microsecond]], time.[[Nanosecond]] + timeDuration).
+    return balance_time(time.hour, time.minute, time.second, time.millisecond, time.microsecond, nanoseconds.to_double());
+}
+
 }

+ 3 - 0
Libraries/LibJS/Runtime/Temporal/PlainTime.h

@@ -16,8 +16,11 @@ 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();
+TimeDuration difference_time(Time const&, Time const&);
 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);
+i8 compare_time_record(Time const&, Time const&);
+Time add_time(Time const&, TimeDuration const& time_duration);
 
 }

+ 9 - 0
Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.prototype.round.js

@@ -89,6 +89,15 @@ describe("correct behavior", () => {
             }
         }
     });
+
+    test("relative to plain date", () => {
+        const duration = new Temporal.Duration(0, 0, 0, 31);
+
+        ["2000-01-01", "2000-01-01T00:00", "2000-01-01T00:00[u-ca=iso8601]"].forEach(relativeTo => {
+            const result = duration.round({ largestUnit: "months", relativeTo });
+            expect(result.months).toBe(1);
+        });
+    });
 });
 
 describe("errors", () => {