diff --git a/Libraries/LibJS/Runtime/Temporal/Duration.cpp b/Libraries/LibJS/Runtime/Temporal/Duration.cpp index 1a59e9d2e92..b8da5f71a5c 100644 --- a/Libraries/LibJS/Runtime/Temporal/Duration.cpp +++ b/Libraries/LibJS/Runtime/Temporal/Duration.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -796,6 +797,32 @@ i8 time_duration_sign(TimeDuration const& time_duration) return 0; } +// 7.5.29 DateDurationDays ( dateDuration, plainRelativeTo ), https://tc39.es/proposal-temporal/#sec-temporal-datedurationdays +ThrowCompletionOr date_duration_days(VM& vm, DateDuration const& date_duration, PlainDate const& plain_relative_to) +{ + // 1. Let yearsMonthsWeeksDuration be ! AdjustDateDurationRecord(dateDuration, 0). + auto years_months_weeks_duration = MUST(adjust_date_duration_record(vm, date_duration, 0)); + + // 2. If DateDurationSign(yearsMonthsWeeksDuration) = 0, return dateDuration.[[Days]]. + if (date_duration_sign(years_months_weeks_duration) == 0) + return date_duration.days; + + // 3. Let later be ? CalendarDateAdd(plainRelativeTo.[[Calendar]], plainRelativeTo.[[ISODate]], yearsMonthsWeeksDuration, CONSTRAIN). + auto later = TRY(calendar_date_add(vm, plain_relative_to.calendar(), plain_relative_to.iso_date(), years_months_weeks_duration, Overflow::Constrain)); + + // 4. Let epochDays1 be ISODateToEpochDays(plainRelativeTo.[[ISODate]].[[Year]], plainRelativeTo.[[ISODate]].[[Month]] - 1, plainRelativeTo.[[ISODate]].[[Day]]). + auto epoch_days1 = iso_date_to_epoch_days(plain_relative_to.iso_date().year, plain_relative_to.iso_date().month - 1, plain_relative_to.iso_date().day); + + // 5. Let epochDays2 be ISODateToEpochDays(later.[[Year]], later.[[Month]] - 1, later.[[Day]]). + auto epoch_days2 = iso_date_to_epoch_days(later.year, later.month - 1, later.day); + + // 6. Let yearsMonthsWeeksInDays be epochDays2 - epochDays1. + auto years_months_weeks_in_days = epoch_days2 - epoch_days1; + + // 7. Return dateDuration.[[Days]] + yearsMonthsWeeksInDays. + return date_duration.days + years_months_weeks_in_days; +} + // 7.5.30 RoundTimeDuration ( timeDuration, increment, unit, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-roundtimeduration ThrowCompletionOr round_time_duration(VM& vm, TimeDuration const& time_duration, Crypto::UnsignedBigInteger const& increment, Unit unit, RoundingMode rounding_mode) { diff --git a/Libraries/LibJS/Runtime/Temporal/Duration.h b/Libraries/LibJS/Runtime/Temporal/Duration.h index 13aaf86821d..f405264c6f5 100644 --- a/Libraries/LibJS/Runtime/Temporal/Duration.h +++ b/Libraries/LibJS/Runtime/Temporal/Duration.h @@ -134,6 +134,7 @@ i8 compare_time_duration(TimeDuration const&, TimeDuration const&); TimeDuration time_duration_from_epoch_nanoseconds_difference(TimeDuration const&, TimeDuration const&); ThrowCompletionOr round_time_duration_to_increment(VM&, TimeDuration const&, Crypto::UnsignedBigInteger const& increment, RoundingMode); i8 time_duration_sign(TimeDuration const&); +ThrowCompletionOr date_duration_days(VM&, DateDuration const&, PlainDate const&); ThrowCompletionOr round_time_duration(VM&, TimeDuration const&, Crypto::UnsignedBigInteger const& increment, Unit, RoundingMode); ThrowCompletionOr nudge_to_calendar_unit(VM&, i8 sign, InternalDuration const&, TimeDuration const& dest_epoch_ns, ISODateTime const&, Optional time_zone, StringView calendar, u64 increment, Unit, RoundingMode); ThrowCompletionOr nudge_to_zoned_time(VM&, i8 sign, InternalDuration const&, ISODateTime const&, StringView time_zone, StringView calendar, u64 increment, Unit, RoundingMode); diff --git a/Libraries/LibJS/Runtime/Temporal/DurationConstructor.cpp b/Libraries/LibJS/Runtime/Temporal/DurationConstructor.cpp index c848dbbe728..b36c25c42f9 100644 --- a/Libraries/LibJS/Runtime/Temporal/DurationConstructor.cpp +++ b/Libraries/LibJS/Runtime/Temporal/DurationConstructor.cpp @@ -133,7 +133,7 @@ JS_DEFINE_NATIVE_FUNCTION(DurationConstructor::compare) // 6. Let zonedRelativeTo be relativeToRecord.[[ZonedRelativeTo]]. // 7. Let plainRelativeTo be relativeToRecord.[[PlainRelativeTo]]. - auto [zoned_relative_to, plain_relative_to] = relative_to_record; + auto [plain_relative_to, zoned_relative_to] = relative_to_record; // 8. Let largestUnit1 be DefaultTemporalLargestUnit(one). auto largest_unit1 = default_temporal_largest_unit(one); @@ -169,8 +169,12 @@ JS_DEFINE_NATIVE_FUNCTION(DurationConstructor::compare) if (!plain_relative_to) return vm.throw_completion(ErrorType::TemporalMissingStartingPoint, "calendar units"); - // FIXME: b. Let days1 be ? DateDurationDays(duration1.[[Date]], plainRelativeTo). - // FIXME: c. Let days2 be ? DateDurationDays(duration2.[[Date]], plainRelativeTo). + // b. Let days1 be ? DateDurationDays(duration1.[[Date]], plainRelativeTo). + days1 = TRY(date_duration_days(vm, duration1.date, *plain_relative_to)); + + // c. Let days2 be ? DateDurationDays(duration2.[[Date]], plainRelativeTo). + days2 = TRY(date_duration_days(vm, duration2.date, *plain_relative_to)); + } // 14. Else, else { diff --git a/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.compare.js b/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.compare.js index 078192885d2..3eefcd66808 100644 --- a/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.compare.js +++ b/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.compare.js @@ -27,6 +27,26 @@ describe("correct behavior", () => { const duration2 = "P2D"; checkCommonResults(duration1, duration2); }); + + test("relative to plain date", () => { + const oneMonth = new Temporal.Duration(0, 1); + const thirtyDays = new Temporal.Duration(0, 0, 0, 30); + + let result = Temporal.Duration.compare(oneMonth, thirtyDays, { + relativeTo: Temporal.PlainDate.from("2018-04-01"), + }); + expect(result).toBe(0); + + result = Temporal.Duration.compare(oneMonth, thirtyDays, { + relativeTo: Temporal.PlainDate.from("2018-03-01"), + }); + expect(result).toBe(1); + + result = Temporal.Duration.compare(oneMonth, thirtyDays, { + relativeTo: Temporal.PlainDate.from("2018-02-01"), + }); + expect(result).toBe(-1); + }); }); describe("errors", () => {