mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-10 10:09:14 +00:00
LibJS: Begin implementing the relativeTo option of Duration.round
This commit is contained in:
parent
0befd52725
commit
70ad66d3c0
Notes:
github-actions[bot]
2024-11-23 13:47:01 +00:00
Author: https://github.com/trflynn89
Commit: 70ad66d3c0
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2513
Reviewed-by: https://github.com/shannonbooth ✅
6 changed files with 219 additions and 9 deletions
|
@ -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));
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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", () => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue