1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 06:07:34 +00:00

LibJS: Implement Temporal.PlainYearMonth.prototype.subtract()

This commit is contained in:
Linus Groh 2021-11-27 17:03:19 +00:00
parent acbcd64cdc
commit 6f2e0b3355
3 changed files with 93 additions and 0 deletions

View file

@ -44,6 +44,7 @@ void PlainYearMonthPrototype::initialize(GlobalObject& global_object)
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(vm.names.with, with, 1, attr);
define_native_function(vm.names.add, add, 1, attr);
define_native_function(vm.names.subtract, subtract, 1, attr);
define_native_function(vm.names.equals, equals, 1, attr);
define_native_function(vm.names.toString, to_string, 0, attr);
define_native_function(vm.names.toLocaleString, to_locale_string, 0, attr);
@ -305,6 +306,78 @@ JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::add)
return TRY(year_month_from_fields(global_object, calendar, *added_date_fields, options_copy));
}
// 9.3.13 Temporal.PlainYearMonth.prototype.subtract ( temporalDurationLike [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.subtract
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::subtract)
{
// 1. Let yearMonth be the this value.
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
auto* year_month = TRY(typed_this_object(global_object));
// 3. Let duration be ? ToLimitedTemporalDuration(temporalDurationLike, « »).
auto duration = TRY(to_limited_temporal_duration(global_object, vm.argument(0), {}));
// 4. Let balanceResult be ? BalanceDuration(duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], "day").
auto balance_result = TRY(balance_duration(global_object, duration.days, duration.hours, duration.minutes, duration.seconds, duration.milliseconds, duration.microseconds, *js_bigint(vm, Crypto::SignedBigInteger::create_from((i64)duration.nanoseconds)), "day"sv));
// 5. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(global_object, vm.argument(1)));
// 6. Let calendar be yearMonth.[[Calendar]].
auto& calendar = year_month->calendar();
// 7. Let fieldNames be ? CalendarFields(calendar, « "monthCode", "year" »).
auto field_names = TRY(calendar_fields(global_object, calendar, { "monthCode"sv, "year"sv }));
// 8. Let sign be ! DurationSign(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], balanceResult.[[Days]], 0, 0, 0, 0, 0, 0).
auto sign = duration_sign(duration.years, duration.months, duration.weeks, balance_result.days, 0, 0, 0, 0, 0, 0);
double day;
// 9. If sign < 0, then
if (sign < 0) {
// a. Let dayFromCalendar be ? CalendarDaysInMonth(calendar, yearMonth).
auto day_from_calendar = TRY(calendar_days_in_month(global_object, calendar, *year_month));
// b. Let day be ? ToPositiveInteger(dayFromCalendar).
day = TRY(to_positive_integer(global_object, day_from_calendar));
}
// 10. Else,
else {
// a. Let day be 1.
day = 1;
}
// 11. Let date be ? CreateTemporalDate(yearMonth.[[ISOYear]], yearMonth.[[ISOMonth]], day, calendar).
auto* date = TRY(create_temporal_date(global_object, year_month->iso_year(), year_month->iso_month(), day, calendar));
// 12. Let durationToAdd be ? CreateTemporalDuration(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], balanceResult.[[Days]], 0, 0, 0, 0, 0, 0).
auto* duration_to_add = TRY(create_temporal_duration(global_object, -duration.years, -duration.months, -duration.weeks, -balance_result.days, 0, 0, 0, 0, 0, 0));
// 13. Let optionsCopy be ! OrdinaryObjectCreate(%Object.prototype%).
auto* options_copy = Object::create(global_object, global_object.object_prototype());
// 14. Let entries be ? EnumerableOwnPropertyNames(options, key+value).
auto entries = TRY(options->enumerable_own_property_names(Object::PropertyKind::KeyAndValue));
// 15. For each element nextEntry of entries, do
for (auto& next_entry : entries) {
auto key = MUST(next_entry.as_array().get_without_side_effects(0).to_property_key(global_object));
auto value = next_entry.as_array().get_without_side_effects(1);
// a. Perform ! CreateDataPropertyOrThrow(optionsCopy, nextEntry[0], nextEntry[1]).
MUST(options_copy->create_data_property_or_throw(key, value));
}
// 16. Let addedDate be ? CalendarDateAdd(calendar, date, durationToAdd, options).
auto* added_date = TRY(calendar_date_add(global_object, calendar, date, *duration_to_add, options));
// 17. Let addedDateFields be ? PrepareTemporalFields(addedDate, fieldNames, «»).
auto* added_date_fields = TRY(prepare_temporal_fields(global_object, *added_date, field_names, {}));
// 18. Return ? YearMonthFromFields(calendar, addedDateFields, optionsCopy).
return TRY(year_month_from_fields(global_object, calendar, *added_date_fields, options_copy));
}
// 9.3.16 Temporal.PlainYearMonth.prototype.equals ( other ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.equals
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::equals)
{

View file

@ -32,6 +32,7 @@ private:
JS_DECLARE_NATIVE_FUNCTION(era_year_getter);
JS_DECLARE_NATIVE_FUNCTION(with);
JS_DECLARE_NATIVE_FUNCTION(add);
JS_DECLARE_NATIVE_FUNCTION(subtract);
JS_DECLARE_NATIVE_FUNCTION(equals);
JS_DECLARE_NATIVE_FUNCTION(to_string);
JS_DECLARE_NATIVE_FUNCTION(to_locale_string);

View file

@ -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");
});
});