mirror of
				https://github.com/LadybirdBrowser/ladybird.git
				synced 2025-10-31 21:30:58 +00:00 
			
		
		
		
	 62793b1bd8
			
		
	
	
		62793b1bd8
		
	
	
	
	
		
			
			This is an editorial change in the ECMA-402 spec. See:
a2beb66
We implement this change by introducing a virtual interface that all
Intl "service" objects must implement. A future patch will make use of
the virtualized RelevantExtensionKeys and ResolutionOptionDescriptors
accessors, and we will need to be able to use those slots from a generic
instance type.
		
	
			
		
			
				
	
	
		
			710 lines
		
	
	
	
		
			36 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			710 lines
		
	
	
	
		
			36 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | ||
|  * Copyright (c) 2021-2025, Tim Flynn <trflynn89@ladybird.org>
 | ||
|  *
 | ||
|  * SPDX-License-Identifier: BSD-2-Clause
 | ||
|  */
 | ||
| 
 | ||
| #include <LibJS/Runtime/Array.h>
 | ||
| #include <LibJS/Runtime/Date.h>
 | ||
| #include <LibJS/Runtime/Intl/DateTimeFormat.h>
 | ||
| #include <LibJS/Runtime/NativeFunction.h>
 | ||
| #include <LibJS/Runtime/Temporal/Instant.h>
 | ||
| #include <LibJS/Runtime/Temporal/PlainDate.h>
 | ||
| #include <LibJS/Runtime/Temporal/PlainDateTime.h>
 | ||
| #include <LibJS/Runtime/Temporal/PlainMonthDay.h>
 | ||
| #include <LibJS/Runtime/Temporal/PlainTime.h>
 | ||
| #include <LibJS/Runtime/Temporal/PlainYearMonth.h>
 | ||
| #include <LibJS/Runtime/Temporal/TimeZone.h>
 | ||
| #include <LibJS/Runtime/Temporal/ZonedDateTime.h>
 | ||
| #include <math.h>
 | ||
| 
 | ||
