2021-07-11 21:04:11 +03:00
/*
* Copyright ( c ) 2021 , Idan Horowitz < idan . horowitz @ serenityos . org >
2023-01-26 14:07:33 +00:00
* Copyright ( c ) 2021 - 2023 , Linus Groh < linusg @ serenityos . org >
2021-07-11 21:04:11 +03:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2022-11-23 13:41:50 +01:00
# include <AK/TypeCasts.h>
2021-07-19 00:29:26 +03:00
# include <LibJS/Runtime/AbstractOperations.h>
2021-09-17 21:02:51 +02:00
# include <LibJS/Runtime/Completion.h>
2022-05-06 20:57:55 +02:00
# include <LibJS/Runtime/Date.h>
2021-07-19 00:29:26 +03:00
# include <LibJS/Runtime/GlobalObject.h>
2021-07-11 21:04:11 +03:00
# include <LibJS/Runtime/Temporal/Calendar.h>
2022-03-09 22:59:17 +01:00
# include <LibJS/Runtime/Temporal/Duration.h>
2021-08-01 17:59:20 +01:00
# include <LibJS/Runtime/Temporal/Instant.h>
2021-07-11 21:04:11 +03:00
# include <LibJS/Runtime/Temporal/PlainDate.h>
2021-07-19 00:29:26 +03:00
# include <LibJS/Runtime/Temporal/PlainDateConstructor.h>
# include <LibJS/Runtime/Temporal/PlainDateTime.h>
2021-07-27 00:21:16 +01:00
# include <LibJS/Runtime/Temporal/PlainYearMonth.h>
2021-08-01 17:59:20 +01:00
# include <LibJS/Runtime/Temporal/TimeZone.h>
# include <LibJS/Runtime/Temporal/ZonedDateTime.h>
2021-07-11 21:04:11 +03:00
namespace JS : : Temporal {
2021-07-19 00:29:26 +03:00
// 3 Temporal.PlainDate Objects, https://tc39.es/proposal-temporal/#sec-temporal-plaindate-objects
2021-07-26 16:48:47 +03:00
PlainDate : : PlainDate ( i32 year , u8 month , u8 day , Object & calendar , Object & prototype )
2022-12-14 12:17:58 +01:00
: Object ( ConstructWithPrototypeTag : : Tag , prototype )
2021-07-19 00:29:26 +03:00
, m_iso_year ( year )
, m_iso_month ( month )
, m_iso_day ( day )
, m_calendar ( calendar )
{
}
void PlainDate : : visit_edges ( Visitor & visitor )
{
2021-08-14 20:09:26 +02:00
Base : : visit_edges ( visitor ) ;
2021-07-19 00:29:26 +03:00
visitor . visit ( & m_calendar ) ;
}
2022-05-16 20:46:46 +01:00
// 3.5.2 CreateISODateRecord ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-create-iso-date-record
ISODateRecord create_iso_date_record ( i32 year , u8 month , u8 day )
{
// 1. Assert: IsValidISODate(year, month, day) is true.
VERIFY ( is_valid_iso_date ( year , month , day ) ) ;
// 2. Return the Record { [[Year]]: year, [[Month]]: month, [[Day]]: day }.
return { . year = year , . month = month , . day = day } ;
}
2021-07-19 00:29:26 +03:00
// 3.5.1 CreateTemporalDate ( isoYear, isoMonth, isoDay, calendar [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporaldate
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < PlainDate * > create_temporal_date ( VM & vm , i32 iso_year , u8 iso_month , u8 iso_day , Object & calendar , FunctionObject const * new_target )
2021-07-19 00:29:26 +03:00
{
2022-08-20 08:52:42 +01:00
auto & realm = * vm . current_realm ( ) ;
2021-07-19 00:29:26 +03:00
// 1. Assert: isoYear is an integer.
// 2. Assert: isoMonth is an integer.
// 3. Assert: isoDay is an integer.
// 4. Assert: Type(calendar) is Object.
2022-04-29 19:03:57 +02:00
// 5. If IsValidISODate(isoYear, isoMonth, isoDay) is false, throw a RangeError exception.
2021-09-17 21:02:51 +02:00
if ( ! is_valid_iso_date ( iso_year , iso_month , iso_day ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidPlainDate ) ;
2021-07-19 00:29:26 +03:00
2022-04-29 19:03:57 +02:00
// 6. If ISODateTimeWithinLimits(isoYear, 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 ( 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 : : TemporalInvalidPlainDate ) ;
2021-07-19 00:29:26 +03:00
2022-03-10 17:04:46 +01:00
// 7. If newTarget is not present, set newTarget to %Temporal.PlainDate%.
2021-07-19 00:29:26 +03:00
if ( ! new_target )
2022-08-27 00:54:55 +01:00
new_target = realm . intrinsics ( ) . temporal_plain_date_constructor ( ) ;
2021-07-19 00:29:26 +03:00
// 8. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainDate.prototype%", « [[InitializedTemporalDate]], [[ISOYear]], [[ISOMonth]], [[ISODay]], [[Calendar]] »).
// 9. Set object.[[ISOYear]] to isoYear.
// 10. Set object.[[ISOMonth]] to isoMonth.
// 11. Set object.[[ISODay]] to isoDay.
// 12. Set object.[[Calendar]] to calendar.
2022-12-14 18:34:32 +00:00
auto object = TRY ( ordinary_create_from_constructor < PlainDate > ( vm , * new_target , & Intrinsics : : temporal_plain_date_prototype , iso_year , iso_month , iso_day , calendar ) ) ;
2021-07-19 00:29:26 +03:00
2022-12-14 18:34:32 +00:00
return object . ptr ( ) ;
2021-07-19 00:29:26 +03:00
}
2021-07-21 22:20:57 +03:00
// 3.5.2 ToTemporalDate ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaldate
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < PlainDate * > to_temporal_date ( VM & vm , Value item , Object const * options )
2021-07-21 22:20:57 +03: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-07-21 22:20:57 +03:00
// 3. If Type(item) is Object, then
if ( item . is_object ( ) ) {
auto & item_object = item . as_object ( ) ;
// a. If item has an [[InitializedTemporalDate]] internal slot, then
if ( is < PlainDate > ( item_object ) ) {
// i. Return item.
return static_cast < PlainDate * > ( & item_object ) ;
}
// b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
2021-08-01 17:59:20 +01:00
if ( is < ZonedDateTime > ( item_object ) ) {
auto & zoned_date_time = static_cast < ZonedDateTime & > ( item_object ) ;
2022-07-05 19:25:01 +02:00
// i. Perform ? ToTemporalOverflow(options).
2022-08-20 08:52:42 +01:00
( void ) TRY ( to_temporal_overflow ( vm , options ) ) ;
2022-07-05 19:25:01 +02:00
// ii. Let instant be ! CreateTemporalInstant(item.[[Nanoseconds]]).
2022-08-20 08:52:42 +01:00
auto * instant = create_temporal_instant ( vm , zoned_date_time . nanoseconds ( ) ) . release_value ( ) ;
2021-08-01 17:59:20 +01:00
2022-07-05 19:25:01 +02:00
// iii. Let plainDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(item.[[TimeZone]], instant, item.[[Calendar]]).
2022-08-20 08:52:42 +01:00
auto * plain_date_time = TRY ( builtin_time_zone_get_plain_date_time_for ( vm , & zoned_date_time . time_zone ( ) , * instant , zoned_date_time . calendar ( ) ) ) ;
2021-08-01 17:59:20 +01:00
2022-07-05 19:25:01 +02:00
// iv. Return ! CreateTemporalDate(plainDateTime.[[ISOYear]], plainDateTime.[[ISOMonth]], plainDateTime.[[ISODay]], plainDateTime.[[Calendar]]).
2022-08-20 08:52:42 +01:00
return create_temporal_date ( vm , plain_date_time - > iso_year ( ) , plain_date_time - > iso_month ( ) , plain_date_time - > iso_day ( ) , plain_date_time - > calendar ( ) ) ;
2021-08-01 17:59:20 +01:00
}
2021-07-21 22:20:57 +03:00
// c. If item has an [[InitializedTemporalDateTime]] internal slot, then
2021-07-23 17:18:06 +03:00
if ( is < PlainDateTime > ( item_object ) ) {
auto & date_time_item = static_cast < PlainDateTime & > ( item_object ) ;
2022-07-05 19:25:01 +02:00
// i. Perform ? ToTemporalOverflow(options).
2022-08-20 08:52:42 +01:00
( void ) TRY ( to_temporal_overflow ( vm , options ) ) ;
2022-07-05 19:25:01 +02:00
// ii. Return ! CreateTemporalDate(item.[[ISOYear]], item.[[ISOMonth]], item.[[ISODay]], item.[[Calendar]]).
2022-08-20 08:52:42 +01:00
return create_temporal_date ( vm , date_time_item . iso_year ( ) , date_time_item . iso_month ( ) , date_time_item . iso_day ( ) , date_time_item . calendar ( ) ) ;
2021-07-23 17:18:06 +03:00
}
2021-07-21 22:20:57 +03:00
// d. 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-07-21 22:20:57 +03:00
// e. 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-07-21 22:20:57 +03:00
// f. 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-07-21 22:20:57 +03:00
2022-04-29 18:34:16 +02:00
// g. Return ? CalendarDateFromFields(calendar, fields, options).
2022-08-20 08:52:42 +01:00
return calendar_date_from_fields ( vm , * calendar , * fields , options ) ;
2021-07-21 22:20:57 +03:00
}
// 4. Perform ? ToTemporalOverflow(options).
2022-08-20 08:52:42 +01:00
( void ) TRY ( to_temporal_overflow ( vm , options ) ) ;
2021-07-21 22:20:57 +03:00
// 5. Let string be ? ToString(item).
2023-01-26 14:22:47 +00:00
auto string = TRY ( item . to_string ( vm ) ) ;
2021-07-21 22:20:57 +03:00
// 6. Let result be ? ParseTemporalDateString(string).
2022-08-20 08:52:42 +01:00
auto result = TRY ( parse_temporal_date_string ( vm , string ) ) ;
2021-07-21 22:20:57 +03:00
2022-04-29 19:03:57 +02:00
// 7. Assert: IsValidISODate(result.[[Year]], result.[[Month]], result.[[Day]]) is true.
2021-09-16 17:51:37 +01:00
VERIFY ( is_valid_iso_date ( result . year , result . month , result . day ) ) ;
2021-07-21 22:20:57 +03: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 , * result . calendar ) : js_undefined ( ) ) ) ;
2021-07-21 22:20:57 +03:00
// 9. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar).
2022-08-20 08:52:42 +01:00
return create_temporal_date ( vm , result . year , result . month , result . day , * calendar ) ;
2021-07-21 22:20:57 +03:00
}
2021-10-10 22:46:10 +01:00
// 3.5.3 DifferenceISODate ( y1, m1, d1, y2, m2, d2, largestUnit ), https://tc39.es/proposal-temporal/#sec-temporal-differenceisodate
2022-08-20 08:52:42 +01:00
DateDurationRecord difference_iso_date ( VM & vm , i32 year1 , u8 month1 , u8 day1 , i32 year2 , u8 month2 , u8 day2 , StringView largest_unit )
2021-10-10 22:46:10 +01:00
{
VERIFY ( largest_unit . is_one_of ( " year " sv , " month " sv , " week " sv , " day " sv ) ) ;
2022-05-06 23:40:50 +02:00
// 1. If largestUnit is "year" or "month", then
2021-10-10 22:46:10 +01:00
if ( largest_unit . is_one_of ( " year " sv , " month " sv ) ) {
// a. Let sign be -(! CompareISODate(y1, m1, d1, y2, m2, d2)).
auto sign = - compare_iso_date ( year1 , month1 , day1 , year2 , month2 , day2 ) ;
2022-03-10 16:52:25 +01:00
// b. If sign is 0, return ! CreateDateDurationRecord(0, 0, 0, 0).
2021-10-10 22:46:10 +01:00
if ( sign = = 0 )
2022-03-10 16:52:25 +01:00
return create_date_duration_record ( 0 , 0 , 0 , 0 ) ;
2021-10-10 22:46:10 +01:00
// c. Let start be the Record { [[Year]]: y1, [[Month]]: m1, [[Day]]: d1 }.
2022-05-16 20:46:46 +01:00
auto start = ISODateRecord { . year = year1 , . month = month1 , . day = day1 } ;
2021-10-10 22:46:10 +01:00
// d. Let end be the Record { [[Year]]: y2, [[Month]]: m2, [[Day]]: d2 }.
2022-05-16 20:46:46 +01:00
auto end = ISODateRecord { . year = year2 , . month = month2 , . day = day2 } ;
2021-10-10 22:46:10 +01:00
2022-04-29 21:09:10 +02:00
// e. Let years be end.[[Year]] - start.[[Year]].
2021-10-10 22:46:10 +01:00
double years = end . year - start . year ;
// f. Let mid be ! AddISODate(y1, m1, d1, years, 0, 0, 0, "constrain").
2022-08-20 08:52:42 +01:00
auto mid = MUST ( add_iso_date ( vm , year1 , month1 , day1 , years , 0 , 0 , 0 , " constrain " sv ) ) ;
2021-10-10 22:46:10 +01:00
// g. Let midSign be -(! CompareISODate(mid.[[Year]], mid.[[Month]], mid.[[Day]], y2, m2, d2)).
auto mid_sign = - compare_iso_date ( mid . year , mid . month , mid . day , year2 , month2 , day2 ) ;
// h. If midSign is 0, then
if ( mid_sign = = 0 ) {
2022-03-10 16:52:25 +01:00
// i. If largestUnit is "year", return ! CreateDateDurationRecord(years, 0, 0, 0).
2021-10-10 22:46:10 +01:00
if ( largest_unit = = " year " sv )
2022-03-10 16:52:25 +01:00
return create_date_duration_record ( years , 0 , 0 , 0 ) ;
2021-10-10 22:46:10 +01:00
2022-03-10 16:52:25 +01:00
// ii. Return ! CreateDateDurationRecord(0, years × 12, 0, 0).
return create_date_duration_record ( 0 , years * 12 , 0 , 0 ) ;
2021-10-10 22:46:10 +01:00
}
2022-04-29 21:09:10 +02:00
// i. Let months be end.[[Month]] - start.[[Month]].
2021-10-10 22:46:10 +01:00
double months = end . month - start . month ;
// j. If midSign is not equal to sign, then
if ( mid_sign ! = sign ) {
// i. Set years to years - sign.
years - = sign ;
// ii. Set months to months + sign × 12.
months + = sign * 12 ;
}
// k. Set mid to ! AddISODate(y1, m1, d1, years, months, 0, 0, "constrain").
2022-08-20 08:52:42 +01:00
mid = MUST ( add_iso_date ( vm , year1 , month1 , day1 , years , months , 0 , 0 , " constrain " sv ) ) ;
2021-10-10 22:46:10 +01:00
// l. Set midSign to -(! CompareISODate(mid.[[Year]], mid.[[Month]], mid.[[Day]], y2, m2, d2)).
mid_sign = - compare_iso_date ( mid . year , mid . month , mid . day , year2 , month2 , day2 ) ;
// m. If midSign is 0, then
if ( mid_sign = = 0 ) {
2022-03-10 16:52:25 +01:00
// i. If largestUnit is "year", return ! CreateDateDurationRecord(years, months, 0, 0).
2021-10-10 22:46:10 +01:00
if ( largest_unit = = " year " sv )
2022-03-10 16:52:25 +01:00
return create_date_duration_record ( years , months , 0 , 0 ) ;
2021-10-10 22:46:10 +01:00
2022-03-10 16:52:25 +01:00
// ii. Return ! CreateDateDurationRecord(0, months + years × 12, 0, 0).
return create_date_duration_record ( 0 , months + years * 12 , 0 , 0 ) ;
2021-10-10 22:46:10 +01:00
}
// n. If midSign is not equal to sign, then
if ( mid_sign ! = sign ) {
// i. Set months to months - sign.
months - = sign ;
// ii. If months is equal to -sign, then
if ( months = = - sign ) {
// 1. Set years to years - sign.
years - = sign ;
// 2. Set months to 11 × sign.
months = 11 * sign ;
}
// iii. Set mid to ! AddISODate(y1, m1, d1, years, months, 0, 0, "constrain").
2022-08-20 08:52:42 +01:00
mid = MUST ( add_iso_date ( vm , year1 , month1 , day1 , years , months , 0 , 0 , " constrain " sv ) ) ;
2021-10-10 22:46:10 +01:00
}
2022-06-15 00:00:25 +01:00
double days ;
2021-10-10 22:46:10 +01:00
2022-06-15 00:00:25 +01:00
// o. If mid.[[Month]] = end.[[Month]], then
2021-10-10 22:46:10 +01:00
if ( mid . month = = end . month ) {
// i. Assert: mid.[[Year]] = end.[[Year]].
VERIFY ( mid . year = = end . year ) ;
2022-06-15 00:00:25 +01:00
// ii. Let days be end.[[Day]] - mid.[[Day]].
2021-10-10 22:46:10 +01:00
days = end . day - mid . day ;
}
2022-06-15 00:00:25 +01:00
// p. Else if sign < 0, let days be -mid.[[Day]] - (! ISODaysInMonth(end.[[Year]], end.[[Month]]) - end.[[Day]]).
2021-10-10 22:46:10 +01:00
else if ( sign < 0 ) {
days = - mid . day - ( iso_days_in_month ( end . year , end . month ) - end . day ) ;
}
2022-06-15 00:00:25 +01:00
// q. Else, let days be end.[[Day]] + (! ISODaysInMonth(mid.[[Year]], mid.[[Month]]) - mid.[[Day]]).
2021-10-10 22:46:10 +01:00
else {
days = end . day + ( iso_days_in_month ( mid . year , mid . month ) - mid . day ) ;
}
2022-06-15 00:00:25 +01:00
// r. If largestUnit is "month", then
2021-10-10 22:46:10 +01:00
if ( largest_unit = = " month " sv ) {
// i. Set months to months + years × 12.
months + = years * 12 ;
// ii. Set years to 0.
years = 0 ;
}
2022-06-15 00:00:25 +01:00
// s. Return ! CreateDateDurationRecord(years, months, 0, days).
2022-03-10 16:52:25 +01:00
return create_date_duration_record ( years , months , 0 , days ) ;
2021-10-10 22:46:10 +01:00
}
2022-06-14 23:40:58 +01:00
// 2. Else,
2021-10-10 22:46:10 +01:00
else {
2022-06-14 23:40:58 +01:00
// a. Assert: largestUnit is "day" or "week".
VERIFY ( largest_unit . is_one_of ( " day " sv , " week " sv ) ) ;
// b. Let epochDays1 be MakeDay(𝔽 (y1), 𝔽 (m1 - 1), 𝔽 (d1)).
2022-05-06 23:40:50 +02:00
auto epoch_days_1 = make_day ( year1 , month1 - 1 , day1 ) ;
2021-10-10 22:46:10 +01:00
2022-06-14 23:40:58 +01:00
// c. Assert: epochDays1 is finite.
2022-05-06 23:40:50 +02:00
VERIFY ( isfinite ( epoch_days_1 ) ) ;
2021-10-10 22:46:10 +01:00
2022-06-14 23:40:58 +01:00
// d. Let epochDays2 be MakeDay(𝔽 (y2), 𝔽 (m2 - 1), 𝔽 (d2)).
2022-05-06 23:40:50 +02:00
auto epoch_days_2 = make_day ( year2 , month2 - 1 , day2 ) ;
2021-10-10 22:46:10 +01:00
2022-06-14 23:40:58 +01:00
// e. Assert: epochDays2 is finite.
2022-05-06 23:40:50 +02:00
VERIFY ( isfinite ( epoch_days_2 ) ) ;
2021-10-10 22:46:10 +01:00
2022-06-14 23:40:58 +01:00
// f. Let days be ℝ (epochDays2) - ℝ (epochDays1).
2022-05-06 23:40:50 +02:00
auto days = epoch_days_2 - epoch_days_1 ;
2021-10-10 22:46:10 +01:00
2022-06-14 23:40:58 +01:00
// g. Let weeks be 0.
2021-10-10 22:46:10 +01:00
double weeks = 0 ;
2022-06-14 23:40:58 +01:00
// h. If largestUnit is "week", then
2021-10-10 22:46:10 +01:00
if ( largest_unit = = " week " sv ) {
2022-10-14 09:29:16 -04:00
// i. Set weeks to truncate(days / 7).
2022-05-06 23:40:50 +02:00
weeks = trunc ( days / 7 ) ;
2021-10-10 22:46:10 +01:00
2022-05-06 23:40:50 +02:00
// ii. Set days to remainder(days, 7).
2021-10-10 22:46:10 +01:00
days = fmod ( days , 7 ) ;
}
2022-06-14 23:40:58 +01:00
// i. Return ! CreateDateDurationRecord(0, 0, weeks, days).
2022-05-06 23:40:50 +02:00
return create_date_duration_record ( 0 , 0 , weeks , days ) ;
2021-10-10 22:46:10 +01:00
}
}
2021-07-21 22:17:40 +03:00
// 3.5.4 RegulateISODate ( year, month, day, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-regulateisodate
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < ISODateRecord > regulate_iso_date ( VM & vm , double year , double month , double day , StringView overflow )
2021-07-21 22:17:40 +03:00
{
VERIFY ( year = = trunc ( year ) & & month = = trunc ( month ) & & day = = trunc ( day ) ) ;
2022-06-14 23:50:24 +01:00
// 1. If overflow is "constrain", then
if ( overflow = = " constrain " sv ) {
2021-07-21 22:17:40 +03:00
// IMPLEMENTATION DEFINED: This is an optimization that allows us to treat this double as normal integer from this point onwards. This
2021-09-07 12:56:50 +02:00
// does not change the exposed behavior as the parent's call to CreateTemporalDate will immediately check that this value is a valid
2021-07-21 22:17:40 +03:00
// ISO value for years: -273975 - 273975, which is a subset of this check.
2021-09-17 21:02:51 +02:00
if ( ! AK : : is_within_range < i32 > ( year ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidPlainDate ) ;
2021-09-17 21:02:51 +02:00
2022-06-14 23:50:24 +01:00
// a. Set month to the result of clamping month between 1 and 12.
2022-03-16 19:10:57 +00:00
month = clamp ( month , 1 , 12 ) ;
2021-07-21 22:17:40 +03:00
2022-06-14 23:50:24 +01:00
// b. Let daysInMonth be ! ISODaysInMonth(year, month).
2022-05-16 20:46:46 +01:00
auto days_in_month = iso_days_in_month ( static_cast < i32 > ( year ) , static_cast < u8 > ( month ) ) ;
2022-03-16 19:10:57 +00:00
2022-06-14 23:50:24 +01:00
// c. Set day to the result of clamping day between 1 and daysInMonth.
2022-03-16 19:10:57 +00:00
day = clamp ( day , 1 , days_in_month ) ;
2022-06-14 23:50:24 +01:00
// d. Return CreateISODateRecord(year, month, day).
2022-05-16 20:46:46 +01:00
return create_iso_date_record ( static_cast < i32 > ( year ) , static_cast < u8 > ( month ) , static_cast < u8 > ( day ) ) ;
2021-07-21 22:17:40 +03:00
}
2022-06-14 23:50:24 +01:00
// 2. Else,
else {
// a. Assert: overflow is "reject".
VERIFY ( overflow = = " reject " sv ) ;
// IMPLEMENTATION DEFINED: This is an optimization that allows us to treat these doubles as normal integers from this point onwards.
// This does not change the exposed behavior as the call to IsValidISODate will immediately check that these values are valid ISO
// values (for years: -273975 - 273975, for months: 1 - 12, for days: 1 - 31) all of which are subsets of this check.
if ( ! AK : : is_within_range < i32 > ( year ) | | ! AK : : is_within_range < u8 > ( month ) | | ! AK : : is_within_range < u8 > ( day ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidPlainDate ) ;
2022-06-14 23:50:24 +01:00
auto y = static_cast < i32 > ( year ) ;
auto m = static_cast < u8 > ( month ) ;
auto d = static_cast < u8 > ( day ) ;
// b. If IsValidISODate(year, month, day) is false, throw a RangeError exception.
if ( ! is_valid_iso_date ( y , m , d ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : TemporalInvalidPlainDate ) ;
2022-06-14 23:50:24 +01:00
// c. Return the Record { [[Year]]: year, [[Month]]: month, [[Day]]: day }.
return ISODateRecord { . year = y , . month = m , . day = d } ;
}
2021-07-21 22:17:40 +03:00
}
2021-07-11 21:04:11 +03:00
// 3.5.5 IsValidISODate ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidisodate
2021-07-26 16:48:47 +03:00
bool is_valid_iso_date ( i32 year , u8 month , u8 day )
2021-07-11 21:04:11 +03:00
{
2022-05-06 19:34:59 +02:00
// 1. If month < 1 or month > 12, then
2021-07-11 21:04:11 +03:00
if ( month < 1 | | month > 12 ) {
// a. Return false.
return false ;
}
2022-05-06 19:34:59 +02:00
// 2. Let daysInMonth be ! ISODaysInMonth(year, month).
2021-07-11 21:04:11 +03:00
auto days_in_month = iso_days_in_month ( year , month ) ;
2022-05-06 19:34:59 +02:00
// 3. If day < 1 or day > daysInMonth, then
2021-07-11 21:04:11 +03:00
if ( day < 1 | | day > days_in_month ) {
// a. Return false.
return false ;
}
2022-05-06 19:34:59 +02:00
// 4. Return true.
2021-07-11 21:04:11 +03:00
return true ;
}
2021-07-27 00:21:16 +01:00
// 3.5.6 BalanceISODate ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-balanceisodate
2022-05-16 20:46:46 +01:00
ISODateRecord balance_iso_date ( double year , double month , double day )
2021-07-27 00:21:16 +01:00
{
2022-05-06 20:57:55 +02:00
// 1. Let epochDays be MakeDay(𝔽 (year), 𝔽 (month - 1), 𝔽 (day)).
auto epoch_days = make_day ( year , month - 1 , day ) ;
2021-07-27 00:21:16 +01:00
2022-05-06 20:57:55 +02:00
// 2. Assert: epochDays is finite.
VERIFY ( isfinite ( epoch_days ) ) ;
2021-07-27 00:21:16 +01:00
2022-05-06 20:57:55 +02:00
// 3. Let ms be MakeDate(epochDays, +0𝔽 ).
auto ms = make_date ( epoch_days , 0 ) ;
2021-07-27 00:21:16 +01:00
2022-05-16 20:46:46 +01:00
// 4. Return CreateISODateRecord(ℝ (YearFromTime(ms)), ℝ (MonthFromTime(ms)) + 1, ℝ (DateFromTime(ms))).
return create_iso_date_record ( year_from_time ( ms ) , static_cast < u8 > ( month_from_time ( ms ) + 1 ) , date_from_time ( ms ) ) ;
2021-07-27 00:21:16 +01:00
}
2021-08-18 21:15:04 +01:00
// 3.5.7 PadISOYear ( y ), https://tc39.es/proposal-temporal/#sec-temporal-padisoyear
2023-01-26 15:54:09 +00:00
ThrowCompletionOr < String > pad_iso_year ( VM & vm , i32 y )
2021-08-18 21:15:04 +01:00
{
// 1. Assert: y is an integer.
2022-03-31 00:54:27 +01:00
// 2. If y ≥ 0 and y ≤ 9999, then
if ( y > = 0 & & y < = 9999 ) {
2022-04-12 22:20:33 +01:00
// a. Return ToZeroPaddedDecimalString(y, 4).
2023-01-26 15:54:09 +00:00
return TRY_OR_THROW_OOM ( vm , String : : formatted ( " {:04} " , y ) ) ;
2021-08-18 21:15:04 +01:00
}
2022-03-31 00:54:27 +01:00
// 3. If y > 0, let yearSign be "+"; otherwise, let yearSign be "-".
auto year_sign = y > 0 ? ' + ' : ' - ' ;
2021-08-18 21:15:04 +01:00
2022-04-12 22:20:33 +01:00
// 4. Let year be ToZeroPaddedDecimalString(abs(y), 6).
2021-08-18 21:15:04 +01:00
// 5. Return the string-concatenation of yearSign and year.
2023-01-26 15:54:09 +00:00
return TRY_OR_THROW_OOM ( vm , String : : formatted ( " {}{:06} " , year_sign , abs ( y ) ) ) ;
2021-08-18 21:15:04 +01:00
}
// 3.5.8 TemporalDateToString ( temporalDate, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-temporaldatetostring
2023-01-26 15:56:26 +00:00
ThrowCompletionOr < String > temporal_date_to_string ( VM & vm , PlainDate & temporal_date , StringView show_calendar )
2021-08-18 21:15:04 +01:00
{
// 1. Assert: Type(temporalDate) is Object.
// 2. Assert: temporalDate has an [[InitializedTemporalDate]] internal slot.
// 3. Let year be ! PadISOYear(temporalDate.[[ISOYear]]).
2023-01-26 15:54:09 +00:00
auto year = MUST_OR_THROW_OOM ( pad_iso_year ( vm , temporal_date . iso_year ( ) ) ) ;
2021-08-18 21:15:04 +01:00
2022-04-12 22:20:33 +01:00
// 4. Let month be ToZeroPaddedDecimalString(monthDay.[[ISOMonth]], 2).
2023-01-26 15:56:26 +00:00
auto month = TRY_OR_THROW_OOM ( vm , String : : formatted ( " {:02} " , temporal_date . iso_month ( ) ) ) ;
2021-08-18 21:15:04 +01:00
2022-04-12 22:20:33 +01:00
// 5. Let day be ToZeroPaddedDecimalString(monthDay.[[ISODay]], 2).
2023-01-26 15:56:26 +00:00
auto day = TRY_OR_THROW_OOM ( vm , String : : formatted ( " {:02} " , temporal_date . iso_day ( ) ) ) ;
2021-08-18 21:15:04 +01:00
2022-08-08 14:48:48 +01:00
// 6. Let calendar be ? MaybeFormatCalendarAnnotation(temporalDate.[[Calendar]], showCalendar).
auto calendar = TRY ( maybe_format_calendar_annotation ( vm , & temporal_date . calendar ( ) , show_calendar ) ) ;
2021-08-18 21:15:04 +01:00
2022-08-08 14:48:48 +01:00
// 7. Return the string-concatenation of year, the code unit 0x002D (HYPHEN-MINUS), month, the code unit 0x002D (HYPHEN-MINUS), day, and calendar.
2023-01-26 15:56:26 +00:00
return TRY_OR_THROW_OOM ( vm , String : : formatted ( " {}-{}-{}{} " , year , month , day , calendar ) ) ;
2021-08-18 21:15:04 +01:00
}
2021-08-30 20:44:05 +01:00
// 3.5.9 AddISODate ( year, month, day, years, months, weeks, days, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-addisodate
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < ISODateRecord > add_iso_date ( VM & vm , i32 year , u8 month , u8 day , double years , double months , double weeks , double days , StringView overflow )
2021-08-30 20:44:05 +01:00
{
// 1. Assert: year, month, day, years, months, weeks, and days are integers.
VERIFY ( years = = trunc ( years ) & & months = = trunc ( months ) & & weeks = = trunc ( weeks ) & & days = = trunc ( days ) ) ;
// 2. Assert: overflow is either "constrain" or "reject".
VERIFY ( overflow = = " constrain " sv | | overflow = = " reject " sv ) ;
// 3. Let intermediate be ! BalanceISOYearMonth(year + years, month + months).
auto intermediate_year_month = balance_iso_year_month ( year + years , month + months ) ;
// 4. Let intermediate be ? RegulateISODate(intermediate.[[Year]], intermediate.[[Month]], day, overflow).
2022-08-20 08:52:42 +01:00
auto intermediate = TRY ( regulate_iso_date ( vm , intermediate_year_month . year , intermediate_year_month . month , day , overflow ) ) ;
2021-08-30 20:44:05 +01:00
// 5. Set days to days + 7 × weeks.
days + = 7 * weeks ;
// 6. Let d be intermediate.[[Day]] + days.
2022-06-14 21:44:33 +01:00
auto d = intermediate . day + days ;
2021-08-30 20:44:05 +01:00
2022-06-14 21:44:33 +01:00
// 7. Return BalanceISODate(intermediate.[[Year]], intermediate.[[Month]], d).
return balance_iso_date ( intermediate . year , intermediate . month , d ) ;
2021-08-30 20:44:05 +01:00
}
2021-07-26 17:00:42 +03:00
// 3.5.10 CompareISODate ( y1, m1, d1, y2, m2, d2 ), https://tc39.es/proposal-temporal/#sec-temporal-compareisodate
i8 compare_iso_date ( i32 year1 , u8 month1 , u8 day1 , i32 year2 , u8 month2 , u8 day2 )
{
// 1. Assert: y1, m1, d1, y2, m2, and d2 are integers.
// 2. If y1 > y2, return 1.
if ( year1 > year2 )
return 1 ;
// 3. If y1 < y2, return -1.
if ( year1 < year2 )
return - 1 ;
// 4. If m1 > m2, return 1.
if ( month1 > month2 )
return 1 ;
// 5. If m1 < m2, return -1.
if ( month1 < month2 )
return - 1 ;
// 6. If d1 > d2, return 1.
if ( day1 > day2 )
return 1 ;
// 7. If d1 < d2, return -1.
if ( day1 < day2 )
return - 1 ;
// 8. Return 0.
return 0 ;
}
2022-05-07 13:32:19 +02:00
// 3.5.11 DifferenceTemporalPlainDate ( operation, temporalDate, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplaindate
2022-08-20 08:52:42 +01:00
ThrowCompletionOr < Duration * > difference_temporal_plain_date ( VM & vm , DifferenceOperation operation , PlainDate & temporal_date , 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 ? ToTemporalDate(other).
2022-08-20 08:52:42 +01:00
auto * other = TRY ( to_temporal_date ( vm , other_value ) ) ;
2022-05-07 13:32:19 +02:00
// 3. If ? CalendarEquals(temporalDate.[[Calendar]], other.[[Calendar]]) is false, throw a RangeError exception.
2022-08-20 08:52:42 +01:00
if ( ! TRY ( calendar_equals ( vm , temporal_date . 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
// 4. Let settings be ? GetDifferenceSettings(operation, options, date, « », "day", "day").
2022-08-20 08:52:42 +01:00
auto settings = TRY ( get_difference_settings ( vm , operation , options_value , UnitGroup : : Date , { } , { " day " sv } , " day " sv ) ) ;
2022-05-07 13:32:19 +02:00
2022-06-24 00:27:29 +01:00
// 5. 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
// 6. Let result be ? CalendarDateUntil(temporalDate.[[Calendar]], temporalDate, other, untilOptions).
2022-08-20 08:52:42 +01:00
auto * duration = TRY ( calendar_date_until ( vm , temporal_date . calendar ( ) , & temporal_date , other , * until_options ) ) ;
2022-05-07 13:32:19 +02:00
auto result = DurationRecord { duration - > years ( ) , duration - > months ( ) , duration - > weeks ( ) , duration - > days ( ) , 0 , 0 , 0 , 0 , 0 , 0 } ;
2022-06-24 00:27:29 +01:00
// 7. If settings.[[SmallestUnit]] is not "day" or settings.[[RoundingIncrement]] ≠ 1, then
if ( settings . smallest_unit ! = " day " sv | | settings . rounding_increment ! = 1 ) {
// a. Set result to (? RoundDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], 0, 0, 0, 0, 0, 0, settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]], temporalDate)).[[DurationRecord]].
2022-08-20 08:52:42 +01:00
result = TRY ( round_duration ( vm , result . years , result . months , result . weeks , result . days , 0 , 0 , 0 , 0 , 0 , 0 , settings . rounding_increment , settings . smallest_unit , settings . rounding_mode , & temporal_date ) ) . duration_record ;
2022-05-07 13:32:19 +02:00
}
// 16. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × result.[[Months]], sign × result.[[Weeks]], sign × result.[[Days]], 0, 0, 0, 0, 0, 0).
2022-08-20 08:52:42 +01:00
return TRY ( create_temporal_duration ( vm , sign * result . years , sign * result . months , sign * result . weeks , sign * result . days , 0 , 0 , 0 , 0 , 0 , 0 ) ) ;
2022-05-07 13:32:19 +02:00
}
2021-07-11 21:04:11 +03:00
}