2021-07-27 00:21:16 +01:00
/*
2022-03-10 17:04:46 +01:00
* Copyright ( c ) 2021 - 2022 , Linus Groh < linusg @ serenityos . org >
2021-07-27 00:21:16 +01:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2021-08-07 22:40:32 +01:00
# include <LibJS/Runtime/AbstractOperations.h>
# 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>
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 )
: Object ( prototype )
, 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 ) ;
2021-08-07 22:40:32 +01:00
visitor . visit ( & m_calendar ) ;
}
2021-09-09 06:27:55 +01:00
// 9.5.1 ToTemporalYearMonth ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalyearmonth
2022-04-07 23:54:17 +01:00
ThrowCompletionOr < PlainYearMonth * > to_temporal_year_month ( GlobalObject & global_object , Value item , Object const * options )
2021-09-09 06:27:55 +01:00
{
auto & vm = global_object . vm ( ) ;
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).
2021-09-16 21:42:01 +01:00
auto * calendar = TRY ( get_temporal_calendar_with_iso_default ( global_object , item_object ) ) ;
2021-09-09 06:27:55 +01:00
// c. Let fieldNames be ? CalendarFields(calendar, « "month", "monthCode", "year" »).
2021-09-16 21:42:01 +01:00
auto field_names = TRY ( calendar_fields ( global_object , * calendar , { " month " sv , " monthCode " sv , " year " sv } ) ) ;
2021-09-09 06:27:55 +01:00
// d. Let fields be ? PrepareTemporalFields(item, fieldNames, «»).
2021-09-16 18:09:52 +01:00
auto * fields = TRY ( prepare_temporal_fields ( global_object , item_object , field_names , { } ) ) ;
2021-09-09 06:27:55 +01:00
2022-04-29 18:34:16 +02:00
// e. Return ? CalendarYearMonthFromFields(calendar, fields, options).
return calendar_year_month_from_fields ( global_object , * calendar , * fields , options ) ;
2021-09-09 06:27:55 +01:00
}
// 4. Perform ? ToTemporalOverflow(options).
2022-04-06 23:56:57 +01:00
( void ) TRY ( to_temporal_overflow ( global_object , options ) ) ;
2021-09-09 06:27:55 +01:00
// 5. Let string be ? ToString(item).
2021-10-12 17:49:01 +01:00
auto string = TRY ( item . to_string ( global_object ) ) ;
2021-09-09 06:27:55 +01:00
// 6. Let result be ? ParseTemporalYearMonthString(string).
2021-09-16 17:59:54 +01:00
auto result = TRY ( parse_temporal_year_month_string ( global_object , string ) ) ;
2021-09-09 06:27:55 +01:00
// 7. Let calendar be ? ToTemporalCalendarWithISODefault(result.[[Calendar]]).
2021-09-16 21:42:01 +01:00
auto * calendar = TRY ( to_temporal_calendar_with_iso_default ( global_object , result . calendar . has_value ( ) ? js_string ( 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]]).
2021-09-16 17:59:54 +01:00
auto * creation_result = TRY ( create_temporal_year_month ( global_object , 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).
return calendar_year_month_from_fields ( global_object , * 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
2021-09-16 01:57:24 +03:00
ThrowCompletionOr < ISOYearMonth > regulate_iso_year_month ( GlobalObject & global_object , double year , double month , StringView overflow )
2021-07-27 00:21:16 +01:00
{
2021-08-16 00:35:05 +01:00
auto & vm = global_object . vm ( ) ;
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 ) )
return vm . throw_completion < RangeError > ( global_object , ErrorType : : TemporalInvalidPlainYearMonth ) ;
2021-08-16 00:35:05 +01:00
// a. Return ! ConstrainISOYearMonth(year, month).
return constrain_iso_year_month ( year , month ) ;
}
2021-07-27 00:21:16 +01:00
2021-08-16 00:35:05 +01:00
// 4. If overflow is "reject", then
if ( overflow = = " reject " sv ) {
// 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 ) )
return vm . throw_completion < RangeError > ( global_object , ErrorType : : TemporalInvalidPlainYearMonth ) ;
2021-08-16 00:35:05 +01:00
// a. If ! IsValidISOMonth(month) is false, throw a RangeError exception.
2021-09-16 01:57:24 +03:00
if ( ! is_valid_iso_month ( month ) )
return vm . throw_completion < RangeError > ( global_object , ErrorType : : TemporalInvalidPlainYearMonth ) ;
2021-08-16 00:35:05 +01:00
2021-08-17 20:38:29 +01:00
// b. 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
2021-08-16 00:35:05 +01:00
VERIFY_NOT_REACHED ( ) ;
}
2021-07-27 00:21:16 +01:00
2021-08-16 00:35:05 +01:00
// 9.5.3 IsValidISOMonth ( month ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidisomonth
bool is_valid_iso_month ( u8 month )
{
// 1. Assert: month is an integer.
// 2. If month < 1 or month > 12, then
if ( month < 1 | | month > 12 ) {
// a.Return false.
return false ;
}
// 3. Return true.
return true ;
2021-07-27 00:21:16 +01:00
}
2021-08-07 22:40:32 +01:00
// 9.5.4 ISOYearMonthWithinLimits ( year, month ), https://tc39.es/proposal-temporal/#sec-temporal-isoyearmonthwithinlimits
bool iso_year_month_within_limits ( i32 year , u8 month )
{
// 1. Assert: year and month are integers.
// 2. If year < − 271821 or year > 275760, then
if ( year < - 271821 | | year > 275760 ) {
// a. Return false.
return false ;
}
// 3. If year is − 271821 and month < 4, then
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 ;
}
2021-08-16 00:35:05 +01:00
// 9.5.5 BalanceISOYearMonth ( year, month ), https://tc39.es/proposal-temporal/#sec-temporal-balanceisoyearmonth
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 ) ;
// 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 } ;
}
// 9.5.6 ConstrainISOYearMonth ( year, month ), https://tc39.es/proposal-temporal/#sec-temporal-constrainisoyearmonth
ISOYearMonth constrain_iso_year_month ( double year , double month )
{
// 1. Assert: year and month are integers.
VERIFY ( year = = trunc ( year ) & & month = = trunc ( month ) ) ;
2022-03-16 19:10:57 +00:00
// 2. Set month to the result of clamping month between 1 and 12.
month = clamp ( month , 1 , 12 ) ;
2021-08-16 00:35:05 +01:00
// 3. Return the Record { [[Year]]: year, [[Month]]: month }.
// NOTE: `year` is known to be in the i32 range.
return ISOYearMonth { . year = static_cast < i32 > ( year ) , . month = static_cast < u8 > ( month ) , . reference_iso_day = 0 } ;
}
2021-08-07 22:40:32 +01:00
// 9.5.7 CreateTemporalYearMonth ( isoYear, isoMonth, calendar, referenceISODay [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalyearmonth
2021-09-16 01:57:24 +03:00
ThrowCompletionOr < PlainYearMonth * > create_temporal_year_month ( GlobalObject & global_object , i32 iso_year , u8 iso_month , Object & calendar , u8 reference_iso_day , FunctionObject const * new_target )
2021-08-07 22:40:32 +01:00
{
auto & vm = global_object . vm ( ) ;
// 1. Assert: isoYear, isoMonth, and referenceISODay are integers.
// 2. Assert: Type(calendar) is Object.
// 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 ) )
return vm . throw_completion < RangeError > ( global_object , 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 ) )
return vm . throw_completion < RangeError > ( global_object , 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 )
new_target = global_object . temporal_plain_year_month_constructor ( ) ;
// 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.
2021-09-16 01:57:24 +03:00
auto * object = TRY ( ordinary_create_from_constructor < PlainYearMonth > ( global_object , * new_target , & GlobalObject : : temporal_plain_year_month_prototype , iso_year , iso_month , reference_iso_day , calendar ) ) ;
2021-08-07 22:40:32 +01:00
// 11. Return object.
return object ;
}
2021-08-19 00:43:39 +01:00
// 9.5.8 TemporalYearMonthToString ( yearMonth, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-temporalyearmonthtostring
2021-09-16 01:57:24 +03:00
ThrowCompletionOr < String > temporal_year_month_to_string ( GlobalObject & global_object , 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.
auto result = String : : formatted ( " {}-{:02} " , pad_iso_year ( year_month . iso_year ( ) ) , year_month . iso_month ( ) ) ;
// 6. Let calendarID be ? ToString(yearMonth.[[Calendar]]).
2021-10-12 17:49:01 +01:00
auto calendar_id = TRY ( Value ( & year_month . calendar ( ) ) . to_string ( global_object ) ) ;
2021-08-19 00:43:39 +01:00
2022-03-30 17:56:30 +01:00
// 7. If showCalendar is "always" or if calendarID is not "iso8601", then
if ( show_calendar = = " always " 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.
result = String : : formatted ( " {}-{:02} " , result , year_month . iso_day ( ) ) ;
}
// 8. Let calendarString be ! FormatCalendarAnnotation(calendarID, showCalendar).
auto calendar_string = format_calendar_annotation ( calendar_id , show_calendar ) ;
// 9. Set result to the string-concatenation of result and calendarString.
// 10. Return result.
return String : : formatted ( " {}{} " , result , calendar_string ) ;
}
2021-07-27 00:21:16 +01:00
}