2021-11-18 10:30:31 -05:00
/*
2025-04-07 15:56:31 -04:00
* Copyright ( c ) 2021 - 2025 , Tim Flynn < trflynn89 @ ladybird . org >
2021-11-18 10:30:31 -05:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <LibJS/Runtime/AbstractOperations.h>
2021-11-28 21:16:27 -05:00
# include <LibJS/Runtime/Array.h>
2022-10-14 10:44:31 -04:00
# include <LibJS/Runtime/Date.h>
2021-11-18 10:30:31 -05:00
# include <LibJS/Runtime/GlobalObject.h>
2021-11-28 21:16:27 -05:00
# include <LibJS/Runtime/Intl/AbstractOperations.h>
2023-07-21 22:13:44 -04:00
# include <LibJS/Runtime/Intl/DateTimeFormat.h>
2021-11-18 10:30:31 -05:00
# include <LibJS/Runtime/Intl/DateTimeFormatConstructor.h>
2024-11-20 20:06:42 -05:00
# include <LibJS/Runtime/Temporal/ISO8601.h>
2024-06-23 09:14:27 -04:00
# include <LibUnicode/DateTimeFormat.h>
# include <LibUnicode/Locale.h>
2021-11-18 10:30:31 -05:00
namespace JS : : Intl {
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( DateTimeFormatConstructor ) ;
2023-11-19 09:45:05 +01:00
2022-03-15 10:30:33 -04:00
// 11.1 The Intl.DateTimeFormat Constructor, https://tc39.es/ecma402/#sec-intl-datetimeformat-constructor
2022-08-16 00:20:49 +01:00
DateTimeFormatConstructor : : DateTimeFormatConstructor ( Realm & realm )
2023-04-13 00:47:15 +02:00
: NativeFunction ( realm . vm ( ) . names . DateTimeFormat . as_string ( ) , realm . intrinsics ( ) . function_prototype ( ) )
2021-11-18 10:30:31 -05:00
{
}
2023-08-07 08:41:28 +02:00
void DateTimeFormatConstructor : : initialize ( Realm & realm )
2021-11-18 10:30:31 -05:00
{
2023-08-07 08:41:28 +02:00
Base : : initialize ( realm ) ;
2021-11-18 10:30:31 -05:00
auto & vm = this - > vm ( ) ;
2022-03-15 10:30:33 -04:00
// 11.2.1 Intl.DateTimeFormat.prototype, https://tc39.es/ecma402/#sec-intl.datetimeformat.prototype
2022-08-27 00:54:55 +01:00
define_direct_property ( vm . names . prototype , realm . intrinsics ( ) . intl_date_time_format_prototype ( ) , 0 ) ;
2021-11-28 21:16:27 -05:00
u8 attr = Attribute : : Writable | Attribute : : Configurable ;
2022-08-22 21:47:35 +01:00
define_native_function ( realm , vm . names . supportedLocalesOf , supported_locales_of , 1 , attr ) ;
2021-11-28 21:16:27 -05:00
2021-11-18 10:30:31 -05:00
define_direct_property ( vm . names . length , Value ( 0 ) , Attribute : : Configurable ) ;
}
2022-03-15 10:30:33 -04:00
// 11.1.1 Intl.DateTimeFormat ( [ locales [ , options ] ] ), https://tc39.es/ecma402/#sec-intl.datetimeformat
2021-11-18 10:30:31 -05:00
ThrowCompletionOr < Value > DateTimeFormatConstructor : : call ( )
{
// 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget.
return TRY ( construct ( * this ) ) ;
}
2022-03-15 10:30:33 -04:00
// 11.1.1 Intl.DateTimeFormat ( [ locales [ , options ] ] ), https://tc39.es/ecma402/#sec-intl.datetimeformat
2024-11-15 04:01:23 +13:00
ThrowCompletionOr < GC : : Ref < Object > > DateTimeFormatConstructor : : construct ( FunctionObject & new_target )
2021-11-18 10:30:31 -05:00
{
2021-11-28 17:55:47 -05:00
auto & vm = this - > vm ( ) ;
2021-11-18 10:30:31 -05:00
2021-11-28 17:55:47 -05:00
auto locales = vm . argument ( 0 ) ;
auto options = vm . argument ( 1 ) ;
2024-11-27 16:09:41 -05:00
// 2. Let dateTimeFormat be ? CreateDateTimeFormat(newTarget, locales, options, ANY, DATE).
2023-07-21 21:33:55 -04:00
auto date_time_format = TRY ( create_date_time_format ( vm , new_target , locales , options , OptionRequired : : Any , OptionDefaults : : Date ) ) ;
2021-11-28 17:55:47 -05:00
2023-07-21 21:33:55 -04:00
// 3. If the implementation supports the normative optional constructor mode of 4.3 Note 1, then
2021-11-28 17:55:47 -05:00
// a. Let this be the this value.
// b. Return ? ChainDateTimeFormat(dateTimeFormat, NewTarget, this).
2023-07-21 21:33:55 -04:00
// 4. Return dateTimeFormat.
2022-12-14 19:18:10 +00:00
return date_time_format ;
2021-11-18 10:30:31 -05:00
}
2022-03-15 10:30:33 -04:00
// 11.2.2 Intl.DateTimeFormat.supportedLocalesOf ( locales [ , options ] ), https://tc39.es/ecma402/#sec-intl.datetimeformat.supportedlocalesof
2021-11-28 21:16:27 -05:00
JS_DEFINE_NATIVE_FUNCTION ( DateTimeFormatConstructor : : supported_locales_of )
{
auto locales = vm . argument ( 0 ) ;
auto options = vm . argument ( 1 ) ;
// 1. Let availableLocales be %DateTimeFormat%.[[AvailableLocales]].
// 2. Let requestedLocales be ? CanonicalizeLocaleList(locales).
2022-08-20 08:25:24 +01:00
auto requested_locales = TRY ( canonicalize_locale_list ( vm , locales ) ) ;
2021-11-28 21:16:27 -05:00
2024-06-18 10:13:30 -04:00
// 3. Return ? FilterLocales(availableLocales, requestedLocales, options).
return TRY ( filter_locales ( vm , requested_locales , options ) ) ;
2021-11-28 21:16:27 -05:00
}
2023-07-21 21:33:55 -04:00
// 11.1.2 CreateDateTimeFormat ( newTarget, locales, options, required, defaults ), https://tc39.es/ecma402/#sec-createdatetimeformat
2024-11-20 20:06:42 -05:00
// 15.7.1 CreateDateTimeFormat ( newTarget, locales, options, required, defaults [ , toLocaleStringTimeZone ] ), https://tc39.es/proposal-temporal/#sec-createdatetimeformat
2024-11-27 16:09:41 -05:00
ThrowCompletionOr < GC : : Ref < DateTimeFormat > > create_date_time_format ( VM & vm , FunctionObject & new_target , Value locales_value , Value options_value , OptionRequired required , OptionDefaults defaults , Optional < String > const & to_locale_string_time_zone )
2022-03-15 10:30:33 -04:00
{
2024-08-15 15:06:54 -04:00
// 1. Let dateTimeFormat be ? OrdinaryCreateFromConstructor(newTarget, "%Intl.DateTimeFormat.prototype%", « [[InitializedDateTimeFormat]], [[Locale]], [[Calendar]], [[NumberingSystem]], [[TimeZone]], [[HourCycle]], [[DateStyle]], [[TimeStyle]], [[DateTimeFormat]], [[BoundFormat]] »).
2023-07-21 21:33:55 -04:00
auto date_time_format = TRY ( ordinary_create_from_constructor < DateTimeFormat > ( vm , new_target , & Intrinsics : : intl_date_time_format_prototype ) ) ;
2025-04-07 16:59:06 -04:00
// 2. Let hour12 be undefined.
auto hour12 = js_undefined ( ) ;
2022-03-15 10:30:33 -04:00
2025-04-07 16:59:06 -04:00
// 3. Let modifyResolutionOptions be a new Abstract Closure with parameters (options) that captures hour12 and performs the following steps when called:
auto modify_resolution_options = [ & ] ( LocaleOptions & options ) {
// a. Set hour12 to options.[[hour12]].
hour12 = options . hour12 ;
2024-06-16 15:36:34 -04:00
2025-04-07 16:59:06 -04:00
// b. Remove field [[hour12]] from options.
options . hour12 = { } ;
2022-03-15 10:30:33 -04:00
2025-04-07 16:59:06 -04:00
// c. If hour12 is not undefined, set options.[[hc]] to null.
if ( ! hour12 . is_undefined ( ) )
options . hc = Empty { } ;
} ;
2022-03-15 10:30:33 -04:00
2025-04-07 16:59:06 -04:00
// 4. Let optionsResolution be ? ResolveOptions(%Intl.DateTimeFormat%, %Intl.DateTimeFormat%.[[LocaleData]], locales, options, « COERCE-OPTIONS », modifyResolutionOptions).
// 5. Set options to optionsResolution.[[Options]].
// 6. Let r be optionsResolution.[[ResolvedLocale]].
auto [ options , result , _ ] = TRY ( resolve_options ( vm , date_time_format , locales_value , options_value , SpecialBehaviors : : CoerceOptions , move ( modify_resolution_options ) ) ) ;
2022-03-15 10:30:33 -04:00
2025-04-07 16:59:06 -04:00
// 7. Set dateTimeFormat.[[Locale]] to r.[[Locale]].
2023-07-21 21:33:55 -04:00
date_time_format - > set_locale ( move ( result . locale ) ) ;
2025-03-17 16:24:09 -04:00
date_time_format - > set_icu_locale ( move ( result . icu_locale ) ) ;
2022-03-15 10:30:33 -04:00
2025-04-07 16:59:06 -04:00
// 8. Let resolvedCalendar be r.[[ca]].
// 9. Set dateTimeFormat.[[Calendar]] to resolvedCalendar.
2024-06-16 15:36:34 -04:00
if ( auto * resolved_calendar = result . ca . get_pointer < String > ( ) )
date_time_format - > set_calendar ( move ( * resolved_calendar ) ) ;
2022-03-15 10:30:33 -04:00
2025-04-07 16:59:06 -04:00
// 10. Set dateTimeFormat.[[NumberingSystem]] to r.[[nu]].
2024-06-16 15:36:34 -04:00
if ( auto * resolved_numbering_system = result . nu . get_pointer < String > ( ) )
date_time_format - > set_numbering_system ( move ( * resolved_numbering_system ) ) ;
2022-03-15 10:30:33 -04:00
2025-04-07 16:59:06 -04:00
// 11. Let resolvedLocaleData be r.[[LocaleData]].
2022-03-15 10:30:33 -04:00
2024-06-23 09:14:27 -04:00
Optional < Unicode : : HourCycle > hour_cycle_value ;
2024-06-12 10:47:20 -04:00
Optional < bool > hour12_value ;
2022-03-28 10:17:39 -04:00
2025-04-07 16:59:06 -04:00
// 12. If hour12 is true, then
2024-08-15 15:06:54 -04:00
// a. Let hc be resolvedLocaleData.[[hourCycle12]].
2025-04-07 16:59:06 -04:00
// 13. Else if hour12 is false, then
2024-08-15 15:06:54 -04:00
// a. Let hc be resolvedLocaleData.[[hourCycle24]].
2024-06-12 10:47:20 -04:00
if ( hour12 . is_boolean ( ) ) {
2024-06-23 09:14:27 -04:00
// NOTE: We let LibUnicode figure out the appropriate hour cycle.
2024-06-12 10:47:20 -04:00
hour12_value = hour12 . as_bool ( ) ;
2022-03-28 10:17:39 -04:00
}
2025-04-07 16:59:06 -04:00
// 14. Else,
2022-03-28 10:17:39 -04:00
else {
2022-04-06 19:19:13 -04:00
// a. Assert: hour12 is undefined.
VERIFY ( hour12 . is_undefined ( ) ) ;
// b. Let hc be r.[[hc]].
2024-06-16 15:36:34 -04:00
if ( auto * resolved_hour_cycle = result . hc . get_pointer < String > ( ) )
2024-06-23 09:14:27 -04:00
hour_cycle_value = Unicode : : hour_cycle_from_string ( * resolved_hour_cycle ) ;
2022-03-28 10:17:39 -04:00
2024-08-15 15:06:54 -04:00
// c. If hc is null, set hc to resolvedLocaleData.[[hourCycle]].
2022-04-06 19:19:13 -04:00
if ( ! hour_cycle_value . has_value ( ) )
2024-06-23 09:14:27 -04:00
hour_cycle_value = Unicode : : default_hour_cycle ( date_time_format - > locale ( ) ) ;
2022-03-28 10:17:39 -04:00
}
2025-04-07 16:59:06 -04:00
// 15. Set dateTimeFormat.[[HourCycle]] to hc.
2024-11-27 16:09:41 -05:00
// NOTE: The [[HourCycle]] is stored and accessed from [[DateTimeFormat]].
2025-04-07 16:59:06 -04:00
// 16. Let timeZone be ? Get(options, "timeZone").
2022-03-15 10:30:33 -04:00
auto time_zone_value = TRY ( options - > get ( vm . names . timeZone ) ) ;
2023-01-26 15:09:59 +00:00
String time_zone ;
2022-03-15 10:30:33 -04:00
2025-04-07 16:59:06 -04:00
// 17. If timeZone is undefined, then
2022-03-15 10:30:33 -04:00
if ( time_zone_value . is_undefined ( ) ) {
2024-11-27 16:09:41 -05:00
// a. If toLocaleStringTimeZone is present, then
if ( to_locale_string_time_zone . has_value ( ) ) {
// i. Set timeZone to toLocaleStringTimeZone.
time_zone = * to_locale_string_time_zone ;
}
// b. Else,
else {
// i. Set timeZone to SystemTimeZoneIdentifier().
time_zone = system_time_zone_identifier ( ) ;
}
2022-03-15 10:30:33 -04:00
}
2025-04-07 16:59:06 -04:00
// 18. Else,
2022-03-15 10:30:33 -04:00
else {
2024-11-27 16:09:41 -05:00
// a. If toLocaleStringTimeZone is present, throw a TypeError exception.
if ( to_locale_string_time_zone . has_value ( ) )
return vm . throw_completion < TypeError > ( ErrorType : : IntlInvalidDateTimeFormatOption , vm . names . timeZone , " a toLocaleString time zone " sv ) ;
// b. Set timeZone to ? ToString(timeZone).
2023-01-26 15:09:59 +00:00
time_zone = TRY ( time_zone_value . to_string ( vm ) ) ;
2023-10-04 15:45:59 -04:00
}
2025-04-07 16:59:06 -04:00
// 19. If IsTimeZoneOffsetString(timeZone) is true, then
2024-11-20 20:06:42 -05:00
bool is_time_zone_offset_string = JS : : is_offset_time_zone_identifier ( time_zone ) ;
2024-06-12 10:47:20 -04:00
if ( is_time_zone_offset_string ) {
2024-11-20 20:06:42 -05:00
// a. Let parseResult be ParseText(StringToCodePoints(timeZone), UTCOffset[~SubMinutePrecision]).
auto parse_result = Temporal : : parse_utc_offset ( time_zone , Temporal : : SubMinutePrecision : : No ) ;
2023-10-04 15:45:59 -04:00
// b. Assert: parseResult is a Parse Node.
VERIFY ( parse_result . has_value ( ) ) ;
2022-03-15 10:30:33 -04:00
2024-11-20 20:06:42 -05:00
// c. Let offsetNanoseconds be ? ParseDateTimeUTCOffset(timeZone).
auto offset_nanoseconds = TRY ( parse_date_time_utc_offset ( vm , time_zone ) ) ;
2023-10-04 15:45:59 -04:00
2024-11-20 20:06:42 -05:00
// d. Let offsetMinutes be offsetNanoseconds / (6 × 10**10).
2023-10-04 15:45:59 -04:00
auto offset_minutes = offset_nanoseconds / 60'000'000'000 ;
2024-11-20 20:06:42 -05:00
// e. Assert: offsetMinutes is an integer.
2023-10-04 15:45:59 -04:00
VERIFY ( trunc ( offset_minutes ) = = offset_minutes ) ;
2024-11-20 20:06:42 -05:00
// f. Set timeZone to FormatOffsetTimeZoneIdentifier(offsetMinutes).
2023-10-04 15:45:59 -04:00
time_zone = format_offset_time_zone_identifier ( offset_minutes ) ;
}
2025-04-07 16:59:06 -04:00
// 20. Else,
2023-10-04 15:45:59 -04:00
else {
2024-06-25 11:06:08 -04:00
// a. Let timeZoneIdentifierRecord be GetAvailableNamedTimeZoneIdentifier(timeZone).
auto time_zone_identifier_record = get_available_named_time_zone_identifier ( time_zone ) ;
// b. If timeZoneIdentifierRecord is EMPTY, throw a RangeError exception.
if ( ! time_zone_identifier_record . has_value ( ) )
return vm . throw_completion < RangeError > ( ErrorType : : OptionIsNotValidValue , time_zone , vm . names . timeZone ) ;
// c. Set timeZone to timeZoneIdentifierRecord.[[PrimaryIdentifier]].
time_zone = time_zone_identifier_record - > primary_identifier ;
2023-10-04 15:45:59 -04:00
}
2022-03-15 10:30:33 -04:00
2025-04-07 16:59:06 -04:00
// 21. Set dateTimeFormat.[[TimeZone]] to timeZone.
2024-06-12 10:47:20 -04:00
date_time_format - > set_time_zone ( time_zone ) ;
// NOTE: ICU requires time zone offset strings to be of the form "GMT+00:00"
if ( is_time_zone_offset_string )
time_zone = MUST ( String : : formatted ( " GMT{} " , time_zone ) ) ;
2022-03-15 10:30:33 -04:00
2024-11-27 16:09:41 -05:00
// AD-HOC: We must store the massaged time zone for creating ICU formatters for Temporal objects.
date_time_format - > set_temporal_time_zone ( time_zone ) ;
2025-04-07 16:59:06 -04:00
// 22. Let formatOptions be a new Record.
2024-06-23 09:14:27 -04:00
Unicode : : CalendarPattern format_options { } ;
2022-04-06 19:19:13 -04:00
2025-04-07 16:59:06 -04:00
// 23. Set formatOptions.[[hourCycle]] to hc.
2022-04-06 19:19:13 -04:00
format_options . hour_cycle = hour_cycle_value ;
2024-06-12 10:47:20 -04:00
format_options . hour12 = hour12_value ;
2022-04-06 19:19:13 -04:00
2025-04-07 16:59:06 -04:00
// 24. Let hasExplicitFormatComponents be false.
2022-03-28 12:29:17 -04:00
// NOTE: Instead of using a boolean, we track any explicitly provided component name for nicer exception messages.
PropertyKey const * explicit_format_component = nullptr ;
2025-04-07 16:59:06 -04:00
// 25. For each row of Table 16, except the header row, in table order, do
2023-11-05 17:42:31 +01:00
TRY ( for_each_calendar_field ( vm , format_options , [ & ] ( auto & option , PropertyKey const & property , auto const & values ) - > ThrowCompletionOr < void > {
2022-03-15 10:30:33 -04:00
using ValueType = typename RemoveReference < decltype ( option ) > : : ValueType ;
2024-08-15 15:06:54 -04:00
// a. Let prop be the name given in the Property column of the current row.
2022-03-15 10:30:33 -04:00
// b. If prop is "fractionalSecondDigits", then
if constexpr ( IsIntegral < ValueType > ) {
// i. Let value be ? GetNumberOption(options, "fractionalSecondDigits", 1, 3, undefined).
2022-08-20 08:25:24 +01:00
auto value = TRY ( get_number_option ( vm , * options , property , 1 , 3 , { } ) ) ;
2022-03-15 10:30:33 -04:00
// d. Set formatOptions.[[<prop>]] to value.
2022-03-28 12:29:17 -04:00
if ( value . has_value ( ) ) {
2022-03-15 10:30:33 -04:00
option = static_cast < ValueType > ( value . value ( ) ) ;
2022-03-28 12:29:17 -04:00
// e. If value is not undefined, then
// i. Set hasExplicitFormatComponents to true.
explicit_format_component = & property ;
}
2022-03-15 10:30:33 -04:00
}
// c. Else,
else {
2024-08-15 15:06:54 -04:00
// i. Let values be a List whose elements are the strings given in the Values column of the current row.
2023-01-12 10:22:37 -05:00
// ii. Let value be ? GetOption(options, prop, string, values, undefined).
2022-08-20 08:52:42 +01:00
auto value = TRY ( get_option ( vm , * options , property , OptionType : : String , values , Empty { } ) ) ;
2022-03-15 10:30:33 -04:00
// d. Set formatOptions.[[<prop>]] to value.
2022-03-28 12:29:17 -04:00
if ( ! value . is_undefined ( ) ) {
2024-06-23 09:14:27 -04:00
option = Unicode : : calendar_pattern_style_from_string ( value . as_string ( ) . utf8_string_view ( ) ) ;
2022-03-28 12:29:17 -04:00
// e. If value is not undefined, then
// i. Set hasExplicitFormatComponents to true.
explicit_format_component = & property ;
}
2022-03-15 10:30:33 -04:00
}
return { } ;
} ) ) ;
2025-04-07 16:59:06 -04:00
// 26. Let formatMatcher be ? GetOption(options, "formatMatcher", string, « "basic", "best fit" », "best fit").
2024-08-15 15:06:54 -04:00
[[maybe_unused]] auto format_matcher = TRY ( get_option ( vm , * options , vm . names . formatMatcher , OptionType : : String , AK : : Array { " basic " sv , " best fit " sv } , " best fit " sv ) ) ;
2022-03-15 10:30:33 -04:00
2025-04-07 16:59:06 -04:00
// 27. Let dateStyle be ? GetOption(options, "dateStyle", string, « "full", "long", "medium", "short" », undefined).
2022-08-20 08:52:42 +01:00
auto date_style = TRY ( get_option ( vm , * options , vm . names . dateStyle , OptionType : : String , AK : : Array { " full " sv , " long " sv , " medium " sv , " short " sv } , Empty { } ) ) ;
2022-03-15 10:30:33 -04:00
2025-04-07 16:59:06 -04:00
// 28. Set dateTimeFormat.[[DateStyle]] to dateStyle.
2022-03-15 10:30:33 -04:00
if ( ! date_style . is_undefined ( ) )
2023-08-08 19:17:55 +02:00
date_time_format - > set_date_style ( date_style . as_string ( ) . utf8_string_view ( ) ) ;
2022-03-15 10:30:33 -04:00
2025-04-07 16:59:06 -04:00
// 29. Let timeStyle be ? GetOption(options, "timeStyle", string, « "full", "long", "medium", "short" », undefined).
2022-08-20 08:52:42 +01:00
auto time_style = TRY ( get_option ( vm , * options , vm . names . timeStyle , OptionType : : String , AK : : Array { " full " sv , " long " sv , " medium " sv , " short " sv } , Empty { } ) ) ;
2022-03-15 10:30:33 -04:00
2025-04-07 16:59:06 -04:00
// 30. Set dateTimeFormat.[[TimeStyle]] to timeStyle.
2022-03-15 10:30:33 -04:00
if ( ! time_style . is_undefined ( ) )
2023-08-08 19:17:55 +02:00
date_time_format - > set_time_style ( time_style . as_string ( ) . utf8_string_view ( ) ) ;
2022-03-15 10:30:33 -04:00
2025-04-07 16:59:06 -04:00
// 31. Let formats be resolvedLocaleData.[[formats]].[[<resolvedCalendar>]].
2024-11-27 16:09:41 -05:00
2024-08-15 15:06:54 -04:00
OwnPtr < Unicode : : DateTimeFormat > formatter ;
2025-04-07 16:59:06 -04:00
// 32. If dateStyle is not undefined or timeStyle is not undefined, then
2023-07-21 21:33:55 -04:00
if ( date_time_format - > has_date_style ( ) | | date_time_format - > has_time_style ( ) ) {
2022-03-28 12:29:17 -04:00
// a. If hasExplicitFormatComponents is true, then
if ( explicit_format_component ! = nullptr ) {
// i. Throw a TypeError exception.
2022-08-16 20:33:17 +01:00
return vm . throw_completion < TypeError > ( ErrorType : : IntlInvalidDateTimeFormatOption , * explicit_format_component , " dateStyle or timeStyle " sv ) ;
2022-03-28 12:29:17 -04:00
}
2022-03-15 10:30:33 -04:00
2023-07-21 22:13:44 -04:00
// b. If required is date and timeStyle is not undefined, then
if ( required = = OptionRequired : : Date & & ! time_style . is_undefined ( ) ) {
// i. Throw a TypeError exception.
return vm . throw_completion < TypeError > ( ErrorType : : IntlInvalidDateTimeFormatOption , " timeStyle " sv , " date " sv ) ;
}
// c. If required is time and dateStyle is not undefined, then
if ( required = = OptionRequired : : Time & & ! date_style . is_undefined ( ) ) {
// i. Throw a TypeError exception.
return vm . throw_completion < TypeError > ( ErrorType : : IntlInvalidDateTimeFormatOption , " dateStyle " sv , " time " sv ) ;
}
2024-08-15 15:06:54 -04:00
// d. Let styles be resolvedLocaleData.[[styles]].[[<resolvedCalendar>]].
2023-07-21 22:13:44 -04:00
// e. Let bestFormat be DateTimeStyleFormat(dateStyle, timeStyle, styles).
2024-08-15 15:06:54 -04:00
formatter = Unicode : : DateTimeFormat : : create_for_date_and_time_style (
2025-03-17 16:24:09 -04:00
date_time_format - > icu_locale ( ) ,
2024-06-12 10:47:20 -04:00
time_zone ,
format_options . hour_cycle ,
format_options . hour12 ,
date_time_format - > date_style ( ) ,
date_time_format - > time_style ( ) ) ;
2023-07-21 22:13:44 -04:00
2024-11-27 16:09:41 -05:00
auto best_format = formatter - > chosen_pattern ( ) ;
using enum Unicode : : CalendarPattern : : Field ;
// f. If dateStyle is not undefined, then
if ( ! date_style . is_undefined ( ) ) {
2024-12-11 09:29:07 -05:00
// i. Set dateTimeFormat.[[TemporalPlainDateFormat]] to AdjustDateTimeStyleFormat(formats, bestFormat, formatMatcher, « [[weekday]], [[era]], [[year]], [[month]], [[day]] »).
2024-11-27 16:09:41 -05:00
auto temporal_plain_date_format = adjust_date_time_style_format ( best_format , { { Weekday , Era , Year , Month , Day } } ) ;
date_time_format - > set_temporal_plain_date_format ( move ( temporal_plain_date_format ) ) ;
2024-12-11 09:29:07 -05:00
// ii. Set dateTimeFormat.[[TemporalPlainYearMonthFormat]] to AdjustDateTimeStyleFormat(formats, bestFormat, formatMatcher, « [[era]], [[year]], [[month]] »).
2024-11-27 16:09:41 -05:00
auto temporal_plain_year_month_format = adjust_date_time_style_format ( best_format , { { Era , Year , Month } } ) ;
date_time_format - > set_temporal_plain_year_month_format ( move ( temporal_plain_year_month_format ) ) ;
2023-07-21 22:13:44 -04:00
2024-12-11 09:29:07 -05:00
// iii. Set dateTimeFormat.[[TemporalPlainMonthDayFormat]] to AdjustDateTimeStyleFormat(formats, bestFormat, formatMatcher, « [[month]], [[day]] »).
2024-11-27 16:09:41 -05:00
auto temporal_plain_month_day_format = adjust_date_time_style_format ( best_format , { { Month , Day } } ) ;
date_time_format - > set_temporal_plain_month_day_format ( move ( temporal_plain_month_day_format ) ) ;
}
// g. Else,
else {
// i. Set dateTimeFormat.[[TemporalPlainDateFormat]] to null.
// ii. Set dateTimeFormat.[[TemporalPlainYearMonthFormat]] to null.
// iii. Set dateTimeFormat.[[TemporalPlainMonthDayFormat]] to null.
2023-07-21 22:13:44 -04:00
}
2024-11-27 16:09:41 -05:00
// h. If timeStyle is not undefined, then
if ( ! time_style . is_undefined ( ) ) {
2024-12-11 09:29:07 -05:00
// i. Set dateTimeFormat.[[TemporalPlainTimeFormat]] to AdjustDateTimeStyleFormat(formats, bestFormat, formatMatcher, « [[dayPeriod]], [[hour]], [[minute]], [[second]], [[fractionalSecondDigits]] »).
2024-11-27 16:09:41 -05:00
auto temporal_plain_time_format = adjust_date_time_style_format ( best_format , { { DayPeriod , Hour , Minute , Second , FractionalSecondDigits } } ) ;
date_time_format - > set_temporal_plain_time_format ( move ( temporal_plain_time_format ) ) ;
}
// i. Else,
else {
// i. Set dateTimeFormat.[[TemporalPlainTimeFormat]] to null.
2023-07-21 22:13:44 -04:00
}
2024-12-11 09:29:07 -05:00
// j. Set dateTimeFormat.[[TemporalPlainDateTimeFormat]] to AdjustDateTimeStyleFormat(formats, bestFormat, formatMatcher, « [[weekday]], [[era]], [[year]], [[month]], [[day]], [[dayPeriod]], [[hour]], [[minute]], [[second]], [[fractionalSecondDigits]] »).
2024-11-27 16:09:41 -05:00
auto temporal_plain_date_time_format = adjust_date_time_style_format ( best_format , { { Weekday , Era , Year , Month , Day , DayPeriod , Hour , Minute , Second , FractionalSecondDigits } } ) ;
date_time_format - > set_temporal_plain_date_time_format ( move ( temporal_plain_date_time_format ) ) ;
// k. Set dateTimeFormat.[[TemporalInstantFormat]] to bestFormat.
date_time_format - > set_temporal_instant_format ( move ( best_format ) ) ;
}
2025-04-07 16:59:06 -04:00
// 33. Else,
2024-11-27 16:09:41 -05:00
else {
// a. Let bestFormat be GetDateTimeFormat(formats, formatMatcher, formatOptions, required, defaults, ALL).
auto best_format = get_date_time_format ( format_options , required , defaults , OptionInherit : : All ) . release_value ( ) ;
// b. Set dateTimeFormat.[[TemporalPlainDateFormat]] to GetDateTimeFormat(formats, formatMatcher, formatOptions, DATE, DATE, RELEVANT).
auto temporal_plain_date_format = get_date_time_format ( format_options , OptionRequired : : Date , OptionDefaults : : Date , OptionInherit : : Relevant ) ;
date_time_format - > set_temporal_plain_date_format ( move ( temporal_plain_date_format ) ) ;
// c. Set dateTimeFormat.[[TemporalPlainYearMonthFormat]] to GetDateTimeFormat(formats, formatMatcher, formatOptions, YEAR-MONTH, YEAR-MONTH, RELEVANT).
auto temporal_plain_year_month_format = get_date_time_format ( format_options , OptionRequired : : YearMonth , OptionDefaults : : YearMonth , OptionInherit : : Relevant ) ;
date_time_format - > set_temporal_plain_year_month_format ( move ( temporal_plain_year_month_format ) ) ;
// d. Set dateTimeFormat.[[TemporalPlainMonthDayFormat]] to GetDateTimeFormat(formats, formatMatcher, formatOptions, MONTH-DAY, MONTH-DAY, RELEVANT).
auto temporal_plain_month_day_format = get_date_time_format ( format_options , OptionRequired : : MonthDay , OptionDefaults : : MonthDay , OptionInherit : : Relevant ) ;
date_time_format - > set_temporal_plain_month_day_format ( move ( temporal_plain_month_day_format ) ) ;
// e. Set dateTimeFormat.[[TemporalPlainTimeFormat]] to GetDateTimeFormat(formats, formatMatcher, formatOptions, TIME, TIME, RELEVANT).
auto temporal_plain_time_format = get_date_time_format ( format_options , OptionRequired : : Time , OptionDefaults : : Time , OptionInherit : : Relevant ) ;
date_time_format - > set_temporal_plain_time_format ( move ( temporal_plain_time_format ) ) ;
// f. Set dateTimeFormat.[[TemporalPlainDateTimeFormat]] to GetDateTimeFormat(formats, formatMatcher, formatOptions, ANY, ALL, RELEVANT).
auto temporal_plain_date_time_format = get_date_time_format ( format_options , OptionRequired : : Any , OptionDefaults : : All , OptionInherit : : Relevant ) ;
date_time_format - > set_temporal_plain_date_time_format ( move ( temporal_plain_date_time_format ) ) ;
// g. If toLocaleStringTimeZone is present, then
if ( to_locale_string_time_zone . has_value ( ) ) {
// i. Set dateTimeFormat.[[TemporalInstantFormat]] to GetDateTimeFormat(formats, formatMatcher, formatOptions, ANY, ZONED-DATE-TIME, ALL).
auto temporal_instant_format = get_date_time_format ( format_options , OptionRequired : : Any , OptionDefaults : : ZonedDateTime , OptionInherit : : All ) ;
date_time_format - > set_temporal_instant_format ( move ( temporal_instant_format ) ) ;
}
2023-07-21 22:13:44 -04:00
// h. Else,
2024-11-27 16:09:41 -05:00
else {
// i. Set dateTimeFormat.[[TemporalInstantFormat]] to GetDateTimeFormat(formats, formatMatcher, formatOptions, ANY, ALL, ALL).
auto temporal_instant_format = get_date_time_format ( format_options , OptionRequired : : Any , OptionDefaults : : All , OptionInherit : : All ) ;
date_time_format - > set_temporal_instant_format ( move ( temporal_instant_format ) ) ;
}
2024-08-15 15:06:54 -04:00
formatter = Unicode : : DateTimeFormat : : create_for_pattern_options (
2025-03-17 16:24:09 -04:00
date_time_format - > icu_locale ( ) ,
2024-06-12 10:47:20 -04:00
time_zone ,
2024-11-27 16:09:41 -05:00
best_format ) ;
2022-03-15 10:30:33 -04:00
}
2025-04-07 16:59:06 -04:00
// 34. Set dateTimeFormat.[[DateTimeFormat]] to bestFormat.
2024-08-15 15:06:54 -04:00
date_time_format - > set_date_time_format ( formatter - > chosen_pattern ( ) ) ;
2022-03-15 10:30:33 -04:00
2024-08-15 15:06:54 -04:00
// Non-standard, create an ICU number formatter for this Intl object.
date_time_format - > set_formatter ( formatter . release_nonnull ( ) ) ;
2022-03-15 10:30:33 -04:00
2025-04-07 16:59:06 -04:00
// 35. Return dateTimeFormat.
2023-07-21 21:33:55 -04:00
return date_time_format ;
2022-03-15 10:30:33 -04:00
}
2023-10-04 15:45:59 -04:00
// 11.1.3 FormatOffsetTimeZoneIdentifier ( offsetMinutes ), https://tc39.es/ecma402/#sec-formatoffsettimezoneidentifier
String format_offset_time_zone_identifier ( double offset_minutes )
{
// 1. If offsetMinutes ≥ 0, let sign be the code unit 0x002B (PLUS SIGN); otherwise, let sign be the code unit 0x002D (HYPHEN-MINUS).
auto sign = offset_minutes > = 0.0 ? ' + ' : ' - ' ;
// 2. Let absoluteMinutes be abs(offsetMinutes).
auto absolute_minutes = fabs ( offset_minutes ) ;
// 3. Let hours be floor(absoluteMinutes / 60).
auto hours = static_cast < i64 > ( floor ( absolute_minutes / 60.0 ) ) ;
// 4. Let minutes be absoluteMinutes modulo 60.
auto minutes = static_cast < i64 > ( modulo ( absolute_minutes , 60.0 ) ) ;
// 5. Return the string-concatenation of sign, ToZeroPaddedDecimalString(hours, 2), the code unit 0x003A (COLON), and ToZeroPaddedDecimalString(minutes, 2).
return MUST ( String : : formatted ( " {}{:02}:{:02} " , sign , hours , minutes ) ) ;
}
2021-11-18 10:30:31 -05:00
}