2021-08-14 23:54:24 +01:00
/*
2023-01-26 15:49:37 +00:00
* Copyright ( c ) 2021 - 2023 , Linus Groh < linusg @ serenityos . org >
2021-09-10 03:09:25 +01:00
* Copyright ( c ) 2021 , Luke Wilde < lukew @ serenityos . org >
2021-08-14 23:54:24 +01:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2022-11-23 13:41:50 +01:00
# include <AK/TypeCasts.h>
2021-08-14 23:54:24 +01:00
# include <LibJS/Runtime/AbstractOperations.h>
# include <LibJS/Runtime/GlobalObject.h>
2021-08-19 08:39:34 +01:00
# include <LibJS/Runtime/Temporal/Calendar.h>
2021-08-14 23:54:24 +01:00
# include <LibJS/Runtime/Temporal/PlainDate.h>
2021-09-10 03:09:25 +01:00
# include <LibJS/Runtime/Temporal/PlainDateTime.h>
2021-08-14 23:54:24 +01:00
# include <LibJS/Runtime/Temporal/PlainMonthDay.h>
# include <LibJS/Runtime/Temporal/PlainMonthDayConstructor.h>
2021-09-10 03:09:25 +01:00
# include <LibJS/Runtime/Temporal/PlainTime.h>
# include <LibJS/Runtime/Temporal/ZonedDateTime.h>
2021-08-14 23:54:24 +01:00
namespace JS : : Temporal {
// 10 Temporal.PlainMonthDay Objects, https://tc39.es/proposal-temporal/#sec-temporal-plainmonthday-objects
PlainMonthDay : : PlainMonthDay ( u8 iso_month , u8 iso_day , i32 iso_year , Object & calendar , Object & prototype )
2022-12-14 12:17:58 +01:00
: Object ( ConstructWithPrototypeTag : : Tag , prototype )
2021-08-14 23:54:24 +01:00
, m_iso_year ( iso_year )
, m_iso_month ( iso_month )
, m_iso_day ( iso_day )
, m_calendar ( calendar )
{
}
void PlainMonthDay : : visit_edges ( Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2023-02-26 16:09:02 -07:00
visitor . visit ( m_calendar ) ;
2021-08-14 23:54:24 +01:00
}
2021-09-10 03:09:25 +01:00
// 10.5.1 ToTemporalMonthDay ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalmonthday
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < PlainMonthDay * > to_temporal_month_day ( VM & vm , Value item , Object const * options )
2021-09-10 03:09:25 +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-10 03:09:25 +01:00
2022-04-06 23:56:57 +01:00
// 3. Let referenceISOYear be 1972 (the first leap year after the Unix epoch).
2021-09-10 03:09:25 +01:00
i32 reference_iso_year = 1972 ;
2022-04-06 23:56:57 +01:00
// 4. If Type(item) is Object, then
2021-09-10 03:09:25 +01:00
if ( item . is_object ( ) ) {
auto & item_object = item . as_object ( ) ;
// a. If item has an [[InitializedTemporalMonthDay]] internal slot, then
if ( is < PlainMonthDay > ( item_object ) ) {
// i. Return item.
return static_cast < PlainMonthDay * > ( & item_object ) ;
}
Object * calendar = nullptr ;
bool calendar_absent ;
// b. If item has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalTime]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
// i. Let calendar be item.[[Calendar]].
// ii. Let calendarAbsent be false.
if ( is < PlainDate > ( item_object ) ) {
calendar = & static_cast < PlainDate & > ( item_object ) . calendar ( ) ;
calendar_absent = false ;
} else if ( is < PlainDateTime > ( item_object ) ) {
calendar = & static_cast < PlainDateTime & > ( item_object ) . calendar ( ) ;
calendar_absent = false ;
} else if ( is < PlainMonthDay > ( item_object ) ) {
calendar = & static_cast < PlainMonthDay & > ( item_object ) . calendar ( ) ;
calendar_absent = false ;
} else if ( is < PlainTime > ( item_object ) ) {
calendar = & static_cast < PlainTime & > ( item_object ) . calendar ( ) ;
calendar_absent = false ;
} else if ( is < PlainYearMonth > ( item_object ) ) {
calendar = & static_cast < PlainYearMonth & > ( item_object ) . calendar ( ) ;
calendar_absent = false ;
} else if ( is < ZonedDateTime > ( item_object ) ) {
calendar = & static_cast < ZonedDateTime & > ( item_object ) . calendar ( ) ;
calendar_absent = false ;
} else {
2022-02-04 19:29:15 +00:00
// i. Let calendarLike be ? Get(item, "calendar").
auto calendar_like = TRY ( item_object . get ( vm . names . calendar ) ) ;
2021-09-10 03:09:25 +01:00
2022-02-04 19:29:15 +00:00
// ii. If calendarLike is undefined, then
2021-09-10 03:09:25 +01:00
// 1. Let calendarAbsent be true.
// iii. Else,
// 1. Let calendarAbsent be false.
2022-02-04 19:29:15 +00:00
calendar_absent = calendar_like . is_undefined ( ) ;
2021-09-10 03:09:25 +01:00
2022-02-04 19:29:15 +00:00
// iv. Let calendar be ? ToTemporalCalendarWithISODefault(calendarLike).
2022-08-20 08:52:42 +01:00
calendar = TRY ( to_temporal_calendar_with_iso_default ( vm , calendar_like ) ) ;
2021-09-10 03:09:25 +01:00
}
// d. Let fieldNames be ? CalendarFields(calendar, « "day", "month", "monthCode", "year" »).
2022-08-20 08:52:42 +01:00
auto field_names = TRY ( calendar_fields ( vm , * calendar , { " day " sv , " month " sv , " monthCode " sv , " year " sv } ) ) ;
2021-09-10 03:09:25 +01:00
// e. 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-10 03:09:25 +01:00
// f. Let month be ? Get(fields, "month").
2021-10-02 23:52:27 +01:00
auto month = TRY ( fields - > get ( vm . names . month ) ) ;
2021-09-10 03:09:25 +01:00
// g. Let monthCode be ? Get(fields, "monthCode").
2021-10-02 23:52:27 +01:00
auto month_code = TRY ( fields - > get ( vm . names . monthCode ) ) ;
2021-09-10 03:09:25 +01:00
// h. Let year be ? Get(fields, "year").
2021-10-02 23:52:27 +01:00
auto year = TRY ( fields - > get ( vm . names . year ) ) ;
2021-09-10 03:09:25 +01:00
// i. If calendarAbsent is true, and month is not undefined, and monthCode is undefined and year is undefined, then
if ( calendar_absent & & ! month . is_undefined ( ) & & month_code . is_undefined ( ) & & year . is_undefined ( ) ) {
// i. Perform ! CreateDataPropertyOrThrow(fields, "year", 𝔽 (referenceISOYear)).
2021-10-03 01:18:46 +01:00
MUST ( fields - > create_data_property_or_throw ( vm . names . year , Value ( reference_iso_year ) ) ) ;
2021-09-10 03:09:25 +01:00
}
2022-04-29 18:34:16 +02:00
// j. Return ? CalendarMonthDayFromFields(calendar, fields, options).
2022-08-20 08:52:42 +01:00
return calendar_month_day_from_fields ( vm , * calendar , * fields , options ) ;
2021-09-10 03:09:25 +01:00
}
2022-04-06 23:56:57 +01:00
// 5. Perform ? ToTemporalOverflow(options).
2022-08-20 08:52:42 +01:00
( void ) TRY ( to_temporal_overflow ( vm , options ) ) ;
2021-11-10 22:28:27 +00:00
2022-04-06 23:56:57 +01:00
// 6. Let string be ? ToString(item).
2023-01-26 14:22:47 +00:00
auto string = TRY ( item . to_string ( vm ) ) ;
2021-11-10 22:28:27 +00:00
2022-04-06 23:56:57 +01:00
// 7. Let result be ? ParseTemporalMonthDayString(string).
2022-08-20 08:52:42 +01:00
auto result = TRY ( parse_temporal_month_day_string ( vm , string ) ) ;
2021-11-10 22:28:27 +00:00
2022-04-06 23:56:57 +01:00
// 8. 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 , move ( * result . calendar ) ) : js_undefined ( ) ) ) ;
2021-11-10 22:28:27 +00:00
2022-04-06 23:56:57 +01:00
// 9. If result.[[Year]] is undefined, then
2021-11-10 22:28:27 +00:00
if ( ! result . year . has_value ( ) ) {
// a. Return ? CreateTemporalMonthDay(result.[[Month]], result.[[Day]], calendar, referenceISOYear).
2022-08-20 08:52:42 +01:00
return TRY ( create_temporal_month_day ( vm , result . month , result . day , * calendar , reference_iso_year ) ) ;
2021-11-10 22:28:27 +00:00
}
2022-04-06 23:56:57 +01:00
// 10. Set result to ? CreateTemporalMonthDay(result.[[Month]], result.[[Day]], calendar, referenceISOYear).
2022-08-20 08:52:42 +01:00
auto * plain_month_day = TRY ( create_temporal_month_day ( vm , result . month , result . day , * calendar , reference_iso_year ) ) ;
2021-11-10 22:28:27 +00:00
2022-04-06 23:56:57 +01:00
// 11. NOTE: The following operation is called without options, in order for the calendar to store a canonical value in the [[ISOYear]] internal slot of the result.
2022-04-29 18:34:16 +02:00
// 12. Return ? CalendarMonthDayFromFields(calendar, result).
2022-08-20 08:52:42 +01:00
return TRY ( calendar_month_day_from_fields ( vm , * calendar , * plain_month_day ) ) ;
2021-09-10 03:09:25 +01:00
}
2021-08-14 23:54:24 +01:00
// 10.5.2 CreateTemporalMonthDay ( isoMonth, isoDay, calendar, referenceISOYear [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalmonthday
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < PlainMonthDay * > create_temporal_month_day ( VM & vm , u8 iso_month , u8 iso_day , Object & calendar , i32 reference_iso_year , FunctionObject const * new_target )
2021-08-14 23:54:24 +01:00
{
2022-08-20 08:52:42 +01:00
auto & realm = * vm . current_realm ( ) ;
2021-08-14 23:54:24 +01:00
// 1. Assert: isoMonth, isoDay, and referenceISOYear are integers.
// 2. Assert: Type(calendar) is Object.
2022-04-29 19:03:57 +02:00
// 3. If IsValidISODate(referenceISOYear, isoMonth, isoDay) is false, throw a RangeError exception.
2021-09-16 02:11:23 +03:00
if ( ! is_valid_iso_date ( reference_iso_year , iso_month , iso_day ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidPlainMonthDay ) ;
2021-08-14 23:54:24 +01:00
2022-07-22 15:07:39 +01:00
// 4. If ISODateTimeWithinLimits(referenceISOYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0) is false, throw a RangeError exception.
2022-10-14 09:47:17 -04:00
if ( ! iso_date_time_within_limits ( reference_iso_year , iso_month , iso_day , 12 , 0 , 0 , 0 , 0 , 0 ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidPlainMonthDay ) ;
2022-07-22 15:07:39 +01:00
// 5. If newTarget is not present, set newTarget to %Temporal.PlainMonthDay%.
2021-08-14 23:54:24 +01:00
if ( ! new_target )
2022-08-27 00:54:55 +01:00
new_target = realm . intrinsics ( ) . temporal_plain_month_day_constructor ( ) ;
2021-08-14 23:54:24 +01:00
2022-07-22 15:07:39 +01:00
// 6. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainMonthDay.prototype%", « [[InitializedTemporalMonthDay]], [[ISOMonth]], [[ISODay]], [[ISOYear]], [[Calendar]] »).
// 7. Set object.[[ISOMonth]] to isoMonth.
// 8. Set object.[[ISODay]] to isoDay.
// 9. Set object.[[Calendar]] to calendar.
// 10. Set object.[[ISOYear]] to referenceISOYear.
2022-12-14 18:34:32 +00:00
auto object = TRY ( ordinary_create_from_constructor < PlainMonthDay > ( vm , * new_target , & Intrinsics : : temporal_plain_month_day_prototype , iso_month , iso_day , reference_iso_year , calendar ) ) ;
2021-08-14 23:54:24 +01:00
2022-07-22 15:07:39 +01:00
// 11. Return object.
2022-12-14 18:34:32 +00:00
return object . ptr ( ) ;
2021-08-14 23:54:24 +01:00
}
2021-08-19 08:39:34 +01:00
// 10.5.3 TemporalMonthDayToString ( monthDay, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-temporalmonthdaytostring
2023-01-26 15:49:37 +00:00
ThrowCompletionOr < String > temporal_month_day_to_string ( VM & vm , PlainMonthDay & month_day , StringView show_calendar )
2021-08-19 08:39:34 +01:00
{
// 1. Assert: Type(monthDay) is Object.
// 2. Assert: monthDay has an [[InitializedTemporalMonthDay]] internal slot.
2022-04-12 22:20:33 +01:00
// 3. Let month be ToZeroPaddedDecimalString(temporalDate.[[ISOMonth]], 2).
// 4. Let day be ToZeroPaddedDecimalString(temporalDate.[[ISODay]], 2).
2021-08-19 08:39:34 +01:00
// 5. Let result be the string-concatenation of month, the code unit 0x002D (HYPHEN-MINUS), and day.
2023-01-26 15:49:37 +00:00
auto result = TRY_OR_THROW_OOM ( vm , String : : formatted ( " {:02}-{:02} " , month_day . iso_month ( ) , month_day . iso_day ( ) ) ) ;
2021-08-19 08:39:34 +01:00
// 6. Let calendarID be ? ToString(monthDay.[[Calendar]]).
2023-01-26 15:49:37 +00:00
auto calendar_id = TRY ( Value ( & month_day . calendar ( ) ) . to_string ( vm ) ) ;
2021-08-19 08:39:34 +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 " sv ) {
2021-08-19 08:39:34 +01:00
// a. Let year be ! PadISOYear(monthDay.[[ISOYear]]).
// b. Set result to the string-concatenation of year, the code unit 0x002D (HYPHEN-MINUS), and result.
2023-01-26 15:54:09 +00:00
result = TRY_OR_THROW_OOM ( vm , String : : formatted ( " {}-{} " , MUST_OR_THROW_OOM ( pad_iso_year ( vm , month_day . iso_year ( ) ) ) , result ) ) ;
2021-08-19 08:39:34 +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 08:39:34 +01:00
// 9. Set result to the string-concatenation of result and calendarString.
// 10. Return result.
2023-01-26 15:49:37 +00:00
return TRY_OR_THROW_OOM ( vm , String : : formatted ( " {}{} " , result , calendar_string ) ) ;
2021-08-19 08:39:34 +01:00
}
2021-08-14 23:54:24 +01:00
}