LibJS: Implement Temporal.PlainYearMonth.prototype.add/subtract
This commit is contained in:
parent
cb5d1b5086
commit
35f22dcf79
Notes:
github-actions[bot]
2024-11-22 18:56:24 +00:00
Author: https://github.com/trflynn89 Commit: https://github.com/LadybirdBrowser/ladybird/commit/35f22dcf79d Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2496
8 changed files with 154 additions and 0 deletions
|
@ -108,6 +108,19 @@ InternalDuration to_internal_duration_record_with_24_hour_days(VM& vm, Duration
|
|||
return MUST(combine_date_and_time_duration(vm, date_duration, move(time_duration)));
|
||||
}
|
||||
|
||||
// 7.5.7 ToDateDurationRecordWithoutTime ( duration ), https://tc39.es/proposal-temporal/#sec-temporal-todatedurationrecordwithouttime
|
||||
ThrowCompletionOr<DateDuration> to_date_duration_record_without_time(VM& vm, Duration const& duration)
|
||||
{
|
||||
// 1. Let internalDuration be ToInternalDurationRecordWith24HourDays(duration).
|
||||
auto internal_duration = to_internal_duration_record_with_24_hour_days(vm, duration);
|
||||
|
||||
// 2. Let days be truncate(internalDuration.[[Time]] / nsPerDay).
|
||||
auto days = internal_duration.time.divided_by(NANOSECONDS_PER_DAY).quotient;
|
||||
|
||||
// 3. Return ? CreateDateDurationRecord(internalDuration.[[Date]].[[Years]], internalDuration.[[Date]].[[Months]], internalDuration.[[Date]].[[Weeks]], days).
|
||||
return TRY(create_date_duration_record(vm, duration.years(), duration.months(), duration.weeks(), days.to_double()));
|
||||
}
|
||||
|
||||
// 7.5.8 TemporalDurationFromInternal ( internalDuration, largestUnit ), https://tc39.es/proposal-temporal/#sec-temporal-temporaldurationfrominternal
|
||||
ThrowCompletionOr<GC::Ref<Duration>> temporal_duration_from_internal(VM& vm, InternalDuration const& internal_duration, Unit largest_unit)
|
||||
{
|
||||
|
|
|
@ -116,6 +116,7 @@ struct CalendarNudgeResult {
|
|||
DateDuration zero_date_duration(VM&);
|
||||
InternalDuration to_internal_duration_record(VM&, Duration const&);
|
||||
InternalDuration to_internal_duration_record_with_24_hour_days(VM&, Duration const&);
|
||||
ThrowCompletionOr<DateDuration> to_date_duration_record_without_time(VM&, Duration const&);
|
||||
ThrowCompletionOr<GC::Ref<Duration>> temporal_duration_from_internal(VM&, InternalDuration const&, Unit largest_unit);
|
||||
ThrowCompletionOr<DateDuration> create_date_duration_record(VM&, double years, double months, double weeks, double days);
|
||||
ThrowCompletionOr<DateDuration> adjust_date_duration_record(VM&, DateDuration const&, double days, Optional<double> weeks = {}, Optional<double> months = {});
|
||||
|
|
|
@ -270,4 +270,73 @@ ThrowCompletionOr<GC::Ref<Duration>> difference_temporal_plain_year_month(VM& vm
|
|||
return result;
|
||||
}
|
||||
|
||||
// 9.5.8 AddDurationToYearMonth ( operation, yearMonth, temporalDurationLike, options )
|
||||
ThrowCompletionOr<GC::Ref<PlainYearMonth>> add_duration_to_year_month(VM& vm, ArithmeticOperation operation, PlainYearMonth const& year_month, Value temporal_duration_like, Value options)
|
||||
{
|
||||
// 1. Let duration be ? ToTemporalDuration(temporalDurationLike).
|
||||
auto duration = TRY(to_temporal_duration(vm, temporal_duration_like));
|
||||
|
||||
// 2. If operation is SUBTRACT, set duration to CreateNegatedTemporalDuration(duration).
|
||||
if (operation == ArithmeticOperation::Subtract)
|
||||
duration = create_negated_temporal_duration(vm, duration);
|
||||
|
||||
// 3. Let resolvedOptions be ? GetOptionsObject(options).
|
||||
auto resolved_options = TRY(get_options_object(vm, options));
|
||||
|
||||
// 4. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).
|
||||
auto overflow = TRY(get_temporal_overflow_option(vm, resolved_options));
|
||||
|
||||
// 5. Let sign be DurationSign(duration).
|
||||
auto sign = duration_sign(duration);
|
||||
|
||||
// 6. Let calendar be yearMonth.[[Calendar]].
|
||||
auto const& calendar = year_month.calendar();
|
||||
|
||||
// 7. Let fields be ISODateToFields(calendar, yearMonth.[[ISODate]], YEAR-MONTH).
|
||||
auto fields = iso_date_to_fields(calendar, year_month.iso_date(), DateType::YearMonth);
|
||||
|
||||
// 8. Set fields.[[Day]] to 1.
|
||||
fields.day = 1;
|
||||
|
||||
// 9. Let intermediateDate be ? CalendarDateFromFields(calendar, fields, CONSTRAIN).
|
||||
auto intermediate_date = TRY(calendar_date_from_fields(vm, calendar, move(fields), Overflow::Constrain));
|
||||
|
||||
ISODate date;
|
||||
|
||||
// 10. If sign < 0, then
|
||||
if (sign < 0) {
|
||||
// a. Let oneMonthDuration be ! CreateDateDurationRecord(0, 1, 0, 0).
|
||||
auto one_month_duration = MUST(create_date_duration_record(vm, 0, 1, 0, 0));
|
||||
|
||||
// b. Let nextMonth be ? CalendarDateAdd(calendar, intermediateDate, oneMonthDuration, CONSTRAIN).
|
||||
auto next_month = TRY(calendar_date_add(vm, calendar, intermediate_date, one_month_duration, Overflow::Constrain));
|
||||
|
||||
// c. Let date be BalanceISODate(nextMonth.[[Year]], nextMonth.[[Month]], nextMonth.[[Day]] - 1).
|
||||
date = balance_iso_date(next_month.year, next_month.month, next_month.day - 1);
|
||||
|
||||
// d. Assert: ISODateWithinLimits(date) is true.
|
||||
VERIFY(iso_date_within_limits(date));
|
||||
}
|
||||
// 11. Else,
|
||||
else {
|
||||
// a. Let date be intermediateDate.
|
||||
date = intermediate_date;
|
||||
}
|
||||
|
||||
// 12. Let durationToAdd be ? ToDateDurationRecordWithoutTime(duration).
|
||||
auto duration_to_add = TRY(to_date_duration_record_without_time(vm, duration));
|
||||
|
||||
// 13. Let addedDate be ? CalendarDateAdd(calendar, date, durationToAdd, overflow).
|
||||
auto added_date = TRY(calendar_date_add(vm, calendar, date, duration_to_add, overflow));
|
||||
|
||||
// 14. Let addedDateFields be ISODateToFields(calendar, addedDate, YEAR-MONTH).
|
||||
auto added_date_fields = iso_date_to_fields(calendar, added_date, DateType::YearMonth);
|
||||
|
||||
// 15. Let isoDate be ? CalendarYearMonthFromFields(calendar, addedDateFields, overflow).
|
||||
auto iso_date = TRY(calendar_year_month_from_fields(vm, calendar, move(added_date_fields), overflow));
|
||||
|
||||
// 16. Return ! CreateTemporalYearMonth(isoDate, calendar).
|
||||
return MUST(create_temporal_year_month(vm, iso_date, calendar));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -44,5 +44,6 @@ ISOYearMonth balance_iso_year_month(double year, double month);
|
|||
ThrowCompletionOr<GC::Ref<PlainYearMonth>> create_temporal_year_month(VM&, ISODate, String calendar, GC::Ptr<FunctionObject> new_target = {});
|
||||
String temporal_year_month_to_string(PlainYearMonth const&, ShowCalendar);
|
||||
ThrowCompletionOr<GC::Ref<Duration>> difference_temporal_plain_year_month(VM&, DurationOperation, PlainYearMonth const&, Value other, Value options);
|
||||
ThrowCompletionOr<GC::Ref<PlainYearMonth>> add_duration_to_year_month(VM&, ArithmeticOperation, PlainYearMonth const&, Value temporal_duration_like, Value options);
|
||||
|
||||
}
|
||||
|
|
|
@ -42,6 +42,8 @@ void PlainYearMonthPrototype::initialize(Realm& realm)
|
|||
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
define_native_function(realm, vm.names.with, with, 1, attr);
|
||||
define_native_function(realm, vm.names.add, add, 1, attr);
|
||||
define_native_function(realm, vm.names.subtract, subtract, 1, attr);
|
||||
define_native_function(realm, vm.names.until, until, 1, attr);
|
||||
define_native_function(realm, vm.names.since, since, 1, attr);
|
||||
define_native_function(realm, vm.names.equals, equals, 1, attr);
|
||||
|
@ -173,6 +175,34 @@ JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::with)
|
|||
return MUST(create_temporal_year_month(vm, iso_date, calendar));
|
||||
}
|
||||
|
||||
// 9.3.14 Temporal.PlainYearMonth.prototype.add ( temporalDurationLike [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.add
|
||||
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::add)
|
||||
{
|
||||
auto temporal_duration_like = vm.argument(0);
|
||||
auto options = vm.argument(1);
|
||||
|
||||
// 1. Let yearMonth be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
|
||||
auto year_month = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return ? AddDurationToYearMonth(ADD, yearMonth, temporalDurationLike, options).
|
||||
return TRY(add_duration_to_year_month(vm, ArithmeticOperation::Add, year_month, temporal_duration_like, options));
|
||||
}
|
||||
|
||||
// 9.3.15 Temporal.PlainYearMonth.prototype.subtract ( temporalDurationLike [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.subtract
|
||||
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::subtract)
|
||||
{
|
||||
auto temporal_duration_like = vm.argument(0);
|
||||
auto options = vm.argument(1);
|
||||
|
||||
// 1. Let yearMonth be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
|
||||
auto year_month = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return ? AddDurationToYearMonth(SUBTRACT, yearMonth, temporalDurationLike, options).
|
||||
return TRY(add_duration_to_year_month(vm, ArithmeticOperation::Subtract, year_month, temporal_duration_like, options));
|
||||
}
|
||||
|
||||
// 9.3.16 Temporal.PlainYearMonth.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.until
|
||||
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::until)
|
||||
{
|
||||
|
|
|
@ -34,6 +34,8 @@ private:
|
|||
JS_DECLARE_NATIVE_FUNCTION(months_in_year_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(in_leap_year_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(with);
|
||||
JS_DECLARE_NATIVE_FUNCTION(add);
|
||||
JS_DECLARE_NATIVE_FUNCTION(subtract);
|
||||
JS_DECLARE_NATIVE_FUNCTION(until);
|
||||
JS_DECLARE_NATIVE_FUNCTION(since);
|
||||
JS_DECLARE_NATIVE_FUNCTION(equals);
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
describe("correct behavior", () => {
|
||||
test("length is 1", () => {
|
||||
expect(Temporal.PlainYearMonth.prototype.add).toHaveLength(1);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
const plainYearMonth = new Temporal.PlainYearMonth(1970, 1);
|
||||
const result = plainYearMonth.add(new Temporal.Duration(51, 6));
|
||||
expect(result.equals(new Temporal.PlainYearMonth(2021, 7))).toBeTrue();
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.PlainYearMonth object", () => {
|
||||
expect(() => {
|
||||
Temporal.PlainYearMonth.prototype.add.call("foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainYearMonth");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
describe("correct behavior", () => {
|
||||
test("length is 1", () => {
|
||||
expect(Temporal.PlainYearMonth.prototype.subtract).toHaveLength(1);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
const plainYearMonth = new Temporal.PlainYearMonth(2021, 7);
|
||||
const result = plainYearMonth.subtract(new Temporal.Duration(51, 6));
|
||||
expect(result.equals(new Temporal.PlainYearMonth(1970, 1))).toBeTrue();
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.PlainYearMonth object", () => {
|
||||
expect(() => {
|
||||
Temporal.PlainYearMonth.prototype.subtract.call("foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainYearMonth");
|
||||
});
|
||||
});
|
Loading…
Add table
Reference in a new issue