| namespace JS::Intl {
 | ||
| 
 | ||
| GC_DEFINE_ALLOCATOR(DateTimeFormat);
 | ||
| 
 | ||
| // 11 DateTimeFormat Objects, https://tc39.es/ecma402/#datetimeformat-objects
 | ||
| DateTimeFormat::DateTimeFormat(Object& prototype)
 | ||
|     : IntlObject(ConstructWithPrototypeTag::Tag, prototype)
 | ||
| {
 | ||
| }
 | ||
| 
 | ||
| void DateTimeFormat::visit_edges(Cell::Visitor& visitor)
 | ||
| {
 | ||
|     Base::visit_edges(visitor);
 | ||
|     visitor.visit(m_bound_format);
 | ||
| }
 | ||
| 
 | ||
| // 11.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl.datetimeformat-internal-slots
 | ||
| ReadonlySpan<StringView> DateTimeFormat::relevant_extension_keys() const
 | ||
| {
 | ||
|     // The value of the [[RelevantExtensionKeys]] internal slot is « "ca", "hc", "nu" ».
 | ||
|     static constexpr AK::Array keys { "ca"sv, "hc"sv, "nu"sv };
 | ||
|     return keys;
 | ||
| }
 | ||
| 
 | ||
| // 11.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl.datetimeformat-internal-slots
 | ||
| ReadonlySpan<ResolutionOptionDescriptor> DateTimeFormat::resolution_option_descriptors(VM& vm) const
 | ||
| {
 | ||
|     // The value of the [[ResolutionOptionDescriptors]] internal slot is « { [[Key]]: "ca", [[Property]]: "calendar" }, { [[Key]]: "nu", [[Property]]: "numberingSystem" }, { [[Key]]: "hour12", [[Property]]: "hour12", [[Type]]: boolean }, { [[Key]]: "hc", [[Property]]: "hourCycle", [[Values]]: « "h11", "h12", "h23", "h24" » } ».
 | ||
|     static constexpr AK::Array hour_cycle_values { "h11"sv, "h12"sv, "h23"sv, "h24"sv };
 | ||
| 
 | ||
|     static auto descriptors = to_array<ResolutionOptionDescriptor>({
 | ||
|         { .key = "ca"sv, .property = vm.names.calendar },
 | ||
|         { .key = "nu"sv, .property = vm.names.numberingSystem },
 | ||
|         { .key = "hour12"sv, .property = vm.names.hour12, .type = OptionType::Boolean },
 | ||
|         { .key = "hc"sv, .property = vm.names.hourCycle, .values = hour_cycle_values },
 | ||
|     });
 | ||
| 
 | ||
|     return descriptors;
 | ||
| }
 | ||
| 
 | ||
| static Optional<Unicode::DateTimeFormat const&> get_or_create_formatter(StringView locale, StringView time_zone, OwnPtr<Unicode::DateTimeFormat>& formatter, Optional<Unicode::CalendarPattern> const& format)
 | ||
| {
 | ||
|     if (formatter)
 | ||
|         return *formatter;
 | ||
|     if (!format.has_value())
 | ||
|         return {};
 | ||
| 
 | ||
|     formatter = Unicode::DateTimeFormat::create_for_pattern_options(locale, time_zone, *format);
 | ||
|     return *formatter;
 | ||
| }
 | ||
| 
 | ||
| Optional<Unicode::DateTimeFormat const&> DateTimeFormat::temporal_plain_date_formatter()
 | ||
| {
 | ||
|     return get_or_create_formatter(m_icu_locale, m_temporal_time_zone, m_temporal_plain_date_formatter, m_temporal_plain_date_format);
 | ||
| }
 | ||
| 
 | ||
| Optional<Unicode::DateTimeFormat const&> DateTimeFormat::temporal_plain_year_month_formatter()
 | ||
| {
 | ||
|     return get_or_create_formatter(m_icu_locale, m_temporal_time_zone, m_temporal_plain_year_month_formatter, m_temporal_plain_year_month_format);
 | ||
| }
 | ||
| 
 | ||
| Optional<Unicode::DateTimeFormat const&> DateTimeFormat::temporal_plain_month_day_formatter()
 | ||
| {
 | ||
|     return get_or_create_formatter(m_icu_locale, m_temporal_time_zone, m_temporal_plain_month_day_formatter, m_temporal_plain_month_day_format);
 | ||
| }
 | ||
| 
 | ||
| Optional<Unicode::DateTimeFormat const&> DateTimeFormat::temporal_plain_time_formatter()
 | ||
| {
 | ||
|     return get_or_create_formatter(m_icu_locale, m_temporal_time_zone, m_temporal_plain_time_formatter, m_temporal_plain_time_format);
 | ||
| }
 | ||
| 
 | ||
| Optional<Unicode::DateTimeFormat const&> DateTimeFormat::temporal_plain_date_time_formatter()
 | ||
| {
 | ||
|     return get_or_create_formatter(m_icu_locale, m_temporal_time_zone, m_temporal_plain_date_time_formatter, m_temporal_plain_date_time_format);
 | ||
| }
 | ||
| 
 | ||
| Optional<Unicode::DateTimeFormat const&> DateTimeFormat::temporal_instant_formatter()
 | ||
| {
 | ||
|     return get_or_create_formatter(m_icu_locale, m_temporal_time_zone, m_temporal_instant_formatter, m_temporal_instant_format);
 | ||
| }
 | ||
| 
 | ||
| // 11.5.5 FormatDateTimePattern ( dateTimeFormat, patternParts, x, rangeFormatOptions ), https://tc39.es/ecma402/#sec-formatdatetimepattern
 | ||
| // 15.9.4 FormatDateTimePattern ( dateTimeFormat, format, pattern, x, epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-formatdatetimepattern
 | ||
| Vector<Unicode::DateTimeFormat::Partition> format_date_time_pattern(ValueFormat const& format_record)
 | ||
| {
 | ||
|     return format_record.formatter.format_to_parts(format_record.epoch_milliseconds);
 | ||
| }
 | ||
| 
 | ||
| // 11.5.6 PartitionDateTimePattern ( dateTimeFormat, x ), https://tc39.es/ecma402/#sec-partitiondatetimepattern
 | ||
| // 15.9.5 PartitionDateTimePattern ( dateTimeFormat, x ), https://tc39.es/proposal-temporal/#sec-partitiondatetimepattern
 | ||
| ThrowCompletionOr<Vector<Unicode::DateTimeFormat::Partition>> partition_date_time_pattern(VM& vm, DateTimeFormat& date_time_format, FormattableDateTime const& time)
 | ||
| {
 | ||
|     // 1. Let xFormatRecord be ? HandleDateTimeValue(dateTimeFormat, x).
 | ||
|     auto format_record = TRY(handle_date_time_value(vm, date_time_format, time));
 | ||
| 
 | ||
|     // 5. Let result be ? FormatDateTimePattern(dateTimeFormat, format, pattern, xFormatRecord.[[EpochNanoseconds]]).
 | ||
|     return format_date_time_pattern(format_record);
 | ||
| }
 | ||
| 
 | ||
| // 11.5.7 FormatDateTime ( dateTimeFormat, x ), https://tc39.es/ecma402/#sec-formatdatetime
 | ||
| // 15.9.6 FormatDateTime ( dateTimeFormat, x ), https://tc39.es/proposal-temporal/#sec-formatdatetime
 | ||
| ThrowCompletionOr<String> format_date_time(VM& vm, DateTimeFormat& date_time_format, FormattableDateTime const& time)
 | ||
| {
 | ||
|     // 1. Let parts be ? PartitionDateTimePattern(dateTimeFormat, x).
 | ||
|     // 2. Let result be the empty String.
 | ||
|     String result;
 | ||
| 
 | ||
|     // NOTE: We short-circuit PartitionDateTimePattern as we do not need individual partitions.
 | ||
|     {
 | ||
|         // 1. Let xFormatRecord be ? HandleDateTimeValue(dateTimeFormat, x).
 | ||
|         auto format_record = TRY(handle_date_time_value(vm, date_time_format, time));
 | ||
| 
 | ||
|         result = format_record.formatter.format(format_record.epoch_milliseconds);
 | ||
|     }
 | ||
| 
 | ||
|     // 4. Return result.
 | ||
|     return result;
 | ||
| }
 | ||
| 
 | ||
| // 11.5.8 FormatDateTimeToParts ( dateTimeFormat, x ), https://tc39.es/ecma402/#sec-formatdatetimetoparts
 | ||
| // 15.9.7 FormatDateTimeToParts ( dateTimeFormat, x ), https://tc39.es/proposal-temporal/#sec-formatdatetimetoparts
 | ||
| ThrowCompletionOr<GC::Ref<Array>> format_date_time_to_parts(VM& vm, DateTimeFormat& date_time_format, FormattableDateTime const& time)
 | ||
| {
 | ||
|     auto& realm = *vm.current_realm();
 | ||
| 
 | ||
|     // 1. Let parts be ? PartitionDateTimePattern(dateTimeFormat, x).
 | ||
|     auto parts = TRY(partition_date_time_pattern(vm, date_time_format, time));
 | ||
| 
 | ||
|     // 2. Let result be ! ArrayCreate(0).
 | ||
|     auto result = MUST(Array::create(realm, 0));
 | ||
| 
 | ||
|     // 3. Let n be 0.
 | ||
|     size_t n = 0;
 | ||
| 
 | ||
|     // 4. For each Record { [[Type]], [[Value]] } part in parts, do
 | ||
|     for (auto& part : parts) {
 | ||
|         // a. Let O be OrdinaryObjectCreate(%Object.prototype%).
 | ||
|         auto object = Object::create(realm, realm.intrinsics().object_prototype());
 | ||
| 
 | ||
|         // b. Perform ! CreateDataPropertyOrThrow(O, "type", part.[[Type]]).
 | ||
|         MUST(object->create_data_property_or_throw(vm.names.type, PrimitiveString::create(vm, part.type)));
 | ||
| 
 | ||
|         // c. Perform ! CreateDataPropertyOrThrow(O, "value", part.[[Value]]).
 | ||
|         MUST(object->create_data_property_or_throw(vm.names.value, PrimitiveString::create(vm, move(part.value))));
 | ||
| 
 | ||
|         // d. Perform ! CreateDataProperty(result, ! ToString(n), O).
 | ||
|         MUST(result->create_data_property_or_throw(n, object));
 | ||
| 
 | ||
|         // e. Increment n by 1.
 | ||
|         ++n;
 | ||
|     }
 | ||
| 
 | ||
|     // 5. Return result.
 | ||
|     return result;
 | ||
| }
 | ||
| 
 | ||
| // 11.5.9 PartitionDateTimeRangePattern ( dateTimeFormat, x, y ), https://tc39.es/ecma402/#sec-partitiondatetimerangepattern
 | ||
| // 15.9.8 PartitionDateTimeRangePattern ( dateTimeFormat, x, y ), https://tc39.es/proposal-temporal/#sec-partitiondatetimerangepattern
 | ||
| ThrowCompletionOr<Vector<Unicode::DateTimeFormat::Partition>> partition_date_time_range_pattern(VM& vm, DateTimeFormat& date_time_format, FormattableDateTime const& start, FormattableDateTime const& end)
 | ||
| {
 | ||
|     // 1. If IsTemporalObject(x) is true or IsTemporalObject(y) is true, then
 | ||
|     if (is_temporal_object(start) || is_temporal_object(end)) {
 | ||
|         // a. If SameTemporalType(x, y) is false, throw a TypeError exception.
 | ||
|         if (!same_temporal_type(start, end))
 | ||
|             return vm.throw_completion<TypeError>(ErrorType::IntlTemporalFormatRangeTypeMismatch);
 | ||
|     }
 | ||
| 
 | ||
|     // 2. Let xFormatRecord be ? HandleDateTimeValue(dateTimeFormat, x).
 | ||
|     auto start_format_record = TRY(handle_date_time_value(vm, date_time_format, start));
 | ||
| 
 | ||
|     // 3. Let yFormatRecord be ? HandleDateTimeValue(dateTimeFormat, y).
 | ||
|     auto end_format_record = TRY(handle_date_time_value(vm, date_time_format, end));
 | ||
| 
 | ||
|     return start_format_record.formatter.format_range_to_parts(start_format_record.epoch_milliseconds, end_format_record.epoch_milliseconds);
 | ||
| }
 | ||
| 
 | ||
| // 11.5.10 FormatDateTimeRange ( dateTimeFormat, x, y ), https://tc39.es/ecma402/#sec-formatdatetimerange
 | ||
| // 15.9.9 FormatDateTimeRange ( dateTimeFormat, x, y ), https://tc39.es/proposal-temporal/#sec-formatdatetimerange
 | ||
| ThrowCompletionOr<String> format_date_time_range(VM& vm, DateTimeFormat& date_time_format, FormattableDateTime const& start, FormattableDateTime const& end)
 | ||
| {
 | ||
|     // 1. Let parts be ? PartitionDateTimeRangePattern(dateTimeFormat, x, y).
 | ||
|     // 2. Let result be the empty String.
 | ||
|     String result;
 | ||
| 
 | ||
|     // NOTE: We short-circuit PartitionDateTimeRangePattern as we do not need individual partitions.
 | ||
|     {
 | ||
|         // 1. If IsTemporalObject(x) is true or IsTemporalObject(y) is true, then
 | ||
|         if (is_temporal_object(start) || is_temporal_object(end)) {
 | ||
|             // a. If SameTemporalType(x, y) is false, throw a TypeError exception.
 | ||
|             if (!same_temporal_type(start, end))
 | ||
|                 return vm.throw_completion<TypeError>(ErrorType::IntlTemporalFormatRangeTypeMismatch);
 | ||
|         }
 | ||
| 
 | ||
|         // 2. Let xFormatRecord be ? HandleDateTimeValue(dateTimeFormat, x).
 | ||
|         auto start_format_record = TRY(handle_date_time_value(vm, date_time_format, start));
 | ||
| 
 | ||
|         // 3. Let yFormatRecord be ? HandleDateTimeValue(dateTimeFormat, y).
 | ||
|         auto end_format_record = TRY(handle_date_time_value(vm, date_time_format, end));
 | ||
| 
 | ||
|         result = start_format_record.formatter.format_range(start_format_record.epoch_milliseconds, end_format_record.epoch_milliseconds);
 | ||
|     }
 | ||
| 
 | ||
|     // 4. Return result.
 | ||
|     return result;
 | ||
| }
 | ||
| 
 | ||
| // 11.5.11 FormatDateTimeRangeToParts ( dateTimeFormat, x, y ), https://tc39.es/ecma402/#sec-formatdatetimerangetoparts
 | ||
| // 15.9.10 FormatDateTimeRangeToParts ( dateTimeFormat, x, y ), https://tc39.es/proposal-temporal/#sec-formatdatetimerangetoparts
 | ||
| ThrowCompletionOr<GC::Ref<Array>> format_date_time_range_to_parts(VM& vm, DateTimeFormat& date_time_format, FormattableDateTime const& start, FormattableDateTime const& end)
 | ||
| {
 | ||
|     auto& realm = *vm.current_realm();
 | ||
| 
 | ||
|     // 1. Let parts be ? PartitionDateTimeRangePattern(dateTimeFormat, x, y).
 | ||
|     auto parts = TRY(partition_date_time_range_pattern(vm, date_time_format, start, end));
 | ||
| 
 | ||
|     // 2. Let result be ! ArrayCreate(0).
 | ||
|     auto result = MUST(Array::create(realm, 0));
 | ||
| 
 | ||
|     // 3. Let n be 0.
 | ||
|     size_t n = 0;
 | ||
| 
 | ||
|     // 4. For each Record { [[Type]], [[Value]], [[Source]] } part in parts, do
 | ||
|     for (auto& part : parts) {
 | ||
|         // a. Let O be OrdinaryObjectCreate(%ObjectPrototype%).
 | ||
|         auto object = Object::create(realm, realm.intrinsics().object_prototype());
 | ||
| 
 | ||
|         // b. Perform ! CreateDataPropertyOrThrow(O, "type", part.[[Type]]).
 | ||
|         MUST(object->create_data_property_or_throw(vm.names.type, PrimitiveString::create(vm, part.type)));
 | ||
| 
 | ||
|         // c. Perform ! CreateDataPropertyOrThrow(O, "value", part.[[Value]]).
 | ||
|         MUST(object->create_data_property_or_throw(vm.names.value, PrimitiveString::create(vm, move(part.value))));
 | ||
| 
 | ||
|         // d. Perform ! CreateDataPropertyOrThrow(O, "source", part.[[Source]]).
 | ||
|         MUST(object->create_data_property_or_throw(vm.names.source, PrimitiveString::create(vm, part.source)));
 | ||
| 
 | ||
|         // e. Perform ! CreateDataProperty(result, ! ToString(n), O).
 | ||
|         MUST(result->create_data_property_or_throw(n, object));
 | ||
| 
 | ||
|         // f. Increment n by 1.
 | ||
|         ++n;
 | ||
|     }
 | ||
| 
 | ||
|     // 5. Return result.
 | ||
|     return result;
 | ||
| }
 | ||
| 
 | ||
| // 15.9.1 GetDateTimeFormat ( formats, matcher, options, required, defaults, inherit ), https://tc39.es/proposal-temporal/#sec-getdatetimeformat
 | ||
| Optional<Unicode::CalendarPattern> get_date_time_format(Unicode::CalendarPattern const& options, OptionRequired required, OptionDefaults defaults, OptionInherit inherit)
 | ||
| {
 | ||
|     using enum Unicode::CalendarPattern::Field;
 | ||
| 
 | ||
|     auto required_options = [&]() -> ReadonlySpan<Unicode::CalendarPattern::Field> {
 | ||
|         static constexpr auto date_fields = AK::Array { Weekday, Year, Month, Day };
 | ||
|         static constexpr auto time_fields = AK::Array { DayPeriod, Hour, Minute, Second, FractionalSecondDigits };
 | ||
|         static constexpr auto year_month_fields = AK::Array { Year, Month };
 | ||
|         static constexpr auto month_day_fields = AK::Array { Month, Day };
 | ||
|         static constexpr auto any_fields = AK::Array { Weekday, Year, Month, Day, DayPeriod, Hour, Minute, Second, FractionalSecondDigits };
 | ||
| 
 | ||
|         switch (required) {
 | ||
|         // 1. If required is DATE, then
 | ||
|         case OptionRequired::Date:
 | ||
|             // a. Let requiredOptions be « "weekday", "year", "month", "day" ».
 | ||
|             return date_fields;
 | ||
|         // 2. Else if required is TIME, then
 | ||
|         case OptionRequired::Time:
 | ||
|             // a. Let requiredOptions be « "dayPeriod", "hour", "minute", "second", "fractionalSecondDigits" ».
 | ||
|             return time_fields;
 | ||
|         // 3. Else if required is YEAR-MONTH, then
 | ||
|         case OptionRequired::YearMonth:
 | ||
|             // a. Let requiredOptions be « "year", "month" ».
 | ||
|             return year_month_fields;
 | ||
|         // 4. Else if required is MONTH-DAY, then
 | ||
|         case OptionRequired::MonthDay:
 | ||
|             // a. Let requiredOptions be « "month", "day" ».
 | ||
|             return month_day_fields;
 | ||
|         // 5. Else,
 | ||
|         case OptionRequired::Any:
 | ||
|             // a. Assert: required is ANY.
 | ||
|             // b. Let requiredOptions be « "weekday", "year", "month", "day", "dayPeriod", "hour", "minute", "second", "fractionalSecondDigits" ».
 | ||
|             return any_fields;
 | ||
|         }
 | ||
|         VERIFY_NOT_REACHED();
 | ||
|     }();
 | ||
| 
 | ||
|     auto default_options = [&]() -> ReadonlySpan<Unicode::CalendarPattern::Field> {
 | ||
|         static constexpr auto date_fields = AK::Array { Year, Month, Day };
 | ||
|         static constexpr auto time_fields = AK::Array { Hour, Minute, Second };
 | ||
|         static constexpr auto year_month_fields = AK::Array { Year, Month };
 | ||
|         static constexpr auto month_day_fields = AK::Array { Month, Day };
 | ||
|         static constexpr auto all_fields = AK::Array { Year, Month, Day, Hour, Minute, Second };
 | ||
| 
 | ||
|         switch (defaults) {
 | ||
|         // 6. If defaults is DATE, then
 | ||
|         case OptionDefaults::Date:
 | ||
|             // a. Let defaultOptions be « "year", "month", "day" ».
 | ||
|             return date_fields;
 | ||
|         // 7. Else if defaults is TIME, then
 | ||
|         case OptionDefaults::Time:
 | ||
|             // a. Let defaultOptions be « "hour", "minute", "second" ».
 | ||
|             return time_fields;
 | ||
|         // 8. Else if defaults is YEAR-MONTH, then
 | ||
|         case OptionDefaults::YearMonth:
 | ||
|             // a. Let defaultOptions be « "year", "month" ».
 | ||
|             return year_month_fields;
 | ||
|         // 9. Else if defaults is MONTH-DAY, then
 | ||
|         case OptionDefaults::MonthDay:
 | ||
|             // a. Let defaultOptions be « "month", "day" ».
 | ||
|             return month_day_fields;
 | ||
|         // 10. Else,
 | ||
|         case OptionDefaults::ZonedDateTime:
 | ||
|         case OptionDefaults::All:
 | ||
|             // a. Assert: defaults is ZONED-DATE-TIME or ALL.
 | ||
|             // b. Let defaultOptions be « "year", "month", "day", "hour", "minute", "second" ».
 | ||
|             return all_fields;
 | ||
|         }
 | ||
|         VERIFY_NOT_REACHED();
 | ||
|     }();
 | ||
| 
 | ||
|     Unicode::CalendarPattern format_options {};
 | ||
| 
 | ||
|     // 11. If inherit is ALL, then
 | ||
|     if (inherit == OptionInherit::All) {
 | ||
|         // a. Let formatOptions be a copy of options.
 | ||
|         format_options = options;
 | ||
|     }
 | ||
|     // 12. Else,
 | ||
|     else {
 | ||
|         // a. Let formatOptions be a new Record.
 | ||
| 
 | ||
|         // b. If required is one of DATE, YEAR-MONTH, or ANY, then
 | ||
|         if (required == OptionRequired::Date || required == OptionRequired::YearMonth || required == OptionRequired::Any) {
 | ||
|             // i. Set formatOptions.[[era]] to options.[[era]].
 | ||
|             format_options.era = options.era;
 | ||
|         }
 | ||
| 
 | ||
|         // c. If required is TIME or ANY, then
 | ||
|         if (required == OptionRequired::Time || required == OptionRequired::Any) {
 | ||
|             // i. Set formatOptions.[[hourCycle]] to options.[[hourCycle]].
 | ||
|             format_options.hour_cycle = options.hour_cycle;
 | ||
|             format_options.hour12 = options.hour12;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     // 13. Let anyPresent be false.
 | ||
|     auto any_present = false;
 | ||
| 
 | ||
|     // 14. For each property name prop of « "weekday", "year", "month", "day", "era", "dayPeriod", "hour", "minute", "second", "fractionalSecondDigits" », do
 | ||
|     static constexpr auto all_fields = AK::Array { Weekday, Year, Month, Day, Era, DayPeriod, Hour, Minute, Second, FractionalSecondDigits };
 | ||
| 
 | ||
|     options.for_each_calendar_field_zipped_with(format_options, all_fields, [&](auto const& option, auto&) {
 | ||
|         // a. If options.[[<prop>]] is not undefined, set anyPresent to true.
 | ||
|         if (option.has_value()) {
 | ||
|             any_present = true;
 | ||
|             return IterationDecision::Break;
 | ||
|         }
 | ||
| 
 | ||
|         return IterationDecision::Continue;
 | ||
|     });
 | ||
| 
 | ||
|     // 15. Let needDefaults be true.
 | ||
|     auto need_defaults = true;
 | ||
| 
 | ||
|     // 16. For each property name prop of requiredOptions, do
 | ||
|     options.for_each_calendar_field_zipped_with(format_options, required_options, [&](auto const& option, auto& format_option) {
 | ||
|         // a. Let value be options.[[<prop>]].
 | ||
|         // b. If value is not undefined, then
 | ||
|         if (option.has_value()) {
 | ||
|             // i. Set formatOptions.[[<prop>]] to value.
 | ||
|             format_option = *option;
 | ||
| 
 | ||
|             // ii. Set needDefaults to false.
 | ||
|             need_defaults = false;
 | ||
|         }
 | ||
| 
 | ||
|         return IterationDecision::Continue;
 | ||
|     });
 | ||
| 
 | ||
|     // 17. If needDefaults is true, then
 | ||
|     if (need_defaults) {
 | ||
|         // a. If anyPresent is true and inherit is RELEVANT, return null.
 | ||
|         if (any_present && inherit == OptionInherit::Relevant)
 | ||
|             return {};
 | ||
| 
 | ||
|         // b. For each property name prop of defaultOptions, do
 | ||
|         options.for_each_calendar_field_zipped_with(format_options, default_options, [&](auto const&, auto& format_option) {
 | ||
|             using ValueType = typename RemoveCVReference<decltype(format_option)>::ValueType;
 | ||
| 
 | ||
|             if constexpr (IsSame<ValueType, Unicode::CalendarPatternStyle>) {
 | ||
|                 // i. Set formatOptions.[[<prop>]] to "numeric".
 | ||
|                 format_option = Unicode::CalendarPatternStyle::Numeric;
 | ||
|             }
 | ||
| 
 | ||
|             return IterationDecision::Continue;
 | ||
|         });
 | ||
| 
 | ||
|         // c. If defaults is ZONED-DATE-TIME and formatOptions.[[timeZoneName]] is undefined, then
 | ||
|         if (defaults == OptionDefaults::ZonedDateTime && !format_options.time_zone_name.has_value()) {
 | ||
|             // i. Set formatOptions.[[timeZoneName]] to "short".
 | ||
|             format_options.time_zone_name = Unicode::CalendarPatternStyle::Short;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     // 18. If matcher is "basic", then
 | ||
|     //     a. Let bestFormat be BasicFormatMatcher(formatOptions, formats).
 | ||
|     // 19. Else,
 | ||
|     //     a. Let bestFormat be BestFitFormatMatcher(formatOptions, formats).
 | ||
|     // 20. Return bestFormat.
 | ||
|     return format_options;
 | ||
| }
 | ||
| 
 | ||
| // 15.9.2 AdjustDateTimeStyleFormat ( formats, baseFormat, matcher, allowedOptions ), https://tc39.es/proposal-temporal/#sec-adjustdatetimestyleformat
 | ||
| Unicode::CalendarPattern adjust_date_time_style_format(Unicode::CalendarPattern const& base_format, ReadonlySpan<Unicode::CalendarPattern::Field> allowed_options)
 | ||
| {
 | ||
|     // 1. Let formatOptions be a new Record.
 | ||
|     Unicode::CalendarPattern format_options;
 | ||
| 
 | ||
|     // 2. For each field name fieldName of allowedOptions, do
 | ||
|     base_format.for_each_calendar_field_zipped_with(format_options, allowed_options, [&](auto const& base_option, auto& format_option) {
 | ||
|         // a. Set the field of formatOptions whose name is fieldName to the value of the field of baseFormat whose name is fieldName.
 | ||
|         format_option = base_option;
 | ||
|         return IterationDecision::Continue;
 | ||
|     });
 | ||
| 
 | ||
|     // 3. If matcher is "basic", then
 | ||
|     //     a. Let bestFormat be BasicFormatMatcher(formatOptions, formats).
 | ||
|     // 4. Else,
 | ||
|     //     a. Let bestFormat be BestFitFormatMatcher(formatOptions, formats).
 | ||
|     // 5. Return bestFormat.
 | ||
|     return format_options;
 | ||
| }
 | ||
| 
 | ||
| // 15.9.11 ToDateTimeFormattable ( value ), https://tc39.es/proposal-temporal/#sec-todatetimeformattable
 | ||
| ThrowCompletionOr<FormattableDateTime> to_date_time_formattable(VM& vm, Value value)
 | ||
| {
 | ||
|     // 1. If IsTemporalObject(value) is true, return value.
 | ||
|     if (value.is_object()) {
 | ||
|         auto& object = value.as_object();
 | ||
| 
 | ||
|         if (is<Temporal::Instant>(object))
 | ||
|             return FormattableDateTime { static_cast<Temporal::Instant&>(object) };
 | ||
|         if (is<Temporal::PlainDate>(object))
 | ||
|             return FormattableDateTime { static_cast<Temporal::PlainDate&>(object) };
 | ||
|         if (is<Temporal::PlainDateTime>(object))
 | ||
|             return FormattableDateTime { static_cast<Temporal::PlainDateTime&>(object) };
 | ||
|         if (is<Temporal::PlainMonthDay>(object))
 | ||
|             return FormattableDateTime { static_cast<Temporal::PlainMonthDay&>(object) };
 | ||
|         if (is<Temporal::PlainTime>(object))
 | ||
|             return FormattableDateTime { static_cast<Temporal::PlainTime&>(object) };
 | ||
|         if (is<Temporal::PlainYearMonth>(object))
 | ||
|             return FormattableDateTime { static_cast<Temporal::PlainYearMonth&>(object) };
 | ||
|         if (is<Temporal::ZonedDateTime>(object))
 | ||
|             return FormattableDateTime { static_cast<Temporal::ZonedDateTime&>(object) };
 | ||
|     }
 | ||
| 
 | ||
|     // 2. Return ? ToNumber(value).
 | ||
|     return FormattableDateTime { TRY(value.to_number(vm)).as_double() };
 | ||
| }
 | ||
| 
 | ||
| // 15.9.12 IsTemporalObject ( value ), https://tc39.es/proposal-temporal/#sec-temporal-istemporalobject
 | ||
| bool is_temporal_object(FormattableDateTime const& value)
 | ||
| {
 | ||
|     // 1. If value is not an Object, then
 | ||
|     //     a. Return false.
 | ||
|     // 2. If value does not have an [[InitializedTemporalDate]], [[InitializedTemporalTime]], [[InitializedTemporalDateTime]],
 | ||
|     //    [[InitializedTemporalZonedDateTime]], [[InitializedTemporalYearMonth]], [[InitializedTemporalMonthDay]], or
 | ||
|     //    [[InitializedTemporalInstant]] internal slot, then
 | ||
|     //     a. Return false.
 | ||
|     // 3. Return true.
 | ||
|     return !value.has<double>();
 | ||
| }
 | ||
| 
 | ||
| // 15.9.13 SameTemporalType ( x, y ), https://tc39.es/proposal-temporal/#sec-temporal-istemporalobject
 | ||
| bool same_temporal_type(FormattableDateTime const& x, FormattableDateTime const& y)
 | ||
| {
 | ||
|     // 1. If either of IsTemporalObject(x) or IsTemporalObject(y) is false, return false.
 | ||
|     if (!is_temporal_object(x) || !is_temporal_object(y))
 | ||
|         return false;
 | ||
| 
 | ||
|     // 2. If x has an [[InitializedTemporalDate]] internal slot and y does not, return false.
 | ||
|     // 3. If x has an [[InitializedTemporalTime]] internal slot and y does not, return false.
 | ||
|     // 4. If x has an [[InitializedTemporalDateTime]] internal slot and y does not, return false.
 | ||
|     // 5. If x has an [[InitializedTemporalZonedDateTime]] internal slot and y does not, return false.
 | ||
|     // 6. If x has an [[InitializedTemporalYearMonth]] internal slot and y does not, return false.
 | ||
|     // 7. If x has an [[InitializedTemporalMonthDay]] internal slot and y does not, return false.
 | ||
|     // 8. If x has an [[InitializedTemporalInstant]] internal slot and y does not, return false.
 | ||
|     // 9. Return true.
 | ||
|     return x.index() == y.index();
 | ||
| }
 | ||
| 
 | ||
| static double to_epoch_milliseconds(Crypto::SignedBigInteger const& epoch_nanoseconds)
 | ||
| {
 | ||
|     return big_floor(epoch_nanoseconds, Temporal::NANOSECONDS_PER_MILLISECOND).to_double();
 | ||
| }
 | ||
| 
 | ||
| // 15.9.15 HandleDateTimeTemporalDate ( dateTimeFormat, temporalDate ), https://tc39.es/proposal-temporal/#sec-temporal-handledatetimetemporaldate
 | ||
| ThrowCompletionOr<ValueFormat> handle_date_time_temporal_date(VM& vm, DateTimeFormat& date_time_format, Temporal::PlainDate const& temporal_date)
 | ||
| {
 | ||
|     // 1. If temporalDate.[[Calendar]] is not dateTimeFormat.[[Calendar]] or "iso8601", throw a RangeError exception.
 | ||
|     if (!temporal_date.calendar().is_one_of(date_time_format.calendar(), "iso8601"sv))
 | ||
|         return vm.throw_completion<RangeError>(ErrorType::IntlTemporalInvalidCalendar, "Temporal.PlainDate"sv, temporal_date.calendar(), date_time_format.calendar());
 | ||
| 
 | ||
|     // 2. Let isoDateTime be CombineISODateAndTimeRecord(temporalDate.[[ISODate]], NoonTimeRecord()).
 | ||
|     auto iso_date_time = Temporal::combine_iso_date_and_time_record(temporal_date.iso_date(), Temporal::noon_time_record());
 | ||
| 
 | ||
|     // 3. Let epochNs be ? GetEpochNanosecondsFor(dateTimeFormat.[[TimeZone]], isoDateTime, COMPATIBLE).
 | ||
|     auto epoch_nanoseconds = TRY(Temporal::get_epoch_nanoseconds_for(vm, date_time_format.time_zone(), iso_date_time, Temporal::Disambiguation::Compatible));
 | ||
| 
 | ||
|     // 4. Let format be dateTimeFormat.[[TemporalPlainDateFormat]].
 | ||
|     auto formatter = date_time_format.temporal_plain_date_formatter();
 | ||
| 
 | ||
|     // 5. If format is null, throw a TypeError exception.
 | ||
|     if (!formatter.has_value())
 | ||
|         return vm.throw_completion<TypeError>(ErrorType::IntlTemporalFormatIsNull, "Temporal.PlainDate"sv);
 | ||
| 
 | ||
|     // 6. Return Value Format Record { [[Format]]: format, [[EpochNanoseconds]]: epochNs  }.
 | ||
|     return ValueFormat { .formatter = *formatter, .epoch_milliseconds = to_epoch_milliseconds(epoch_nanoseconds) };
 | ||
| }
 | ||
| 
 | ||
| // 15.9.16 HandleDateTimeTemporalYearMonth ( dateTimeFormat, temporalYearMonth ), https://tc39.es/proposal-temporal/#sec-temporal-handledatetimetemporalyearmonth
 | ||
| ThrowCompletionOr<ValueFormat> handle_date_time_temporal_year_month(VM& vm, DateTimeFormat& date_time_format, Temporal::PlainYearMonth const& temporal_year_month)
 | ||
| {
 | ||
|     // 1. If temporalYearMonth.[[Calendar]] is not equal to dateTimeFormat.[[Calendar]], then
 | ||
|     if (temporal_year_month.calendar() != date_time_format.calendar()) {
 | ||
|         // a. Throw a RangeError exception.
 | ||
|         return vm.throw_completion<RangeError>(ErrorType::IntlTemporalInvalidCalendar, "Temporal.PlainYearMonth"sv, temporal_year_month.calendar(), date_time_format.calendar());
 | ||
|     }
 | ||
| 
 | ||
|     // 2. Let isoDateTime be CombineISODateAndTimeRecord(temporalYearMonth.[[ISODate]], NoonTimeRecord()).
 | ||
|     auto iso_date_time = Temporal::combine_iso_date_and_time_record(temporal_year_month.iso_date(), Temporal::noon_time_record());
 | ||
| 
 | ||
|     // 3. Let epochNs be ? GetEpochNanosecondsFor(dateTimeFormat.[[TimeZone]], isoDateTime, COMPATIBLE).
 | ||
|     auto epoch_nanoseconds = TRY(Temporal::get_epoch_nanoseconds_for(vm, date_time_format.time_zone(), iso_date_time, Temporal::Disambiguation::Compatible));
 | ||
| 
 | ||
|     // 4. Let format be dateTimeFormat.[[TemporalPlainYearMonthFormat]].
 | ||
|     auto formatter = date_time_format.temporal_plain_year_month_formatter();
 | ||
| 
 | ||
|     // 5. If format is null, throw a TypeError exception.
 | ||
|     if (!formatter.has_value())
 | ||
|         return vm.throw_completion<TypeError>(ErrorType::IntlTemporalFormatIsNull, "Temporal.PlainYearMonth"sv);
 | ||
| 
 | ||
|     // 6. Return Value Format Record { [[Format]]: format, [[EpochNanoseconds]]: epochNs  }.
 | ||
|     return ValueFormat { .formatter = *formatter, .epoch_milliseconds = to_epoch_milliseconds(epoch_nanoseconds) };
 | ||
| }
 | ||
| 
 | ||
| // 15.9.17 HandleDateTimeTemporalMonthDay ( dateTimeFormat, temporalMonthDay ), https://tc39.es/proposal-temporal/#sec-temporal-handledatetimetemporalmonthday
 | ||
| ThrowCompletionOr<ValueFormat> handle_date_time_temporal_month_day(VM& vm, DateTimeFormat& date_time_format, Temporal::PlainMonthDay const& temporal_month_day)
 | ||
| {
 | ||
|     // 1. If temporalMonthDay.[[Calendar]] is not equal to dateTimeFormat.[[Calendar]], then
 | ||
|     if (temporal_month_day.calendar() != date_time_format.calendar()) {
 | ||
|         // a. Throw a RangeError exception.
 | ||
|         return vm.throw_completion<RangeError>(ErrorType::IntlTemporalInvalidCalendar, "Temporal.PlainMonthDay"sv, temporal_month_day.calendar(), date_time_format.calendar());
 | ||
|     }
 | ||
| 
 | ||
|     // 2. Let isoDateTime be CombineISODateAndTimeRecord(temporalMonthDay.[[ISODate]], NoonTimeRecord()).
 | ||
|     auto iso_date_time = Temporal::combine_iso_date_and_time_record(temporal_month_day.iso_date(), Temporal::noon_time_record());
 | ||
| 
 | ||
|     // 3. Let epochNs be ? GetEpochNanosecondsFor(dateTimeFormat.[[TimeZone]], isoDateTime, COMPATIBLE).
 | ||
|     auto epoch_nanoseconds = TRY(Temporal::get_epoch_nanoseconds_for(vm, date_time_format.time_zone(), iso_date_time, Temporal::Disambiguation::Compatible));
 | ||
| 
 | ||
|     // 4. Let format be dateTimeFormat.[[TemporalPlainMonthDayFormat]].
 | ||
|     auto formatter = date_time_format.temporal_plain_month_day_formatter();
 | ||
| 
 | ||
|     // 5. If format is null, throw a TypeError exception.
 | ||
|     if (!formatter.has_value())
 | ||
|         return vm.throw_completion<TypeError>(ErrorType::IntlTemporalFormatIsNull, "Temporal.PlainMonthDay"sv);
 | ||
| 
 | ||
|     // 6. Return Value Format Record { [[Format]]: format, [[EpochNanoseconds]]: epochNs  }.
 | ||
|     return ValueFormat { .formatter = *formatter, .epoch_milliseconds = to_epoch_milliseconds(epoch_nanoseconds) };
 | ||
| }
 | ||
| 
 | ||
| // 15.9.18 HandleDateTimeTemporalTime ( dateTimeFormat, temporalTime ), https://tc39.es/proposal-temporal/#sec-temporal-handledatetimetemporaltime
 | ||
| ThrowCompletionOr<ValueFormat> handle_date_time_temporal_time(VM& vm, DateTimeFormat& date_time_format, Temporal::PlainTime const& temporal_time)
 | ||
| {
 | ||
|     // 1. Let isoDate be CreateISODateRecord(1970, 1, 1).
 | ||
|     auto iso_date = Temporal::create_iso_date_record(1970, 1, 1);
 | ||
| 
 | ||
|     // 2. Let isoDateTime be CombineISODateAndTimeRecord(isoDate, temporalTime.[[Time]]).
 | ||
|     auto iso_date_time = Temporal::combine_iso_date_and_time_record(iso_date, temporal_time.time());
 | ||
| 
 | ||
|     // 3. Let epochNs be ? GetEpochNanosecondsFor(dateTimeFormat.[[TimeZone]], isoDateTime, COMPATIBLE).
 | ||
|     auto epoch_nanoseconds = TRY(Temporal::get_epoch_nanoseconds_for(vm, date_time_format.time_zone(), iso_date_time, Temporal::Disambiguation::Compatible));
 | ||
| 
 | ||
|     // 4. Let format be dateTimeFormat.[[TemporalPlainTimeFormat]].
 | ||
|     auto formatter = date_time_format.temporal_plain_time_formatter();
 | ||
| 
 | ||
|     // 5. If format is null, throw a TypeError exception.
 | ||
|     if (!formatter.has_value())
 | ||
|         return vm.throw_completion<TypeError>(ErrorType::IntlTemporalFormatIsNull, "Temporal.PlainTime"sv);
 | ||
| 
 | ||
|     // 6. Return Value Format Record { [[Format]]: format, [[EpochNanoseconds]]: epochNs  }.
 | ||
|     return ValueFormat { .formatter = *formatter, .epoch_milliseconds = to_epoch_milliseconds(epoch_nanoseconds) };
 | ||
| }
 | ||
| 
 | ||
| // 15.9.19 HandleDateTimeTemporalDateTime ( dateTimeFormat, dateTime ), https://tc39.es/proposal-temporal/#sec-temporal-handledatetimetemporaldatetime
 | ||
| ThrowCompletionOr<ValueFormat> handle_date_time_temporal_date_time(VM& vm, DateTimeFormat& date_time_format, Temporal::PlainDateTime const& date_time)
 | ||
| {
 | ||
|     // 1. If dateTime.[[Calendar]] is not "iso8601" and not equal to dateTimeFormat.[[Calendar]], then
 | ||
|     if (!date_time.calendar().is_one_of(date_time_format.calendar(), "iso8601"sv)) {
 | ||
|         // a. Throw a RangeError exception.
 | ||
|         return vm.throw_completion<RangeError>(ErrorType::IntlTemporalInvalidCalendar, "Temporal.PlainDateTime"sv, date_time.calendar(), date_time_format.calendar());
 | ||
|     }
 | ||
| 
 | ||
|     // 2. Let epochNs be ? GetEpochNanosecondsFor(dateTimeFormat.[[TimeZone]], dateTime.[[ISODateTime]], COMPATIBLE).
 | ||
|     auto epoch_nanoseconds = TRY(Temporal::get_epoch_nanoseconds_for(vm, date_time_format.time_zone(), date_time.iso_date_time(), Temporal::Disambiguation::Compatible));
 | ||
| 
 | ||
|     // 3. Let format be dateTimeFormat.[[TemporalPlainDateTimeFormat]].
 | ||
|     auto formatter = date_time_format.temporal_plain_date_time_formatter();
 | ||
|     VERIFY(formatter.has_value());
 | ||
| 
 | ||
|     // 4. Return Value Format Record { [[Format]]: format, [[EpochNanoseconds]]: epochNs  }.
 | ||
|     return ValueFormat { .formatter = *formatter, .epoch_milliseconds = to_epoch_milliseconds(epoch_nanoseconds) };
 | ||
| }
 | ||
| 
 | ||
| // 15.9.20 HandleDateTimeTemporalInstant ( dateTimeFormat, instant ), https://tc39.es/proposal-temporal/#sec-temporal-handledatetimetemporalinstant
 | ||
| ValueFormat handle_date_time_temporal_instant(DateTimeFormat& date_time_format, Temporal::Instant const& instant)
 | ||
| {
 | ||
|     // 1. Let format be dateTimeFormat.[[TemporalInstantFormat]].
 | ||
|     auto formatter = date_time_format.temporal_instant_formatter();
 | ||
|     VERIFY(formatter.has_value());
 | ||
| 
 | ||
|     // 2. Return Value Format Record { [[Format]]: format, [[EpochNanoseconds]]: instant.[[EpochNanoseconds]]  }.
 | ||
|     return ValueFormat { .formatter = *formatter, .epoch_milliseconds = to_epoch_milliseconds(instant.epoch_nanoseconds()->big_integer()) };
 | ||
| }
 | ||
| 
 | ||
| // 15.9.21 HandleDateTimeOthers ( dateTimeFormat, x ), https://tc39.es/proposal-temporal/#sec-temporal-handledatetimeothers
 | ||
| ThrowCompletionOr<ValueFormat> handle_date_time_others(VM& vm, DateTimeFormat& date_time_format, double time)
 | ||
| {
 | ||
|     // 1. Set x to TimeClip(x).
 | ||
|     time = time_clip(time);
 | ||
| 
 | ||
|     // 2. If x is NaN, throw a RangeError exception.
 | ||
|     if (isnan(time))
 | ||
|         return vm.throw_completion<RangeError>(ErrorType::IntlInvalidTime);
 | ||
| 
 | ||
|     // 3. Let epochNanoseconds be ℤ(ℝ(x) × 10**6).
 | ||
| 
 | ||
|     // 4. Let format be dateTimeFormat.[[DateTimeFormat]].
 | ||
|     auto const& formatter = date_time_format.formatter();
 | ||
| 
 | ||
|     // 5. Return Value Format Record { [[Format]]: format, [[EpochNanoseconds]]: epochNanoseconds  }.
 | ||
|     return ValueFormat { .formatter = formatter, .epoch_milliseconds = time };
 | ||
| }
 | ||
| 
 | ||
| // 15.9.22 HandleDateTimeValue ( dateTimeFormat, x ), https://tc39.es/proposal-temporal/#sec-temporal-handledatetimevalue
 | ||
| ThrowCompletionOr<ValueFormat> handle_date_time_value(VM& vm, DateTimeFormat& date_time_format, FormattableDateTime const& formattable)
 | ||
| {
 | ||
|     return formattable.visit(
 | ||
|         // 1. If x is an Object, then
 | ||
|         // a. If x has an [[InitializedTemporalDate]] internal slot, then
 | ||
|         [&](GC::Ref<Temporal::PlainDate> temporal_date) {
 | ||
|             // i. Return ? HandleDateTimeTemporalDate(dateTimeFormat, x).
 | ||
|             return handle_date_time_temporal_date(vm, date_time_format, temporal_date);
 | ||
|         },
 | ||
|         // b. If x has an [[InitializedTemporalYearMonth]] internal slot, then
 | ||
|         [&](GC::Ref<Temporal::PlainYearMonth> temporal_year_month) {
 | ||
|             // i. Return ? HandleDateTimeTemporalYearMonth(dateTimeFormat, x).
 | ||
|             return handle_date_time_temporal_year_month(vm, date_time_format, temporal_year_month);
 | ||
|         },
 | ||
|         // c. If x has an [[InitializedTemporalMonthDay]] internal slot, then
 | ||
|         [&](GC::Ref<Temporal::PlainMonthDay> temporal_month_day) {
 | ||
|             // i. Return ? HandleDateTimeTemporalMonthDay(dateTimeFormat, x).
 | ||
|             return handle_date_time_temporal_month_day(vm, date_time_format, temporal_month_day);
 | ||
|         },
 | ||
|         // d. If x has an [[InitializedTemporalTime]] internal slot, then
 | ||
|         [&](GC::Ref<Temporal::PlainTime> temporal_time) {
 | ||
|             // i. Return ? HandleDateTimeTemporalTime(dateTimeFormat, x).
 | ||
|             return handle_date_time_temporal_time(vm, date_time_format, temporal_time);
 | ||
|         },
 | ||
|         // e. If x has an [[InitializedTemporalDateTime]] internal slot, then
 | ||
|         [&](GC::Ref<Temporal::PlainDateTime> date_time) {
 | ||
|             // i. Return ? HandleDateTimeTemporalDateTime(dateTimeFormat, x).
 | ||
|             return handle_date_time_temporal_date_time(vm, date_time_format, date_time);
 | ||
|         },
 | ||
|         // f. If x has an [[InitializedTemporalInstant]] internal slot, then
 | ||
|         [&](GC::Ref<Temporal::Instant> instant) -> ThrowCompletionOr<ValueFormat> {
 | ||
|             // i. Return HandleDateTimeTemporalInstant(dateTimeFormat, x).
 | ||
|             return handle_date_time_temporal_instant(date_time_format, instant);
 | ||
|         },
 | ||
|         // g. Assert: x has an [[InitializedTemporalZonedDateTime]] internal slot.
 | ||
|         [&](GC::Ref<Temporal::ZonedDateTime>) -> ThrowCompletionOr<ValueFormat> {
 | ||
|             // h. Throw a TypeError exception.
 | ||
|             return vm.throw_completion<TypeError>(ErrorType::IntlTemporalZonedDateTime);
 | ||
|         },
 | ||
|         // 2. Return ? HandleDateTimeOthers(dateTimeFormat, x).
 | ||
|         [&](double time) {
 | ||
|             return handle_date_time_others(vm, date_time_format, time);
 | ||
|         });
 | ||
| }
 | ||
| 
 | ||
| }
 |