2021-07-27 00:21:16 +01:00
/*
2023-01-26 14:07:33 +00:00
* Copyright ( c ) 2021 - 2023 , Linus Groh < linusg @ serenityos . org >
2021-07-27 00:21:16 +01:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2022-11-23 13:41:50 +01:00
# include <AK/TypeCasts.h>
2021-08-07 22:40:32 +01:00
# include <LibJS/Runtime/AbstractOperations.h>
2022-05-06 19:01:15 +02:00
# include <LibJS/Runtime/Array.h>
2021-08-07 22:40:32 +01:00
# include <LibJS/Runtime/GlobalObject.h>
2021-08-16 00:35:05 +01:00
# include <LibJS/Runtime/Temporal/AbstractOperations.h>
2021-08-19 00:43:39 +01:00
# include <LibJS/Runtime/Temporal/Calendar.h>
2022-05-06 19:01:15 +02:00
# include <LibJS/Runtime/Temporal/Duration.h>
2021-08-07 22:40:32 +01:00
# include <LibJS/Runtime/Temporal/PlainDate.h>
2021-07-27 00:21:16 +01:00
# include <LibJS/Runtime/Temporal/PlainYearMonth.h>
2021-08-07 22:40:32 +01:00
# include <LibJS/Runtime/Temporal/PlainYearMonthConstructor.h>
2021-07-27 00:21:16 +01:00
namespace JS : : Temporal {
2021-08-07 22:40:32 +01:00
// 9 Temporal.PlainYearMonth Objects, https://tc39.es/proposal-temporal/#sec-temporal-plainyearmonth-objects
PlainYearMonth : : PlainYearMonth ( i32 iso_year , u8 iso_month , u8 iso_day , Object & calendar , Object & prototype )
2022-12-14 12:17:58 +01:00
: Object ( ConstructWithPrototypeTag : : Tag , prototype )
2021-08-07 22:40:32 +01:00
, m_iso_year ( iso_year )
, m_iso_month ( iso_month )
, m_iso_day ( iso_day )
, m_calendar ( calendar )
{
}
void PlainYearMonth : : visit_edges ( Visitor & visitor )
{
2021-08-14 20:09:26 +02:00
Base : : visit_edges ( visitor ) ;
2023-02-26 16:09:02 -07:00
visitor . visit ( m_calendar ) ;
2021-08-07 22:40:32 +01:00
}
2021-09-09 06:27:55 +01:00
// 9.5.1 ToTemporalYearMonth ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalyearmonth
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < PlainYearMonth * > to_temporal_year_month ( VM & vm , Value item , Object const * options )
2021-09-09 06:27:55 +01:00
{
2022-04-06 23:56:57 +01:00
// 1. If options is not present, set options to undefined.
// 2. Assert: Type(options) is Object or Undefined.
2021-09-09 06:27:55 +01:00
// 3. If Type(item) is Object, then
if ( item . is_object ( ) ) {
auto & item_object = item . as_object ( ) ;
// a. If item has an [[InitializedTemporalYearMonth]] internal slot, then
if ( is < PlainYearMonth > ( item_object ) ) {
// i. Return item.
return static_cast < PlainYearMonth * > ( & item_object ) ;
}
// b. Let calendar be ? GetTemporalCalendarWithISODefault(item).
2022-08-20 08:52:42 +01:00
auto * calendar = TRY ( get_temporal_calendar_with_iso_default ( vm , item_object ) ) ;
2021-09-09 06:27:55 +01:00
// c. Let fieldNames be ? CalendarFields(calendar, « "month", "monthCode", "year" »).
2022-08-20 08:52:42 +01:00
auto field_names = TRY ( calendar_fields ( vm , * calendar , { " month " sv , " monthCode " sv , " year " sv } ) ) ;
2021-09-09 06:27:55 +01:00
// d. Let fields be ? PrepareTemporalFields(item, fieldNames, «»).
2022-08-20 08:52:42 +01:00
auto * fields = TRY ( prepare_temporal_fields ( vm , item_object , field_names , Vector < StringView > { } ) ) ;
2021-09-09 06:27:55 +01:00
2022-04-29 18:34:16 +02:00
// e. Return ? CalendarYearMonthFromFields(calendar, fields, options).
2022-08-20 08:52:42 +01:00
return calendar_year_month_from_fields ( vm , * calendar , * fields , options ) ;
2021-09-09 06:27:55 +01:00
}
// 4. Perform ? ToTemporalOverflow(options).
2022-08-20 08:52:42 +01:00
( void ) TRY ( to_temporal_overflow ( vm , options ) ) ;
2021-09-09 06:27:55 +01:00
// 5. Let string be ? ToString(item).
2023-01-26 14:22:47 +00:00
auto string = TRY ( item . to_string ( vm ) ) ;
2021-09-09 06:27:55 +01:00
// 6. Let result be ? ParseTemporalYearMonthString(string).
2022-08-20 08:52:42 +01:00
auto result = TRY ( parse_temporal_year_month_string ( vm , string ) ) ;
2021-09-09 06:27:55 +01:00
// 7. Let calendar be ? ToTemporalCalendarWithISODefault(result.[[Calendar]]).
2022-12-06 22:17:27 +00:00
auto * calendar = TRY ( to_temporal_calendar_with_iso_default ( vm , result . calendar . has_value ( ) ? PrimitiveString : : create ( vm , * result . calendar ) : js_undefined ( ) ) ) ;
2021-09-09 06:27:55 +01:00
// 8. Set result to ? CreateTemporalYearMonth(result.[[Year]], result.[[Month]], calendar, result.[[Day]]).
2022-08-20 08:52:42 +01:00
auto * creation_result = TRY ( create_temporal_year_month ( vm , result . year , result . month , * calendar , result . day ) ) ;
2021-09-09 06:27:55 +01:00
2022-04-06 23:56:57 +01:00
// 9. NOTE: The following operation is called without options, in order for the calendar to store a canonical value in the [[ISODay]] internal slot of the result.
2022-04-29 18:34:16 +02:00
// 10. Return ? CalendarYearMonthFromFields(calendar, result).
2022-08-20 08:52:42 +01:00
return calendar_year_month_from_fields ( vm , * calendar , * creation_result ) ;
2021-09-09 06:27:55 +01:00
}
2021-08-16 00:35:05 +01:00
// 9.5.2 RegulateISOYearMonth ( year, month, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-regulateisoyearmonth
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < ISOYearMonth > regulate_iso_year_month ( VM & vm , double year , double month , StringView overflow )
2021-07-27 00:21:16 +01:00
{
// 1. Assert: year and month are integers.
2021-08-16 00:35:05 +01:00
VERIFY ( year = = trunc ( year ) & & month = = trunc ( month ) ) ;
// 2. Assert: overflow is either "constrain" or "reject".
// NOTE: Asserted by the VERIFY_NOT_REACHED at the end
// 3. If overflow is "constrain", then
if ( overflow = = " constrain " sv ) {
// IMPLEMENTATION DEFINED: This is an optimization that allows us to treat `year` (a double) as normal integer from this point onwards.
2021-09-07 12:56:50 +02:00
// This does not change the exposed behavior as the subsequent call to CreateTemporalYearMonth will check that its value is a valid ISO
2021-08-16 00:35:05 +01:00
// values (for years: -273975 - 273975) which is a subset of this check.
// If RegulateISOYearMonth is ever used outside ISOYearMonthFromFields, this may need to be changed.
2021-09-16 01:57:24 +03:00
if ( ! AK : : is_within_range < i32 > ( year ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidPlainYearMonth ) ;
2021-08-16 00:35:05 +01:00
2022-06-14 23:48:08 +01:00
// a. Set month to the result of clamping month between 1 and 12.
month = clamp ( month , 1 , 12 ) ;
// b. Return the Record { [[Year]]: year, [[Month]]: month }.
return ISOYearMonth { . year = static_cast < i32 > ( year ) , . month = static_cast < u8 > ( month ) , . reference_iso_day = 0 } ;
2021-08-16 00:35:05 +01:00
}
2022-06-14 23:40:58 +01:00
// 4. Else,
else {
// a. Assert: overflow is "reject".
VERIFY ( overflow = = " reject " sv ) ;
2021-07-27 00:21:16 +01:00
2021-08-16 00:35:05 +01:00
// IMPLEMENTATION DEFINED: This is an optimization that allows us to treat these doubles as normal integers from this point onwards.
2021-09-07 12:56:50 +02:00
// This does not change the exposed behavior as the call to IsValidISOMonth and subsequent call to CreateTemporalDateTime will check
2021-08-16 00:35:05 +01:00
// that these values are valid ISO values (for years: -273975 - 273975, for months: 1 - 12) all of which are subsets of this check.
2021-09-16 01:57:24 +03:00
if ( ! AK : : is_within_range < i32 > ( year ) | | ! AK : : is_within_range < u8 > ( month ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidPlainYearMonth ) ;
2021-08-16 00:35:05 +01:00
2022-10-16 13:36:59 +01:00
// b. If month < 1 or month > 12, throw a RangeError exception.
if ( month < 1 | | month > 12 )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidPlainYearMonth ) ;
2021-08-16 00:35:05 +01:00
2022-06-14 23:40:58 +01:00
// c. Return the Record { [[Year]]: year, [[Month]]: month }.
2021-08-16 00:35:05 +01:00
return ISOYearMonth { . year = static_cast < i32 > ( year ) , . month = static_cast < u8 > ( month ) , . reference_iso_day = 0 } ;
}
}
2021-07-27 00:21:16 +01:00
2022-10-16 13:36:59 +01:00
// 9.5.3 ISOYearMonthWithinLimits ( year, month ), https://tc39.es/proposal-temporal/#sec-temporal-isoyearmonthwithinlimits
2021-08-07 22:40:32 +01:00
bool iso_year_month_within_limits ( i32 year , u8 month )
{
// 1. Assert: year and month are integers.
2022-04-29 21:09:10 +02:00
// 2. If year < -271821 or year > 275760, then
2021-08-07 22:40:32 +01:00
if ( year < - 271821 | | year > 275760 ) {
// a. Return false.
return false ;
}
2022-04-29 21:09:10 +02:00
// 3. If year is -271821 and month < 4, then
2021-08-07 22:40:32 +01:00
if ( year = = - 271821 & & month < 4 ) {
// a. Return false.
return false ;
}
// 4. If year is 275760 and month > 9, then
if ( year = = 275760 & & month > 9 ) {
// a. Return false.
return false ;
}
// 5. Return true.
return true ;
}
2022-10-16 13:36:59 +01:00
// 9.5.4 BalanceISOYearMonth ( year, month ), https://tc39.es/proposal-temporal/#sec-temporal-balanceisoyearmonth
2021-08-16 00:35:05 +01:00
ISOYearMonth balance_iso_year_month ( double year , double month )
{
// 1. Assert: year and month are integers.
VERIFY ( year = = trunc ( year ) & & month = = trunc ( month ) ) ;
// 2. Set year to year + floor((month - 1) / 12).
year + = floor ( ( month - 1 ) / 12 ) ;
2022-06-14 23:43:45 +01:00
// 3. Set month to ((month - 1) modulo 12) + 1.
2021-12-21 21:22:38 +01:00
month = modulo ( month - 1 , 12 ) + 1 ;
2021-08-16 00:35:05 +01:00
2021-08-17 20:38:29 +01:00
// 4. Return the Record { [[Year]]: year, [[Month]]: month }.
2021-08-16 00:35:05 +01:00
return ISOYearMonth { . year = static_cast < i32 > ( year ) , . month = static_cast < u8 > ( month ) , . reference_iso_day = 0 } ;
}
2022-10-16 13:36:59 +01:00
// 9.5.5 CreateTemporalYearMonth ( isoYear, isoMonth, calendar, referenceISODay [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalyearmonth
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < PlainYearMonth * > create_temporal_year_month ( VM & vm , i32 iso_year , u8 iso_month , Object & calendar , u8 reference_iso_day , FunctionObject const * new_target )
2021-08-07 22:40:32 +01:00
{
2022-08-20 08:52:42 +01:00
auto & realm = * vm . current_realm ( ) ;
2021-08-07 22:40:32 +01:00
// 1. Assert: isoYear, isoMonth, and referenceISODay are integers.
// 2. Assert: Type(calendar) is Object.
2022-04-29 19:03:57 +02:00
// 3. If IsValidISODate(isoYear, isoMonth, referenceISODay) is false, throw a RangeError exception.
2021-09-16 01:57:24 +03:00
if ( ! is_valid_iso_date ( iso_year , iso_month , reference_iso_day ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidPlainYearMonth ) ;
2021-08-07 22:40:32 +01:00
// 4. If ! ISOYearMonthWithinLimits(isoYear, isoMonth) is false, throw a RangeError exception.
2021-09-16 01:57:24 +03:00
if ( ! iso_year_month_within_limits ( iso_year , iso_month ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidPlainYearMonth ) ;
2021-08-07 22:40:32 +01:00
2022-03-10 17:04:46 +01:00
// 5. If newTarget is not present, set newTarget to %Temporal.PlainYearMonth%.
2021-08-07 22:40:32 +01:00
if ( ! new_target )
2022-08-27 00:54:55 +01:00
new_target = realm . intrinsics ( ) . temporal_plain_year_month_constructor ( ) ;
2021-08-07 22:40:32 +01:00
// 6. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainYearMonth.prototype%", « [[InitializedTemporalYearMonth]], [[ISOYear]], [[ISOMonth]], [[ISODay]], [[Calendar]] »).
// 7. Set object.[[ISOYear]] to isoYear.
// 8. Set object.[[ISOMonth]] to isoMonth.
// 9. Set object.[[Calendar]] to calendar.
// 10. Set object.[[ISODay]] to referenceISODay.
2022-12-14 18:34:32 +00:00
auto object = TRY ( ordinary_create_from_constructor < PlainYearMonth > ( vm , * new_target , & Intrinsics : : temporal_plain_year_month_prototype , iso_year , iso_month , reference_iso_day , calendar ) ) ;
2021-08-07 22:40:32 +01:00
// 11. Return object.
2022-12-14 18:34:32 +00:00
return object . ptr ( ) ;
2021-08-07 22:40:32 +01:00
}
2022-10-16 13:36:59 +01:00
// 9.5.6 TemporalYearMonthToString ( yearMonth, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-temporalyearmonthtostring
2023-01-26 16:00:20 +00:00
ThrowCompletionOr < String > temporal_year_month_to_string ( VM & vm , PlainYearMonth & year_month , StringView show_calendar )
2021-08-19 00:43:39 +01:00
{
// 1. Assert: Type(yearMonth) is Object.
// 2. Assert: yearMonth has an [[InitializedTemporalYearMonth]] internal slot.
// 3. Let year be ! PadISOYear(yearMonth.[[ISOYear]]).
2022-04-12 22:20:33 +01:00
// 4. Let month be ToZeroPaddedDecimalString(yearMonth.[[ISOMonth]], 2).
2021-08-19 00:43:39 +01:00
// 5. Let result be the string-concatenation of year, the code unit 0x002D (HYPHEN-MINUS), and month.
2023-01-26 16:00:20 +00:00
auto result = TRY_OR_THROW_OOM ( vm , String : : formatted ( " {}-{:02} " , MUST_OR_THROW_OOM ( pad_iso_year ( vm , year_month . iso_year ( ) ) ) , year_month . iso_month ( ) ) ) ;
2021-08-19 00:43:39 +01:00
// 6. Let calendarID be ? ToString(yearMonth.[[Calendar]]).
2023-01-26 16:00:20 +00:00
auto calendar_id = TRY ( Value ( & year_month . calendar ( ) ) . to_string ( vm ) ) ;
2021-08-19 00:43:39 +01:00
2022-11-02 19:24:47 +00:00
// 7. If showCalendar is one of "always" or "critical", or if calendarID is not "iso8601", then
if ( show_calendar . is_one_of ( " always " sv , " critical " sv ) | | calendar_id ! = " iso8601 " ) {
2022-04-12 22:20:33 +01:00
// a. Let day be ToZeroPaddedDecimalString(yearMonth.[[ISODay]], 2).
2021-08-19 00:43:39 +01:00
// b. Set result to the string-concatenation of result, the code unit 0x002D (HYPHEN-MINUS), and day.
2023-01-26 16:00:20 +00:00
result = TRY_OR_THROW_OOM ( vm , String : : formatted ( " {}-{:02} " , result , year_month . iso_day ( ) ) ) ;
2021-08-19 00:43:39 +01:00
}
// 8. Let calendarString be ! FormatCalendarAnnotation(calendarID, showCalendar).
2023-01-26 15:27:53 +00:00
auto calendar_string = MUST_OR_THROW_OOM ( format_calendar_annotation ( vm , calendar_id , show_calendar ) ) ;
2021-08-19 00:43:39 +01:00
// 9. Set result to the string-concatenation of result and calendarString.
// 10. Return result.
2023-01-26 16:00:20 +00:00
return TRY_OR_THROW_OOM ( vm , String : : formatted ( " {}{} " , result , calendar_string ) ) ;
2021-08-19 00:43:39 +01:00
}
2022-10-16 13:36:59 +01:00
// 9.5.7 DifferenceTemporalPlainYearMonth ( operation, yearMonth, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplainyearmonth
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < Duration * > difference_temporal_plain_year_month ( VM & vm , DifferenceOperation operation , PlainYearMonth & year_month , Value other_value , Value options_value )
2022-05-07 13:32:19 +02:00
{
// 1. If operation is since, let sign be -1. Otherwise, let sign be 1.
i8 sign = operation = = DifferenceOperation : : Since ? - 1 : 1 ;
// 2. Set other to ? ToTemporalYearMonth(other).
2022-08-20 08:52:42 +01:00
auto * other = TRY ( to_temporal_year_month ( vm , other_value ) ) ;
2022-05-07 13:32:19 +02:00
// 3. Let calendar be yearMonth.[[Calendar]].
auto & calendar = year_month . calendar ( ) ;
// 4. If ? CalendarEquals(calendar, other.[[Calendar]]) is false, throw a RangeError exception.
2022-08-20 08:52:42 +01:00
if ( ! TRY ( calendar_equals ( vm , calendar , other - > calendar ( ) ) ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalDifferentCalendars ) ;
2022-05-07 13:32:19 +02:00
2022-06-24 00:27:29 +01:00
// 5. Let settings be ? GetDifferenceSettings(operation, options, date, « "week", "day" », "month", "year").
2022-08-20 08:52:42 +01:00
auto settings = TRY ( get_difference_settings ( vm , operation , options_value , UnitGroup : : Date , { " week " sv , " day " sv } , { " month " sv } , " year " sv ) ) ;
2022-05-07 13:32:19 +02:00
2022-06-24 00:27:29 +01:00
// 6. Let fieldNames be ? CalendarFields(calendar, « "monthCode", "year" »).
2022-08-20 08:52:42 +01:00
auto field_names = TRY ( calendar_fields ( vm , calendar , { " monthCode " sv , " year " sv } ) ) ;
2022-05-07 13:32:19 +02:00
2022-06-24 00:27:29 +01:00
// 7. Let otherFields be ? PrepareTemporalFields(other, fieldNames, «»).
2022-08-20 08:52:42 +01:00
auto * other_fields = TRY ( prepare_temporal_fields ( vm , * other , field_names , Vector < StringView > { } ) ) ;
2022-05-07 13:32:19 +02:00
2022-06-24 00:27:29 +01:00
// 8. Perform ! CreateDataPropertyOrThrow(otherFields, "day", 1𝔽 ).
2022-05-07 13:32:19 +02:00
MUST ( other_fields - > create_data_property_or_throw ( vm . names . day , Value ( 1 ) ) ) ;
2022-06-24 00:27:29 +01:00
// 9. Let otherDate be ? CalendarDateFromFields(calendar, otherFields).
2022-08-20 08:52:42 +01:00
auto * other_date = TRY ( calendar_date_from_fields ( vm , calendar , * other_fields ) ) ;
2022-05-07 13:32:19 +02:00
2022-06-24 00:27:29 +01:00
// 10. Let thisFields be ? PrepareTemporalFields(yearMonth, fieldNames, «»).
2022-08-20 08:52:42 +01:00
auto * this_fields = TRY ( prepare_temporal_fields ( vm , year_month , field_names , Vector < StringView > { } ) ) ;
2022-05-07 13:32:19 +02:00
2022-06-24 00:27:29 +01:00
// 11. Perform ! CreateDataPropertyOrThrow(thisFields, "day", 1𝔽 ).
2022-05-07 13:32:19 +02:00
MUST ( this_fields - > create_data_property_or_throw ( vm . names . day , Value ( 1 ) ) ) ;
2022-06-24 00:27:29 +01:00
// 12. Let thisDate be ? CalendarDateFromFields(calendar, thisFields).
2022-08-20 08:52:42 +01:00
auto * this_date = TRY ( calendar_date_from_fields ( vm , calendar , * this_fields ) ) ;
2022-05-07 13:32:19 +02:00
2022-06-24 00:27:29 +01:00
// 13. Let untilOptions be ? MergeLargestUnitOption(settings.[[Options]], settings.[[LargestUnit]]).
2023-01-26 14:43:42 +00:00
auto * until_options = TRY ( merge_largest_unit_option ( vm , settings . options , move ( settings . largest_unit ) ) ) ;
2022-05-07 13:32:19 +02:00
2022-06-24 00:27:29 +01:00
// 14. Let result be ? CalendarDateUntil(calendar, thisDate, otherDate, untilOptions).
2022-08-20 08:52:42 +01:00
auto * duration = TRY ( calendar_date_until ( vm , calendar , this_date , other_date , * until_options ) ) ;
2022-05-07 13:32:19 +02:00
auto result = DurationRecord { duration - > years ( ) , duration - > months ( ) , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
2022-06-24 00:27:29 +01:00
// 15. If settings.[[SmallestUnit]] is not "month" or settings.[[RoundingIncrement]] ≠ 1, then
if ( settings . smallest_unit ! = " month " sv | | settings . rounding_increment ! = 1 ) {
// a. Set result to (? RoundDuration(result.[[Years]], result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0, settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]], thisDate)).[[DurationRecord]].
2022-08-20 08:52:42 +01:00
result = TRY ( round_duration ( vm , result . years , result . months , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , settings . rounding_increment , settings . smallest_unit , settings . rounding_mode , this_date ) ) . duration_record ;
2022-05-07 13:32:19 +02:00
}
2022-06-24 00:27:29 +01:00
// 16. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
return MUST ( create_temporal_duration ( vm , sign * result . years , sign * result . months , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2022-05-07 13:32:19 +02:00
}
2022-12-30 20:01:41 +01:00
// 9.5.8 AddDurationToOrSubtractDurationFromPlainYearMonth ( operation, yearMonth, temporalDurationLike, options ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoorsubtractdurationfromplainyearmonth
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < PlainYearMonth * > add_duration_to_or_subtract_duration_from_plain_year_month ( VM & vm , ArithmeticOperation operation , PlainYearMonth & year_month , Value temporal_duration_like , Value options_value )
2022-05-06 19:01:15 +02:00
{
2022-08-20 08:52:42 +01:00
auto & realm = * vm . current_realm ( ) ;
2022-05-06 19:01:15 +02:00
2022-06-23 19:35:53 +01:00
// 1. Let duration be ? ToTemporalDuration(temporalDurationLike).
2022-08-20 08:52:42 +01:00
auto * duration = TRY ( to_temporal_duration ( vm , temporal_duration_like ) ) ;
2022-05-06 19:01:15 +02:00
// 2. If operation is subtract, then
if ( operation = = ArithmeticOperation : : Subtract ) {
// a. Set duration to ! CreateNegatedTemporalDuration(duration).
2022-08-20 08:52:42 +01:00
duration = create_negated_temporal_duration ( vm , * duration ) ;
2022-05-06 19:01:15 +02:00
}
// 3. Let balanceResult be ? BalanceDuration(duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], "day").
2022-08-26 00:49:50 +02:00
auto balance_result = TRY ( balance_duration ( vm , duration - > days ( ) , duration - > hours ( ) , duration - > minutes ( ) , duration - > seconds ( ) , duration - > milliseconds ( ) , duration - > microseconds ( ) , Crypto : : SignedBigInteger { duration - > nanoseconds ( ) } , " day " sv ) ) ;
2022-05-06 19:01:15 +02:00
// 4. Set options to ? GetOptionsObject(options).
2022-08-20 08:52:42 +01:00
auto * options = TRY ( get_options_object ( vm , options_value ) ) ;
2022-05-06 19:01:15 +02:00
// 5. Let calendar be yearMonth.[[Calendar]].
auto & calendar = year_month . calendar ( ) ;
// 6. Let fieldNames be ? CalendarFields(calendar, « "monthCode", "year" »).
2022-08-20 08:52:42 +01:00
auto field_names = TRY ( calendar_fields ( vm , calendar , { " monthCode " sv , " year " sv } ) ) ;
2022-05-06 19:01:15 +02:00
// 7. Let fields be ? PrepareTemporalFields(yearMonth, fieldNames, «»).
2022-08-20 08:52:42 +01:00
auto * fields = TRY ( prepare_temporal_fields ( vm , year_month , field_names , Vector < StringView > { } ) ) ;
2022-05-06 19:01:15 +02:00
// 8. Set sign to ! DurationSign(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], balanceResult.[[Days]], 0, 0, 0, 0, 0, 0).
2022-06-23 19:35:53 +01:00
auto sign = duration_sign ( duration - > years ( ) , duration - > months ( ) , duration - > weeks ( ) , balance_result . days , 0 , 0 , 0 , 0 , 0 , 0 ) ;
2022-05-06 19:01:15 +02:00
double day ;
// 9. If sign < 0, then
if ( sign < 0 ) {
2022-12-30 20:01:41 +01:00
// a. Let day be ? CalendarDaysInMonth(calendar, yearMonth).
2022-12-30 05:05:46 +01:00
day = TRY ( calendar_days_in_month ( vm , calendar , year_month ) ) ;
2022-05-06 19:01:15 +02:00
}
// 10. Else,
else {
// a. Let day be 1.
day = 1 ;
}
2022-06-23 19:59:03 +01:00
// 11. Perform ! CreateDataPropertyOrThrow(fields, "day", 𝔽 (day)).
2022-05-06 19:01:15 +02:00
MUST ( fields - > create_data_property_or_throw ( vm . names . day , Value ( day ) ) ) ;
2022-05-24 18:23:35 +01:00
// 12. Let date be ? CalendarDateFromFields(calendar, fields).
2022-08-20 08:52:42 +01:00
auto * date = TRY ( calendar_date_from_fields ( vm , calendar , * fields ) ) ;
2022-05-06 19:01:15 +02:00
// 13. Let durationToAdd be ! CreateTemporalDuration(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], balanceResult.[[Days]], 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
auto * duration_to_add = MUST ( create_temporal_duration ( vm , duration - > years ( ) , duration - > months ( ) , duration - > weeks ( ) , balance_result . days , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2022-05-06 19:01:15 +02:00
2022-06-27 23:29:11 +01:00
// 14. Let optionsCopy be OrdinaryObjectCreate(null).
2022-12-13 20:49:50 +00:00
auto options_copy = Object : : create ( realm , nullptr ) ;
2022-05-06 19:01:15 +02:00
// 15. Let entries be ? EnumerableOwnPropertyNames(options, key+value).
auto entries = TRY ( options - > enumerable_own_property_names ( Object : : PropertyKind : : KeyAndValue ) ) ;
2022-06-15 00:19:04 +01:00
// 16. For each element entry of entries, do
for ( auto & entry : entries ) {
2022-08-21 14:00:56 +01:00
auto key = MUST ( entry . as_array ( ) . get_without_side_effects ( 0 ) . to_property_key ( vm ) ) ;
2022-06-15 00:19:04 +01:00
auto value = entry . as_array ( ) . get_without_side_effects ( 1 ) ;
2022-05-06 19:01:15 +02:00
2022-06-15 00:19:04 +01:00
// a. Perform ! CreateDataPropertyOrThrow(optionsCopy, entry[0], entry[1]).
2022-05-06 19:01:15 +02:00
MUST ( options_copy - > create_data_property_or_throw ( key , value ) ) ;
}
// 17. Let addedDate be ? CalendarDateAdd(calendar, date, durationToAdd, options).
2022-08-20 08:52:42 +01:00
auto * added_date = TRY ( calendar_date_add ( vm , calendar , date , * duration_to_add , options ) ) ;
2022-05-06 19:01:15 +02:00
// 18. Let addedDateFields be ? PrepareTemporalFields(addedDate, fieldNames, «»).
2022-08-20 08:52:42 +01:00
auto * added_date_fields = TRY ( prepare_temporal_fields ( vm , * added_date , field_names , Vector < StringView > { } ) ) ;
2022-05-06 19:01:15 +02:00
// 19. Return ? CalendarYearMonthFromFields(calendar, addedDateFields, optionsCopy).
2022-08-20 08:52:42 +01:00
return calendar_year_month_from_fields ( vm , calendar , * added_date_fields , options_copy ) ;
2022-05-06 19:01:15 +02:00
}
2021-07-27 00:21:16 +01:00
}