mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 23:38:12 +00:00
LibJS: Update Temporal RoundDuration AO to some spec changes
This commit effectively just does a bulk update of this function to the spec. Since there have been so many spec changes, no specific change was made in mind, and many FIXMEs have been left for where we are still out of date. These changes also appear to include a normative change to the temporal spec which was previously resulting in timeouts for some tests, and is now resulting in a timeout. Furthermore, this also resolves some crashes by protecting against division by zero, instead throwing a RangeError. This can only happen when a custom calender is provided which returns funky values. See: https://github.com/tc39/proposal-temporal/commit/ed85e9 Diff Tests: +8 ✅ -4 💀 -4 💥️
This commit is contained in:
parent
83b84cf0bd
commit
a7316d3641
4 changed files with 283 additions and 273 deletions
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
|
||||
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -1188,321 +1189,371 @@ ThrowCompletionOr<ZonedDateTime*> move_relative_zoned_date_time(VM& vm, ZonedDat
|
|||
return MUST(create_temporal_zoned_date_time(vm, *intermediate_ns, zoned_date_time.time_zone(), zoned_date_time.calendar()));
|
||||
}
|
||||
|
||||
// 7.5.25 RoundDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, increment, unit, roundingMode [ , relativeTo ] ), https://tc39.es/proposal-temporal/#sec-temporal-roundduration
|
||||
ThrowCompletionOr<RoundedDuration> round_duration(VM& vm, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, u32 increment, StringView unit, StringView rounding_mode, Object* relative_to_object)
|
||||
// 7.5.27 RoundDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, increment, unit, roundingMode [ , plainRelativeTo [ , calendarRec [ , zonedRelativeTo [ , timeZoneRec [ , precalculatedPlainDateTime ] ] ] ] ] ), https://tc39.es/proposal-temporal/#sec-temporal-roundduration
|
||||
// FIXME: Support calendarRec, zonedRelativeTo, timeZoneRec, precalculatedPlainDateTime
|
||||
ThrowCompletionOr<RoundedDuration> round_duration(VM& vm, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, u32 increment, StringView unit, StringView rounding_mode, Object* plain_relative_to_object)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
Object* calendar = nullptr;
|
||||
double fractional_seconds = 0;
|
||||
Object* calendar = nullptr; // FIXME: Should come from calendarRec
|
||||
|
||||
// 1. If relativeTo is not present, set relativeTo to undefined.
|
||||
// NOTE: `relative_to_object` and `relative_to` in the various code paths below are all the same as far as the
|
||||
// spec is concerned, but the latter is more strictly typed for convenience.
|
||||
PlainDate* relative_to = nullptr;
|
||||
double fractional_seconds = 0;
|
||||
double fractional_days = 0;
|
||||
|
||||
// FIXME: 1. Assert: If either of plainRelativeTo or zonedRelativeTo are present and not undefined, calendarRec is not undefined.
|
||||
// FIXME: 2. Assert: If zonedRelativeTo is present and not undefined, timeZoneRec is not undefined.
|
||||
|
||||
// 3. If plainRelativeTo is not present, set plainRelativeTo to undefined.
|
||||
// NOTE: `plain_relative_to_object` and `plain_relative_to` in the various code paths below are all the same as far as the
|
||||
// spec is concerned, but the latter is more strictly typed for convenience.
|
||||
PlainDate* plain_relative_to = nullptr;
|
||||
|
||||
// FIXME: 4. If zonedRelativeTo is not present, set zonedRelativeTo to undefined.
|
||||
ZonedDateTime* zoned_relative_to = nullptr;
|
||||
|
||||
// FIXME: 5. If precalculatedPlainDateTime is not present, set precalculatedPlainDateTime to undefined.
|
||||
|
||||
// FIXME: assuming "smallestUnit" as the option name here leads to confusing error messages in some cases:
|
||||
// > new Temporal.Duration().total({ unit: "month" })
|
||||
// Uncaught exception: [RangeError] month is not a valid value for option smallestUnit
|
||||
// 2. If unit is "year", "month", or "week", and relativeTo is undefined, then
|
||||
if (unit.is_one_of("year"sv, "month"sv, "week"sv) && !relative_to_object) {
|
||||
// a. Throw a RangeError exception.
|
||||
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, unit, "smallestUnit"sv);
|
||||
// 6. If unit is "year", "month", or "week", then
|
||||
if (unit.is_one_of("year"sv, "month"sv, "week"sv)) {
|
||||
// a. If plainRelativeTo is undefined, throw a RangeError exception.
|
||||
if (!plain_relative_to_object)
|
||||
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, unit, "smallestUnit"sv);
|
||||
|
||||
// FIXME: b. Assert: CalendarMethodsRecordHasLookedUp(calendarRec, dateAdd) is true.
|
||||
// FIXME: c. Assert: CalendarMethodsRecordHasLookedUp(calendarRec, dateUntil) is true.
|
||||
}
|
||||
|
||||
// 3. Let zonedRelativeTo be undefined.
|
||||
ZonedDateTime* zoned_relative_to = nullptr;
|
||||
|
||||
// 4. If relativeTo is not undefined, then
|
||||
if (relative_to_object) {
|
||||
// a. If relativeTo has an [[InitializedTemporalZonedDateTime]] internal slot, then
|
||||
if (is<ZonedDateTime>(relative_to_object)) {
|
||||
auto* relative_to_zoned_date_time = static_cast<ZonedDateTime*>(relative_to_object);
|
||||
|
||||
// i. Set zonedRelativeTo to relativeTo.
|
||||
// FIXME: AD-HOC, should be coming from arguments given to this function.
|
||||
if (plain_relative_to_object) {
|
||||
if (is<ZonedDateTime>(plain_relative_to_object)) {
|
||||
auto* relative_to_zoned_date_time = static_cast<ZonedDateTime*>(plain_relative_to_object);
|
||||
zoned_relative_to = relative_to_zoned_date_time;
|
||||
|
||||
// ii. Set relativeTo to ? ToTemporalDate(relativeTo).
|
||||
relative_to = TRY(to_temporal_date(vm, relative_to_object));
|
||||
plain_relative_to = TRY(to_temporal_date(vm, plain_relative_to_object));
|
||||
} else {
|
||||
VERIFY(is<PlainDate>(plain_relative_to_object));
|
||||
plain_relative_to = verify_cast<PlainDate>(plain_relative_to_object);
|
||||
}
|
||||
// b. Else,
|
||||
else {
|
||||
// i. Assert: relativeTo has an [[InitializedTemporalDate]] internal slot.
|
||||
VERIFY(is<PlainDate>(relative_to_object));
|
||||
|
||||
relative_to = static_cast<PlainDate*>(relative_to_object);
|
||||
}
|
||||
|
||||
// c. Let calendar be relativeTo.[[Calendar]].
|
||||
calendar = &relative_to->calendar();
|
||||
calendar = &plain_relative_to->calendar();
|
||||
}
|
||||
// 5. Else,
|
||||
// a. NOTE: calendar will not be used below.
|
||||
|
||||
// 6. If unit is one of "year", "month", "week", or "day", then
|
||||
// 7. If unit is one of "year", "month", "week", or "day", then
|
||||
if (unit.is_one_of("year"sv, "month"sv, "week"sv, "day"sv)) {
|
||||
// a. Let nanoseconds be ! TotalDurationNanoseconds(0, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 0).
|
||||
|
||||
// a. Let nanoseconds be TotalDurationNanoseconds(hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
|
||||
// FIXME: We shouldn't be passing through 0 days and 0 offset digit
|
||||
auto nanoseconds_bigint = total_duration_nanoseconds(0, hours, minutes, seconds, milliseconds, microseconds, Crypto::SignedBigInteger { nanoseconds }, 0);
|
||||
|
||||
// b. Let intermediate be undefined.
|
||||
ZonedDateTime* intermediate = nullptr;
|
||||
|
||||
// c. If zonedRelativeTo is not undefined, then
|
||||
// b. If zonedRelativeTo is not undefined, then
|
||||
if (zoned_relative_to) {
|
||||
// i. Let intermediate be ? MoveRelativeZonedDateTime(zonedRelativeTo, years, months, weeks, days).
|
||||
intermediate = TRY(move_relative_zoned_date_time(vm, *zoned_relative_to, years, months, weeks, days));
|
||||
// i. Let intermediate be ? MoveRelativeZonedDateTime(zonedRelativeTo, calendarRec, timeZoneRec, years, months, weeks, days, precalculatedPlainDateTime).
|
||||
// FIXME: Pass through calendarRecord, timeZoneRec and precalculatedPlainDateTime
|
||||
auto* intermediate = TRY(move_relative_zoned_date_time(vm, *zoned_relative_to, years, months, weeks, days));
|
||||
|
||||
// ii. Let result be ? NanosecondsToDays(nanoseconds, intermediate, timeZoneRec).
|
||||
// FIXME: Pass through timeZoneRec
|
||||
auto result = TRY(nanoseconds_to_days(vm, nanoseconds_bigint, intermediate));
|
||||
|
||||
// iii. Let fractionalDays be days + result.[[Days]] + result.[[Nanoseconds]] / result.[[DayLength]].
|
||||
auto nanoseconds_division_result = result.nanoseconds.divided_by(Crypto::UnsignedBigInteger { result.day_length });
|
||||
fractional_days = days + result.days + nanoseconds_division_result.quotient.to_double() + nanoseconds_division_result.remainder.to_double() / result.day_length;
|
||||
}
|
||||
// c. Else,
|
||||
else {
|
||||
// i. Let fractionalDays be days + nanoseconds / nsPerDay.
|
||||
auto nanoseconds_division_result = nanoseconds_bigint.divided_by(ns_per_day_bigint);
|
||||
fractional_days = days + nanoseconds_division_result.quotient.to_double() + (nanoseconds_division_result.remainder.to_double() / ns_per_day);
|
||||
}
|
||||
|
||||
// d. Let result be ? NanosecondsToDays(nanoseconds, intermediate).
|
||||
auto result = TRY(nanoseconds_to_days(vm, nanoseconds_bigint, intermediate));
|
||||
|
||||
// e. Set days to days + result.[[Days]] + result.[[Nanoseconds]] / result.[[DayLength]].
|
||||
auto nanoseconds_division_result = result.nanoseconds.divided_by(Crypto::UnsignedBigInteger { result.day_length });
|
||||
days += result.days + nanoseconds_division_result.quotient.to_double() + nanoseconds_division_result.remainder.to_double() / result.day_length;
|
||||
|
||||
// f. Set hours, minutes, seconds, milliseconds, microseconds, and nanoseconds to 0.
|
||||
// d. Set days, hours, minutes, seconds, milliseconds, microseconds, and nanoseconds to 0.
|
||||
days = 0;
|
||||
hours = 0;
|
||||
minutes = 0;
|
||||
seconds = 0;
|
||||
milliseconds = 0;
|
||||
microseconds = 0;
|
||||
nanoseconds = 0;
|
||||
|
||||
// e. Assert: fractionalSeconds is not used below.
|
||||
}
|
||||
// 7. Else,
|
||||
// 8. Else,
|
||||
else {
|
||||
// a. Let fractionalSeconds be nanoseconds × 10^-9 + microseconds × 10^-6 + milliseconds × 10^-3 + seconds.
|
||||
fractional_seconds = nanoseconds * 0.000000001 + microseconds * 0.000001 + milliseconds * 0.001 + seconds;
|
||||
|
||||
// b. Assert: fractionalDays is not used below.
|
||||
}
|
||||
|
||||
// 8. Let remainder be undefined.
|
||||
double remainder = 0;
|
||||
// 9. Let total be unset.
|
||||
double total = 0;
|
||||
|
||||
// 9. If unit is "year", then
|
||||
// 10. If unit is "year", then
|
||||
if (unit == "year"sv) {
|
||||
VERIFY(relative_to);
|
||||
VERIFY(plain_relative_to);
|
||||
|
||||
// a. Let yearsDuration be ! CreateTemporalDuration(years, 0, 0, 0, 0, 0, 0, 0, 0, 0).
|
||||
auto* years_duration = MUST(create_temporal_duration(vm, years, 0, 0, 0, 0, 0, 0, 0, 0, 0));
|
||||
|
||||
// b. Let dateAdd be ? GetMethod(calendar, "dateAdd").
|
||||
// FIXME: b. Let yearsLater be ? AddDate(calendarRec, plainRelativeTo, yearsDuration).
|
||||
auto date_add = TRY(Value(calendar).get_method(vm, vm.names.dateAdd));
|
||||
auto* years_later = TRY(calendar_date_add(vm, *calendar, plain_relative_to, *years_duration, nullptr, date_add));
|
||||
|
||||
// c. Let yearsLater be ? CalendarDateAdd(calendar, relativeTo, yearsDuration, undefined, dateAdd).
|
||||
auto* years_later = TRY(calendar_date_add(vm, *calendar, relative_to, *years_duration, nullptr, date_add));
|
||||
|
||||
// d. Let yearsMonthsWeeks be ! CreateTemporalDuration(years, months, weeks, 0, 0, 0, 0, 0, 0, 0).
|
||||
// c. Let yearsMonthsWeeks be ! CreateTemporalDuration(years, months, weeks, 0, 0, 0, 0, 0, 0, 0).
|
||||
auto* years_months_weeks = MUST(create_temporal_duration(vm, years, months, weeks, 0, 0, 0, 0, 0, 0, 0));
|
||||
|
||||
// e. Let yearsMonthsWeeksLater be ? CalendarDateAdd(calendar, relativeTo, yearsMonthsWeeks, undefined, dateAdd).
|
||||
auto* years_months_weeks_later = TRY(calendar_date_add(vm, *calendar, relative_to, *years_months_weeks, nullptr, date_add));
|
||||
// FIXME: d. Let yearsMonthsWeeksLater be ? AddDate(calendarRec, plainRelativeTo, yearsMonthsWeeks).
|
||||
auto* years_months_weeks_later = TRY(calendar_date_add(vm, *calendar, plain_relative_to, *years_months_weeks, nullptr, date_add));
|
||||
|
||||
// f. Let monthsWeeksInDays be DaysUntil(yearsLater, yearsMonthsWeeksLater).
|
||||
// e. Let monthsWeeksInDays be DaysUntil(yearsLater, yearsMonthsWeeksLater).
|
||||
auto months_weeks_in_days = days_until(*years_later, *years_months_weeks_later);
|
||||
|
||||
// g. Set relativeTo to yearsLater.
|
||||
relative_to = years_later;
|
||||
// f. Set plainRelativeTo to yearsLater.
|
||||
plain_relative_to = years_later;
|
||||
|
||||
// h. Let days be days + monthsWeeksInDays.
|
||||
days += months_weeks_in_days;
|
||||
// g. Set fractionalDays to fractionalDays + monthsWeeksInDays.
|
||||
fractional_days = fractional_days + months_weeks_in_days;
|
||||
|
||||
// i. Let wholeDaysDuration be ? CreateTemporalDuration(0, 0, 0, truncate(days), 0, 0, 0, 0, 0, 0).
|
||||
auto* whole_days_duration = TRY(create_temporal_duration(vm, 0, 0, 0, trunc(days), 0, 0, 0, 0, 0, 0));
|
||||
// h. Let isoResult be ! AddISODate(plainRelativeTo.[[ISOYear]]. plainRelativeTo.[[ISOMonth]], plainRelativeTo.[[ISODay]], 0, 0, 0, truncate(fractionalDays), "constrain").
|
||||
auto iso_result = MUST(add_iso_date(vm, plain_relative_to->iso_year(), plain_relative_to->iso_month(), plain_relative_to->iso_day(), 0, 0, 0, trunc(fractional_days), "constrain"sv));
|
||||
|
||||
// j. Let wholeDaysLater be ? CalendarDateAdd(calendar, relativeTo, wholeDaysDuration, undefined, dateAdd).
|
||||
auto* whole_days_later = TRY(calendar_date_add(vm, *calendar, relative_to, *whole_days_duration, nullptr, date_add));
|
||||
// i. Let wholeDaysLater be ? CreateTemporalDate(isoResult.[[Year]], isoResult.[[Month]], isoResult.[[Day]], calendarRec.[[Receiver]]).
|
||||
// FIXME: Pass through receiver from calendarRec
|
||||
auto* whole_days_later = TRY(create_temporal_date(vm, iso_result.year, iso_result.month, iso_result.day, *calendar));
|
||||
|
||||
// k. Let untilOptions be OrdinaryObjectCreate(null).
|
||||
// j. Let untilOptions be OrdinaryObjectCreate(null).
|
||||
auto until_options = Object::create(realm, nullptr);
|
||||
|
||||
// l. Perform ! CreateDataPropertyOrThrow(untilOptions, "largestUnit", "year").
|
||||
// k. Perform ! CreateDataPropertyOrThrow(untilOptions, "largestUnit", "year").
|
||||
MUST(until_options->create_data_property_or_throw(vm.names.largestUnit, PrimitiveString::create(vm, "year"_string)));
|
||||
|
||||
// m. Let timePassed be ? CalendarDateUntil(calendar, relativeTo, wholeDaysLater, untilOptions).
|
||||
auto* time_passed = TRY(calendar_date_until(vm, *calendar, relative_to, whole_days_later, *until_options));
|
||||
// l. Let timePassed be ? DifferenceDate(calendarRec, plainRelativeTo, wholeDaysLater, untilOptions).
|
||||
// FIXME: Pass through calendarRec
|
||||
auto time_passed = TRY(difference_date(vm, *calendar, *plain_relative_to, *whole_days_later, *until_options));
|
||||
|
||||
// n. Let yearsPassed be timePassed.[[Years]].
|
||||
// m. Let yearsPassed be timePassed.[[Years]].
|
||||
auto years_passed = time_passed->years();
|
||||
|
||||
// o. Set years to years + yearsPassed.
|
||||
// n. Set years to years + yearsPassed.
|
||||
years += years_passed;
|
||||
|
||||
// p. Let oldRelativeTo be relativeTo.
|
||||
auto* old_relative_to = relative_to;
|
||||
|
||||
// q. Let yearsDuration be ! CreateTemporalDuration(yearsPassed, 0, 0, 0, 0, 0, 0, 0, 0, 0).
|
||||
// o. Let yearsDuration be ! CreateTemporalDuration(yearsPassed, 0, 0, 0, 0, 0, 0, 0, 0, 0).
|
||||
years_duration = MUST(create_temporal_duration(vm, years_passed, 0, 0, 0, 0, 0, 0, 0, 0, 0));
|
||||
|
||||
// r. Set relativeTo to ? CalendarDateAdd(calendar, relativeTo, yearsDuration, undefined, dateAdd).
|
||||
relative_to = TRY(calendar_date_add(vm, *calendar, relative_to, *years_duration, nullptr, date_add));
|
||||
// p. Let moveResult be ? MoveRelativeDate(calendarRec, plainRelativeTo, yearsDuration).
|
||||
// FIXME: Pass through calendarRec instead of date_add
|
||||
auto move_result = TRY(move_relative_date(vm, *calendar, *plain_relative_to, *years_duration, date_add));
|
||||
|
||||
// s. Let daysPassed be DaysUntil(oldRelativeTo, relativeTo).
|
||||
auto days_passed = days_until(*old_relative_to, *relative_to);
|
||||
// q. Set plainRelativeTo to moveResult.[[RelativeTo]].
|
||||
auto plain_relative_to = move_result.relative_to;
|
||||
|
||||
// t. Set days to days - daysPassed.
|
||||
days -= days_passed;
|
||||
// r. Let daysPassed be moveResult.[[Days]].
|
||||
auto days_passed = move_result.days;
|
||||
|
||||
// u. If days < 0, let sign be -1; else, let sign be 1.
|
||||
auto sign = days < 0 ? -1 : 1;
|
||||
// s. Set fractionalDays to fractionalDays - daysPassed.
|
||||
fractional_days -= days_passed;
|
||||
|
||||
// v. Let oneYear be ! CreateTemporalDuration(sign, 0, 0, 0, 0, 0, 0, 0, 0, 0).
|
||||
// t. If fractionalDays < 0, let sign be -1; else, let sign be 1.
|
||||
auto sign = fractional_days < 0 ? -1 : 1;
|
||||
|
||||
// u. Let oneYear be ! CreateTemporalDuration(sign, 0, 0, 0, 0, 0, 0, 0, 0, 0).
|
||||
auto* one_year = MUST(create_temporal_duration(vm, sign, 0, 0, 0, 0, 0, 0, 0, 0, 0));
|
||||
|
||||
// w. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneYear, dateAdd).
|
||||
auto move_result = TRY(move_relative_date(vm, *calendar, *relative_to, *one_year, date_add));
|
||||
// v. Set moveResult to ? MoveRelativeDate(calendarRec, plainRelativeTo, oneYear).
|
||||
// FIXME:: pass through calendarRec
|
||||
move_result = TRY(move_relative_date(vm, *calendar, *plain_relative_to, *one_year, date_add));
|
||||
|
||||
// x. Let oneYearDays be moveResult.[[Days]].
|
||||
// w. Let oneYearDays be moveResult.[[Days]].
|
||||
auto one_year_days = move_result.days;
|
||||
|
||||
// y. Let fractionalYears be years + days / abs(oneYearDays).
|
||||
auto fractional_years = years + days / fabs(one_year_days);
|
||||
// x. If oneYearDays = 0, throw a RangeError exception.
|
||||
if (one_year_days == 0)
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarFunctionResult, "dateAdd", "result implying a year is zero days long");
|
||||
|
||||
// y. Let fractionalYears be years + fractionalDays / abs(oneYearDays).
|
||||
auto fractional_years = years + fractional_days / fabs(one_year_days);
|
||||
|
||||
// z. Set years to RoundNumberToIncrement(fractionalYears, increment, roundingMode).
|
||||
years = round_number_to_increment(fractional_years, increment, rounding_mode);
|
||||
|
||||
// aa. Set remainder to fractionalYears - years.
|
||||
remainder = fractional_years - years;
|
||||
// aa. Set total to fractionalYears.
|
||||
total = fractional_years;
|
||||
|
||||
// ab. Set months, weeks, and days to 0.
|
||||
// ab. Set months and weeks to 0.
|
||||
months = 0;
|
||||
weeks = 0;
|
||||
days = 0;
|
||||
}
|
||||
// 10. Else if unit is "month", then
|
||||
else if (unit == "month"sv) {
|
||||
VERIFY(relative_to);
|
||||
VERIFY(plain_relative_to);
|
||||
|
||||
// a. Let yearsMonths be ! CreateTemporalDuration(years, months, 0, 0, 0, 0, 0, 0, 0, 0).
|
||||
auto* years_months = MUST(create_temporal_duration(vm, years, months, 0, 0, 0, 0, 0, 0, 0, 0));
|
||||
|
||||
// b. Let dateAdd be ? GetMethod(calendar, "dateAdd").
|
||||
// FIXME: b. Let yearsMonthsLater be ? AddDate(calendarRec, plainRelativeTo, yearsMonths).
|
||||
auto date_add = TRY(Value(calendar).get_method(vm, vm.names.dateAdd));
|
||||
auto* years_months_later = TRY(calendar_date_add(vm, *calendar, plain_relative_to, *years_months, nullptr, date_add));
|
||||
|
||||
// c. Let yearsMonthsLater be ? CalendarDateAdd(calendar, relativeTo, yearsMonths, undefined, dateAdd).
|
||||
auto* years_months_later = TRY(calendar_date_add(vm, *calendar, relative_to, *years_months, nullptr, date_add));
|
||||
|
||||
// d. Let yearsMonthsWeeks be ! CreateTemporalDuration(years, months, weeks, 0, 0, 0, 0, 0, 0, 0).
|
||||
// c. Let yearsMonthsWeeks be ! CreateTemporalDuration(years, months, weeks, 0, 0, 0, 0, 0, 0, 0).
|
||||
auto* years_months_weeks = MUST(create_temporal_duration(vm, years, months, weeks, 0, 0, 0, 0, 0, 0, 0));
|
||||
|
||||
// e. Let yearsMonthsWeeksLater be ? CalendarDateAdd(calendar, relativeTo, yearsMonthsWeeks, undefined, dateAdd).
|
||||
auto* years_months_weeks_later = TRY(calendar_date_add(vm, *calendar, relative_to, *years_months_weeks, nullptr, date_add));
|
||||
// FIXME: d. Let yearsMonthsWeeksLater be ? AddDate(calendarRec, plainRelativeTo, yearsMonthsWeeks).
|
||||
auto* years_months_weeks_later = TRY(calendar_date_add(vm, *calendar, plain_relative_to, *years_months_weeks, nullptr, date_add));
|
||||
|
||||
// f. Let weeksInDays be DaysUntil(yearsMonthsLater, yearsMonthsWeeksLater).
|
||||
// e. Let weeksInDays be DaysUntil(yearsMonthsLater, yearsMonthsWeeksLater).
|
||||
auto weeks_in_days = days_until(*years_months_later, *years_months_weeks_later);
|
||||
|
||||
// g. Set relativeTo to yearsMonthsLater.
|
||||
relative_to = years_months_later;
|
||||
// f. Set plainRelativeTo to yearsMonthsLater.
|
||||
plain_relative_to = years_months_later;
|
||||
|
||||
// h. Let days be days + weeksInDays.
|
||||
days += weeks_in_days;
|
||||
// g. Set fractionalDays to fractionalDays + weeksInDays.
|
||||
fractional_days += weeks_in_days;
|
||||
|
||||
// i. If days < 0, let sign be -1; else, let sign be 1.
|
||||
auto sign = days < 0 ? -1 : 1;
|
||||
// h. Let isoResult be ! AddISODate(plainRelativeTo.[[ISOYear]], plainRelativeTo.[[ISOMonth]], plainRelativeTo.[[ISODay]], 0, 0, 0, truncate(fractionalDays), "constrain").
|
||||
auto iso_result = MUST(add_iso_date(vm, plain_relative_to->iso_year(), plain_relative_to->iso_month(), plain_relative_to->iso_day(), 0, 0, 0, trunc(fractional_days), "constrain"sv));
|
||||
|
||||
// j. Let oneMonth be ! CreateTemporalDuration(0, sign, 0, 0, 0, 0, 0, 0, 0, 0).
|
||||
// i. Let wholeDaysLater be ? CreateTemporalDate(isoResult.[[Year]], isoResult.[[Month]], isoResult.[[Day]], calendarRec.[[Receiver]]).
|
||||
// FIXME: Pass through calendarRec
|
||||
auto* whole_days_later = TRY(create_temporal_date(vm, iso_result.year, iso_result.month, iso_result.day, *calendar)); // FIXME: receiver
|
||||
|
||||
// j. Let untilOptions be OrdinaryObjectCreate(null).
|
||||
auto until_options = Object::create(realm, nullptr);
|
||||
|
||||
// k. Perform ! CreateDataPropertyOrThrow(untilOptions, "largestUnit", "month").
|
||||
MUST(until_options->create_data_property_or_throw(vm.names.largestUnit, PrimitiveString::create(vm, "month"_string)));
|
||||
|
||||
// l. Let timePassed be ? DifferenceDate(calendarRec, plainRelativeTo, wholeDaysLater, untilOptions).
|
||||
// FIXME: Pass through receiver from calendarRec
|
||||
auto time_passed = TRY(difference_date(vm, *calendar, *plain_relative_to, *whole_days_later, *until_options));
|
||||
|
||||
// m. Let monthsPassed be timePassed.[[Months]].
|
||||
auto months_passed = time_passed->months();
|
||||
|
||||
// n. Set months to months + monthsPassed.
|
||||
months += months_passed;
|
||||
|
||||
// o. Let monthsPassedDuration be ! CreateTemporalDuration(0, monthsPassed, 0, 0, 0, 0, 0, 0, 0, 0).
|
||||
auto* months_passed_duration = MUST(create_temporal_duration(vm, 0, months_passed, 0, 0, 0, 0, 0, 0, 0, 0));
|
||||
|
||||
// p. Let moveResult be ? MoveRelativeDate(calendarRec, plainRelativeTo, monthsPassedDuration).
|
||||
// FIXME: Pass through calendarRec
|
||||
auto move_result = TRY(move_relative_date(vm, *calendar, *plain_relative_to, *months_passed_duration, date_add));
|
||||
|
||||
// q. Set plainRelativeTo to moveResult.[[RelativeTo]].
|
||||
plain_relative_to = move_result.relative_to;
|
||||
|
||||
// r. Let daysPassed be moveResult.[[Days]].
|
||||
auto days_passed = move_result.days;
|
||||
|
||||
// s. Set fractionalDays to fractionalDays - daysPassed.
|
||||
fractional_days -= days_passed;
|
||||
|
||||
// t. If fractionalDays < 0, let sign be -1; else, let sign be 1.
|
||||
auto sign = fractional_days < 0 ? -1 : 1;
|
||||
|
||||
// u. Let oneMonth be ! CreateTemporalDuration(0, sign, 0, 0, 0, 0, 0, 0, 0, 0).
|
||||
auto* one_month = MUST(create_temporal_duration(vm, 0, sign, 0, 0, 0, 0, 0, 0, 0, 0));
|
||||
|
||||
// k. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneMonth, dateAdd).
|
||||
auto move_result = TRY(move_relative_date(vm, *calendar, *relative_to, *one_month, date_add));
|
||||
// v. Let moveResult be ? MoveRelativeDate(calendarRec, plainRelativeTo, oneMonth).
|
||||
// FIXME: spec bug, this should be set.
|
||||
move_result = TRY(move_relative_date(vm, *calendar, *plain_relative_to, *one_month, date_add));
|
||||
|
||||
// l. Set relativeTo to moveResult.[[RelativeTo]].
|
||||
relative_to = move_result.relative_to.cell();
|
||||
|
||||
// m. Let oneMonthDays be moveResult.[[Days]].
|
||||
// w. Let oneMonthDays be moveResult.[[Days]].
|
||||
auto one_month_days = move_result.days;
|
||||
|
||||
// n. Repeat, while abs(days) ≥ abs(oneMonthDays),
|
||||
while (fabs(days) >= fabs(one_month_days)) {
|
||||
// i. Set months to months + sign.
|
||||
months += sign;
|
||||
// x. If oneMonthDays = 0, throw a RangeError exception.
|
||||
if (one_month_days == 0)
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarFunctionResult, "dateAdd", "result implying a month is zero days long");
|
||||
|
||||
// ii. Set days to days - oneMonthDays.
|
||||
days -= one_month_days;
|
||||
// y. Let fractionalMonths be months + fractionalDays / abs(oneMonthDays).
|
||||
auto fractional_months = months + fractional_days / fabs(one_month_days);
|
||||
|
||||
// iii. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, oneMonth, dateAdd).
|
||||
move_result = TRY(move_relative_date(vm, *calendar, *relative_to, *one_month, date_add));
|
||||
|
||||
// iv. Set relativeTo to moveResult.[[RelativeTo]].
|
||||
relative_to = move_result.relative_to.cell();
|
||||
|
||||
// v. Set oneMonthDays to moveResult.[[Days]].
|
||||
one_month_days = move_result.days;
|
||||
}
|
||||
|
||||
// o. Let fractionalMonths be months + days / abs(oneMonthDays).
|
||||
auto fractional_months = months + days / fabs(one_month_days);
|
||||
|
||||
// p. Set months to RoundNumberToIncrement(fractionalMonths, increment, roundingMode).
|
||||
// z. Set months to RoundNumberToIncrement(fractionalMonths, increment, roundingMode).
|
||||
months = round_number_to_increment(fractional_months, increment, rounding_mode);
|
||||
|
||||
// q. Set remainder to fractionalMonths - months.
|
||||
remainder = fractional_months - months;
|
||||
// aa. Set total to fractionalMonths.
|
||||
total = fractional_months;
|
||||
|
||||
// r. Set weeks and days to 0.
|
||||
// ab. Set weeks to 0.
|
||||
weeks = 0;
|
||||
days = 0;
|
||||
}
|
||||
// 11. Else if unit is "week", then
|
||||
else if (unit == "week"sv) {
|
||||
VERIFY(relative_to);
|
||||
VERIFY(plain_relative_to);
|
||||
|
||||
// a. If days < 0, let sign be -1; else, let sign be 1.
|
||||
auto sign = days < 0 ? -1 : 1;
|
||||
// a. Let isoResult be ! AddISODate(plainRelativeTo.[[ISOYear]], plainRelativeTo.[[ISOMonth]], plainRelativeTo.[[ISODay]], 0, 0, 0, truncate(fractionalDays), "constrain").
|
||||
auto iso_result = MUST(add_iso_date(vm, plain_relative_to->iso_year(), plain_relative_to->iso_month(), plain_relative_to->iso_day(), 0, 0, 0, trunc(fractional_days), "constrain"sv));
|
||||
|
||||
// b. Let oneWeek be ! CreateTemporalDuration(0, 0, sign, 0, 0, 0, 0, 0, 0, 0).
|
||||
// b. Let wholeDaysLater be ? CreateTemporalDate(isoResult.[[Year]], isoResult.[[Month]], isoResult.[[Day]], calendarRec.[[Receiver]]).
|
||||
// FIXME: Pass through receiver from calendarRec
|
||||
auto* whole_days_later = TRY(create_temporal_date(vm, iso_result.year, iso_result.month, iso_result.day, *calendar));
|
||||
|
||||
// c. Let untilOptions be OrdinaryObjectCreate(null).
|
||||
auto until_options = Object::create(realm, nullptr);
|
||||
|
||||
// d. Perform ! CreateDataPropertyOrThrow(untilOptions, "largestUnit", "week").
|
||||
MUST(until_options->create_data_property_or_throw(vm.names.largestUnit, PrimitiveString::create(vm, "month"_string)));
|
||||
|
||||
// e. Let timePassed be ? DifferenceDate(calendarRec, plainRelativeTo, wholeDaysLater, untilOptions).
|
||||
// FIXME: Pass through calendarRec
|
||||
auto time_passed = TRY(difference_date(vm, *calendar, *plain_relative_to, *whole_days_later, *until_options));
|
||||
|
||||
// f. Let weeksPassed be timePassed.[[Weeks]].
|
||||
auto weeks_passed = time_passed->weeks();
|
||||
|
||||
// g. Set weeks to weeks + weeksPassed.
|
||||
weeks += weeks_passed;
|
||||
|
||||
// h. Let weeksPassedDuration be ! CreateTemporalDuration(0, 0, weeksPassed, 0, 0, 0, 0, 0, 0, 0).
|
||||
auto* weeks_passed_duration = MUST(create_temporal_duration(vm, 0, 0, weeks_passed, 0, 0, 0, 0, 0, 0, 0));
|
||||
|
||||
// FIXME: i. Let moveResult be ? MoveRelativeDate(calendarRec, plainRelativeTo, weeksPassedDuration).
|
||||
auto date_add = TRY(Value(calendar).get_method(vm, vm.names.dateAdd));
|
||||
auto move_result = TRY(move_relative_date(vm, *calendar, *plain_relative_to, *weeks_passed_duration, date_add));
|
||||
|
||||
// j. Set plainRelativeTo to moveResult.[[RelativeTo]].
|
||||
plain_relative_to = move_result.relative_to;
|
||||
|
||||
// k. Let daysPassed be moveResult.[[Days]].
|
||||
auto days_passed = move_result.days;
|
||||
|
||||
// l. Set fractionalDays to fractionalDays - daysPassed.
|
||||
fractional_days -= days_passed;
|
||||
|
||||
// m. If fractionalDays < 0, let sign be -1; else, let sign be 1.
|
||||
auto sign = fractional_days < 0 ? -1 : 1;
|
||||
|
||||
// n. Let oneWeek be ! CreateTemporalDuration(0, 0, sign, 0, 0, 0, 0, 0, 0, 0).
|
||||
auto* one_week = MUST(create_temporal_duration(vm, 0, 0, sign, 0, 0, 0, 0, 0, 0, 0));
|
||||
|
||||
// c. Let dateAdd be ? GetMethod(calendar, "dateAdd").
|
||||
auto date_add = TRY(Value(calendar).get_method(vm, vm.names.dateAdd));
|
||||
// o. Let moveResult be ? MoveRelativeDate(calendarRec, plainRelativeTo, oneWeek).
|
||||
// FIXME: spec bug, should be set
|
||||
move_result = TRY(move_relative_date(vm, *calendar, *plain_relative_to, *one_week, date_add));
|
||||
|
||||
// d. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneWeek, dateAdd).
|
||||
auto move_result = TRY(move_relative_date(vm, *calendar, *relative_to, *one_week, date_add));
|
||||
|
||||
// e. Set relativeTo to moveResult.[[RelativeTo]].
|
||||
relative_to = move_result.relative_to.cell();
|
||||
|
||||
// f. Let oneWeekDays be moveResult.[[Days]].
|
||||
// p. Let oneWeekDays be moveResult.[[Days]].
|
||||
auto one_week_days = move_result.days;
|
||||
|
||||
// g. Repeat, while abs(days) ≥ abs(oneWeekDays),
|
||||
while (fabs(days) >= fabs(one_week_days)) {
|
||||
// i. Set weeks to weeks + sign.
|
||||
weeks += sign;
|
||||
// q. If oneWeekDays = 0, throw a RangeError exception.
|
||||
if (one_week_days == 0)
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarFunctionResult, "dateAdd", "result implying a month is zero days long");
|
||||
|
||||
// ii. Set days to days - oneWeekDays.
|
||||
days -= one_week_days;
|
||||
// r. Let fractionalWeeks be weeks + fractionalDays / abs(oneWeekDays).
|
||||
auto fractional_weeks = weeks + fractional_days / fabs(one_week_days);
|
||||
|
||||
// iii. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, oneWeek, dateAdd).
|
||||
move_result = TRY(move_relative_date(vm, *calendar, *relative_to, *one_week, date_add));
|
||||
|
||||
// iv. Set relativeTo to moveResult.[[RelativeTo]].
|
||||
relative_to = move_result.relative_to.cell();
|
||||
|
||||
// v. Set oneWeekDays to moveResult.[[Days]].
|
||||
one_week_days = move_result.days;
|
||||
}
|
||||
|
||||
// h. Let fractionalWeeks be weeks + days / abs(oneWeekDays).
|
||||
auto fractional_weeks = weeks + days / fabs(one_week_days);
|
||||
|
||||
// i. Set weeks to RoundNumberToIncrement(fractionalWeeks, increment, roundingMode).
|
||||
// s. Set weeks to RoundNumberToIncrement(fractionalWeeks, increment, roundingMode).
|
||||
weeks = round_number_to_increment(fractional_weeks, increment, rounding_mode);
|
||||
|
||||
// j. Set remainder to fractionalWeeks - weeks.
|
||||
remainder = fractional_weeks - weeks;
|
||||
|
||||
// k. Set days to 0.
|
||||
days = 0;
|
||||
// t. Set total to fractionalWeeks.
|
||||
total = fractional_weeks;
|
||||
}
|
||||
// 12. Else if unit is "day", then
|
||||
else if (unit == "day"sv) {
|
||||
// a. Let fractionalDays be days.
|
||||
auto fractional_days = days;
|
||||
// a. Set days to RoundNumberToIncrement(fractionalDays, increment, roundingMode).
|
||||
days = round_number_to_increment(fractional_days, increment, rounding_mode);
|
||||
|
||||
// b. Set days to RoundNumberToIncrement(days, increment, roundingMode).
|
||||
days = round_number_to_increment(days, increment, rounding_mode);
|
||||
|
||||
// c. Set remainder to fractionalDays - days.
|
||||
remainder = fractional_days - days;
|
||||
// b. Set total to fractionalDays.
|
||||
total = fractional_days;
|
||||
}
|
||||
// 13. Else if unit is "hour", then
|
||||
else if (unit == "hour"sv) {
|
||||
|
@ -1512,8 +1563,8 @@ ThrowCompletionOr<RoundedDuration> round_duration(VM& vm, double years, double m
|
|||
// b. Set hours to RoundNumberToIncrement(fractionalHours, increment, roundingMode).
|
||||
hours = round_number_to_increment(fractional_hours, increment, rounding_mode);
|
||||
|
||||
// c. Set remainder to fractionalHours - hours.
|
||||
remainder = fractional_hours - hours;
|
||||
// c. Set total to fractionalHours.
|
||||
total = fractional_hours;
|
||||
|
||||
// d. Set minutes, seconds, milliseconds, microseconds, and nanoseconds to 0.
|
||||
minutes = 0;
|
||||
|
@ -1530,8 +1581,8 @@ ThrowCompletionOr<RoundedDuration> round_duration(VM& vm, double years, double m
|
|||
// b. Set minutes to RoundNumberToIncrement(fractionalMinutes, increment, roundingMode).
|
||||
minutes = round_number_to_increment(fractional_minutes, increment, rounding_mode);
|
||||
|
||||
// c. Set remainder to fractionalMinutes - minutes.
|
||||
remainder = fractional_minutes - minutes;
|
||||
// c. Set total to fractionalMinutes.
|
||||
total = fractional_minutes;
|
||||
|
||||
// d. Set seconds, milliseconds, microseconds, and nanoseconds to 0.
|
||||
seconds = 0;
|
||||
|
@ -1544,8 +1595,8 @@ ThrowCompletionOr<RoundedDuration> round_duration(VM& vm, double years, double m
|
|||
// a. Set seconds to RoundNumberToIncrement(fractionalSeconds, increment, roundingMode).
|
||||
seconds = round_number_to_increment(fractional_seconds, increment, rounding_mode);
|
||||
|
||||
// b. Set remainder to fractionalSeconds - seconds.
|
||||
remainder = fractional_seconds - seconds;
|
||||
// b. Set total to fractionalSeconds.
|
||||
total = fractional_seconds;
|
||||
|
||||
// c. Set milliseconds, microseconds, and nanoseconds to 0.
|
||||
milliseconds = 0;
|
||||
|
@ -1560,8 +1611,8 @@ ThrowCompletionOr<RoundedDuration> round_duration(VM& vm, double years, double m
|
|||
// b. Set milliseconds to RoundNumberToIncrement(fractionalMilliseconds, increment, roundingMode).
|
||||
milliseconds = round_number_to_increment(fractional_milliseconds, increment, rounding_mode);
|
||||
|
||||
// c. Set remainder to fractionalMilliseconds - milliseconds.
|
||||
remainder = fractional_milliseconds - milliseconds;
|
||||
// c. Set total to fractionalMilliseconds.
|
||||
total = fractional_milliseconds;
|
||||
|
||||
// d. Set microseconds and nanoseconds to 0.
|
||||
microseconds = 0;
|
||||
|
@ -1575,8 +1626,8 @@ ThrowCompletionOr<RoundedDuration> round_duration(VM& vm, double years, double m
|
|||
// b. Set microseconds to RoundNumberToIncrement(fractionalMicroseconds, increment, roundingMode).
|
||||
microseconds = round_number_to_increment(fractional_microseconds, increment, rounding_mode);
|
||||
|
||||
// c. Set remainder to fractionalMicroseconds - microseconds.
|
||||
remainder = fractional_microseconds - microseconds;
|
||||
// c. Set total to fractionalMicroseconds.
|
||||
total = fractional_microseconds;
|
||||
|
||||
// d. Set nanoseconds to 0.
|
||||
nanoseconds = 0;
|
||||
|
@ -1586,21 +1637,18 @@ ThrowCompletionOr<RoundedDuration> round_duration(VM& vm, double years, double m
|
|||
// a. Assert: unit is "nanosecond".
|
||||
VERIFY(unit == "nanosecond"sv);
|
||||
|
||||
// b. Set remainder to nanoseconds.
|
||||
remainder = nanoseconds;
|
||||
// b. Set total to nanoseconds.
|
||||
total = nanoseconds;
|
||||
|
||||
// c. Set nanoseconds to RoundNumberToIncrement(nanoseconds, increment, roundingMode).
|
||||
nanoseconds = round_number_to_increment(nanoseconds, increment, rounding_mode);
|
||||
|
||||
// d. Set remainder to remainder - nanoseconds.
|
||||
remainder -= nanoseconds;
|
||||
}
|
||||
|
||||
// 19. Let duration be ? CreateDurationRecord(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
|
||||
// 20. Let duration be ? CreateDurationRecord(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
|
||||
auto duration = TRY(create_duration_record(vm, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds));
|
||||
|
||||
// 20. Return the Record { [[DurationRecord]]: duration, [[Remainder]]: remainder }.
|
||||
return RoundedDuration { .duration_record = duration, .remainder = remainder };
|
||||
// 21. Return the Record { [[DurationRecord]]: duration, [[Total]]: total }.
|
||||
return RoundedDuration { .duration_record = duration, .total = total };
|
||||
}
|
||||
|
||||
// 7.5.26 AdjustRoundedDurationDays ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, increment, unit, roundingMode, relativeTo ), https://tc39.es/proposal-temporal/#sec-temporal-adjustroundeddurationdays
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue