mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-01 13:49:16 +00:00
LibJS: Begin implementing the relativeTo option of Duration.total
This commit is contained in:
parent
70ad66d3c0
commit
d0149d8fc0
Notes:
github-actions[bot]
2024-11-23 13:46:56 +00:00
Author: https://github.com/trflynn89
Commit: d0149d8fc0
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2513
Reviewed-by: https://github.com/shannonbooth ✅
6 changed files with 89 additions and 18 deletions
|
@ -834,7 +834,7 @@ ThrowCompletionOr<TimeDuration> round_time_duration(VM& vm, TimeDuration const&
|
|||
}
|
||||
|
||||
// 7.5.31 TotalTimeDuration ( timeDuration, unit ), https://tc39.es/proposal-temporal/#sec-temporal-totaltimeduration
|
||||
double total_time_duration(TimeDuration const& time_duration, Unit unit)
|
||||
Crypto::BigFraction total_time_duration(TimeDuration const& time_duration, Unit unit)
|
||||
{
|
||||
// 1. Let divisor be the value in the "Length in Nanoseconds" column of the row of Table 21 whose "Value" column contains unit.
|
||||
auto const& divisor = temporal_unit_length_in_nanoseconds(unit);
|
||||
|
@ -844,8 +844,7 @@ double total_time_duration(TimeDuration const& time_duration, Unit unit)
|
|||
// or with software emulation such as in the SoftFP library.
|
||||
|
||||
// 3. Return timeDuration / divisor.
|
||||
auto result = Crypto::BigFraction { time_duration } / Crypto::BigFraction { Crypto::SignedBigInteger { divisor } };
|
||||
return result.to_double();
|
||||
return Crypto::BigFraction { time_duration } / Crypto::BigFraction { Crypto::SignedBigInteger { divisor } };
|
||||
}
|
||||
|
||||
// 7.5.33 NudgeToCalendarUnit ( sign, duration, destEpochNs, isoDateTime, timeZone, calendar, increment, unit, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-nudgetocalendarunit
|
||||
|
@ -1175,10 +1174,10 @@ ThrowCompletionOr<DurationNudgeResult> nudge_to_day_or_time(VM& vm, InternalDura
|
|||
time_duration.negate();
|
||||
|
||||
// 5. Let wholeDays be truncate(TotalTimeDuration(timeDuration, DAY)).
|
||||
auto whole_days = trunc(total_time_duration(time_duration, Unit::Day));
|
||||
auto whole_days = trunc(total_time_duration(time_duration, Unit::Day).to_double());
|
||||
|
||||
// 6. Let roundedWholeDays be truncate(TotalTimeDuration(roundedTime, DAY)).
|
||||
auto rounded_whole_days = trunc(total_time_duration(rounded_time, Unit::Day));
|
||||
auto rounded_whole_days = trunc(total_time_duration(rounded_time, Unit::Day).to_double());
|
||||
|
||||
// 7. Let dayDelta be roundedWholeDays - wholeDays.
|
||||
auto day_delta = rounded_whole_days - whole_days;
|
||||
|
@ -1374,6 +1373,28 @@ ThrowCompletionOr<InternalDuration> round_relative_duration(VM& vm, InternalDura
|
|||
return duration;
|
||||
}
|
||||
|
||||
// 7.5.38 TotalRelativeDuration ( duration, destEpochNs, isoDateTime, timeZone, calendar, unit ), https://tc39.es/proposal-temporal/#sec-temporal-totalrelativeduration
|
||||
ThrowCompletionOr<Crypto::BigFraction> total_relative_duration(VM& vm, InternalDuration const& duration, TimeDuration const& dest_epoch_ns, ISODateTime const& iso_date_time, Optional<StringView> time_zone, StringView calendar, Unit unit)
|
||||
{
|
||||
// 1. If IsCalendarUnit(unit) is true, or timeZone is not UNSET and unit is DAY, then
|
||||
if (is_calendar_unit(unit) || (time_zone.has_value() && unit == Unit::Day)) {
|
||||
// a. Let sign be InternalDurationSign(duration).
|
||||
auto sign = internal_duration_sign(duration);
|
||||
|
||||
// b. Let record be ? NudgeToCalendarUnit(sign, duration, destEpochNs, isoDateTime, timeZone, calendar, 1, unit, TRUNC).
|
||||
auto record = TRY(nudge_to_calendar_unit(vm, sign, duration, dest_epoch_ns, iso_date_time, time_zone, calendar, 1, unit, RoundingMode::Trunc));
|
||||
|
||||
// c. Return record.[[Total]].
|
||||
return record.total;
|
||||
}
|
||||
|
||||
// 2. Let timeDuration be ! Add24HourDaysToTimeDuration(duration.[[Time]], duration.[[Date]].[[Days]]).
|
||||
auto time_duration = MUST(add_24_hour_days_to_time_duration(vm, duration.time, duration.date.days));
|
||||
|
||||
// 3. Return TotalTimeDuration(timeDuration, unit).
|
||||
return total_time_duration(time_duration, unit);
|
||||
}
|
||||
|
||||
// 7.5.39 TemporalDurationToString ( duration, precision ), https://tc39.es/proposal-temporal/#sec-temporal-temporaldurationtostring
|
||||
String temporal_duration_to_string(Duration const& duration, Precision precision)
|
||||
{
|
||||
|
|
|
@ -136,12 +136,13 @@ ThrowCompletionOr<TimeDuration> round_time_duration_to_increment(VM&, TimeDurati
|
|||
i8 time_duration_sign(TimeDuration const&);
|
||||
ThrowCompletionOr<double> date_duration_days(VM&, DateDuration const&, PlainDate const&);
|
||||
ThrowCompletionOr<TimeDuration> round_time_duration(VM&, TimeDuration const&, Crypto::UnsignedBigInteger const& increment, Unit, RoundingMode);
|
||||
Crypto::BigFraction total_time_duration(TimeDuration const&, Unit);
|
||||
ThrowCompletionOr<CalendarNudgeResult> nudge_to_calendar_unit(VM&, i8 sign, InternalDuration const&, TimeDuration const& dest_epoch_ns, ISODateTime const&, Optional<StringView> time_zone, StringView calendar, u64 increment, Unit, RoundingMode);
|
||||
ThrowCompletionOr<DurationNudgeResult> nudge_to_zoned_time(VM&, i8 sign, InternalDuration const&, ISODateTime const&, StringView time_zone, StringView calendar, u64 increment, Unit, RoundingMode);
|
||||
ThrowCompletionOr<DurationNudgeResult> nudge_to_day_or_time(VM&, InternalDuration const&, TimeDuration const& dest_epoch_ns, Unit largest_unit, u64 increment, Unit smallest_unit, RoundingMode);
|
||||
ThrowCompletionOr<InternalDuration> bubble_relative_duration(VM&, i8 sign, InternalDuration, TimeDuration const& nudged_epoch_ns, ISODateTime const&, Optional<StringView> time_zone, StringView calendar, Unit largest_unit, Unit smallest_unit);
|
||||
ThrowCompletionOr<InternalDuration> round_relative_duration(VM&, InternalDuration, TimeDuration const& dest_epoch_ns, ISODateTime const&, Optional<StringView> time_zone, StringView calendar, Unit largest_unit, u64 increment, Unit smallest_unit, RoundingMode);
|
||||
double total_time_duration(TimeDuration const&, Unit);
|
||||
ThrowCompletionOr<Crypto::BigFraction> total_relative_duration(VM&, InternalDuration const&, TimeDuration const&, ISODateTime const&, Optional<StringView> time_zone, StringView calendar, Unit);
|
||||
String temporal_duration_to_string(Duration const&, Precision);
|
||||
ThrowCompletionOr<GC::Ref<Duration>> add_durations(VM&, ArithmeticOperation, Duration const&, Value);
|
||||
|
||||
|
|
|
@ -409,7 +409,7 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::round)
|
|||
auto fractional_days = total_time_duration(internal_duration.time, Unit::Day);
|
||||
|
||||
// b. Let days be RoundNumberToIncrement(fractionalDays, roundingIncrement, roundingMode).
|
||||
auto days = round_number_to_increment(fractional_days, rounding_increment, rounding_mode);
|
||||
auto days = round_number_to_increment(fractional_days.to_double(), rounding_increment, rounding_mode);
|
||||
|
||||
// c. Let dateDuration be ? CreateDateDurationRecord(0, 0, 0, days).
|
||||
auto date_duration = TRY(create_date_duration_record(vm, 0, 0, 0, days));
|
||||
|
@ -470,12 +470,12 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::total)
|
|||
// 7. Let relativeToRecord be ? GetTemporalRelativeToOption(totalOf).
|
||||
// 8. Let zonedRelativeTo be relativeToRecord.[[ZonedRelativeTo]].
|
||||
// 9. Let plainRelativeTo be relativeToRecord.[[PlainRelativeTo]].
|
||||
auto [zoned_relative_to, plain_relative_to] = TRY(get_temporal_relative_to_option(vm, *total_of));
|
||||
auto [plain_relative_to, zoned_relative_to] = TRY(get_temporal_relative_to_option(vm, *total_of));
|
||||
|
||||
// 10. Let unit be ? GetTemporalUnitValuedOption(totalOf, "unit", DATETIME, REQUIRED).
|
||||
auto unit = TRY(get_temporal_unit_valued_option(vm, *total_of, vm.names.unit, UnitGroup::DateTime, Required {})).get<Unit>();
|
||||
|
||||
double total = 0;
|
||||
Crypto::BigFraction total;
|
||||
|
||||
// 11. If zonedRelativeTo is not undefined, then
|
||||
if (zoned_relative_to) {
|
||||
|
@ -488,14 +488,29 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::total)
|
|||
}
|
||||
// 12. Else if plainRelativeTo is not undefined, then
|
||||
else if (plain_relative_to) {
|
||||
// FIXME: a. Let internalDuration be ToInternalDurationRecordWith24HourDays(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. Let total be ? DifferencePlainDateTimeWithTotal(isoDateTime, targetDateTime, calendar, unit).
|
||||
// a. Let internalDuration be ToInternalDurationRecordWith24HourDays(duration).
|
||||
auto internal_duration = to_internal_duration_record_with_24_hour_days(vm, duration);
|
||||
|
||||
// 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. Let total be ? DifferencePlainDateTimeWithTotal(isoDateTime, targetDateTime, calendar, unit).
|
||||
total = TRY(difference_plain_date_time_with_total(vm, iso_date_time, target_date_time, calendar, unit));
|
||||
}
|
||||
// 13. Else,
|
||||
else {
|
||||
|
@ -516,7 +531,7 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::total)
|
|||
}
|
||||
|
||||
// 14. Return 𝔽(total).
|
||||
return total;
|
||||
return total.to_double();
|
||||
}
|
||||
|
||||
// 7.3.22 Temporal.Duration.prototype.toString ( [ options ] ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.tostring
|
||||
|
|
|
@ -167,4 +167,27 @@ ThrowCompletionOr<InternalDuration> difference_plain_date_time_with_rounding(VM&
|
|||
return TRY(round_relative_duration(vm, diff, dest_epoch_ns, iso_date_time1, {}, calendar, largest_unit, rounding_increment, smallest_unit, rounding_mode));
|
||||
}
|
||||
|
||||
// 5.5.14 DifferencePlainDateTimeWithTotal ( isoDateTime1, isoDateTime2, calendar, unit ), https://tc39.es/proposal-temporal/#sec-temporal-differenceplaindatetimewithtotal
|
||||
ThrowCompletionOr<Crypto::BigFraction> difference_plain_date_time_with_total(VM& vm, ISODateTime const& iso_date_time1, ISODateTime const& iso_date_time2, StringView calendar, Unit unit)
|
||||
{
|
||||
// 1. If CompareISODateTime(isoDateTime1, isoDateTime2) = 0, then
|
||||
if (compare_iso_date_time(iso_date_time1, iso_date_time2) == 0) {
|
||||
// a. Return 0.
|
||||
return Crypto::BigFraction {};
|
||||
}
|
||||
|
||||
// 2. Let diff be ? DifferenceISODateTime(isoDateTime1, isoDateTime2, calendar, unit).
|
||||
auto diff = TRY(difference_iso_date_time(vm, iso_date_time1, iso_date_time2, calendar, unit));
|
||||
|
||||
// 3. If unit is NANOSECOND, return diff.[[Time]].
|
||||
if (unit == Unit::Nanosecond)
|
||||
return move(diff.time);
|
||||
|
||||
// 4. Let destEpochNs be GetUTCEpochNanoseconds(isoDateTime2).
|
||||
auto dest_epoch_ns = get_utc_epoch_nanoseconds(iso_date_time2);
|
||||
|
||||
// 5. Return ? TotalRelativeDuration(diff, destEpochNs, isoDateTime1, UNSET, calendar, unit).
|
||||
return TRY(total_relative_duration(vm, diff, dest_epoch_ns, iso_date_time1, {}, calendar, unit));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <LibCrypto/BigFraction/BigFraction.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Temporal/ISORecords.h>
|
||||
|
||||
|
@ -20,5 +21,6 @@ ISODateTime balance_iso_date_time(double year, double month, double day, double
|
|||
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);
|
||||
ThrowCompletionOr<Crypto::BigFraction> difference_plain_date_time_with_total(VM&, ISODateTime const&, ISODateTime const&, StringView calendar, Unit);
|
||||
|
||||
}
|
||||
|
|
|
@ -16,6 +16,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.total({ unit: "months", relativeTo });
|
||||
expect(result).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue