From 2289a37d11ab9c4c8fc1f1e2383dd711d3df53b8 Mon Sep 17 00:00:00 2001 From: Vellumic <161718748+Vellumic@users.noreply.github.com> Date: Sun, 12 Apr 2026 18:34:04 +0300 Subject: [PATCH 1/3] Fix a spec violation in `Date.prototype.toLocaleString` --- .../src/builtins/intl/date_time_format/mod.rs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/core/engine/src/builtins/intl/date_time_format/mod.rs b/core/engine/src/builtins/intl/date_time_format/mod.rs index 1090907f792..b888eebbe21 100644 --- a/core/engine/src/builtins/intl/date_time_format/mod.rs +++ b/core/engine/src/builtins/intl/date_time_format/mod.rs @@ -1025,24 +1025,6 @@ pub(crate) fn format_date_time_locale( context: &mut Context, ) -> JsResult { let options = coerce_options_to_object(options, context)?; - if format_type != FormatType::Time - && get_option::(&options, js_string!("dateStyle"), context)?.is_none() - { - options.create_data_property_or_throw( - js_string!("dateStyle"), - JsValue::from(js_string!("long")), - context, - )?; - } - if format_type != FormatType::Date - && get_option::(&options, js_string!("timeStyle"), context)?.is_none() - { - options.create_data_property_or_throw( - js_string!("timeStyle"), - JsValue::from(js_string!("long")), - context, - )?; - } let options_value = options.into(); let dtf = create_date_time_format(locales, &options_value, format_type, defaults, context)?; // FormatDateTime steps 1–2: TimeClip and NaN check (format_timestamp_with_dtf does ToLocalTime + format only). From ff676cda6e326dae77479ffb6b12a73d6ed25456 Mon Sep 17 00:00:00 2001 From: Vellumic <161718748+Vellumic@users.noreply.github.com> Date: Mon, 13 Apr 2026 04:09:23 +0300 Subject: [PATCH 2/3] Add test --- .../builtins/intl/date_time_format/tests.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/core/engine/src/builtins/intl/date_time_format/tests.rs b/core/engine/src/builtins/intl/date_time_format/tests.rs index 66d97ccfd99..f8af4902267 100644 --- a/core/engine/src/builtins/intl/date_time_format/tests.rs +++ b/core/engine/src/builtins/intl/date_time_format/tests.rs @@ -65,3 +65,26 @@ fn dtf_basic() { TestAction::assert_eq("result === 'Sunday, 20 December 2020 at 14:23:16'", true), ]); } + +#[cfg(feature = "intl_bundled")] +#[test] +fn date_to_locale_string() { + run_test_actions([ + TestAction::run(indoc! {" + // Setup date + const date = new Date(Date.UTC(2021, 3, 12, 6, 7)); + + let result = date.toLocaleString('en-US', { dateStyle: 'short' }); + "}), + TestAction::assert_eq("result === '4/12/21'", true), + ]); + run_test_actions([ + TestAction::run(indoc! {" + // Setup date + const date = new Date(Date.UTC(2021, 3, 12, 6, 7)); + + let result = date.toLocaleString('en-US', { timeStyle: 'short' }); + "}), + TestAction::assert_eq("result === '6:07\u{202f}AM'", true), + ]); +} From a3edd2fac13ec042f07237fcdac93e493bdde39d Mon Sep 17 00:00:00 2001 From: Vellumic <161718748+Vellumic@users.noreply.github.com> Date: Wed, 15 Apr 2026 04:31:22 +0300 Subject: [PATCH 3/3] Fix observable behavior of `Intl.DateTimeFormat` constructor --- .../src/builtins/intl/date_time_format/mod.rs | 15 ++++++---- .../builtins/intl/date_time_format/tests.rs | 28 +++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/core/engine/src/builtins/intl/date_time_format/mod.rs b/core/engine/src/builtins/intl/date_time_format/mod.rs index b888eebbe21..455368a69bd 100644 --- a/core/engine/src/builtins/intl/date_time_format/mod.rs +++ b/core/engine/src/builtins/intl/date_time_format/mod.rs @@ -177,6 +177,11 @@ impl BuiltInConstructor for DateTimeFormat { let options = args.get_or_undefined(1); // 2. Let dateTimeFormat be ? CreateDateTimeFormat(newTarget, locales, options, any, date). + let prototype = get_prototype_from_constructor( + new_target_inner, + StandardConstructors::date_time_format, + context, + )?; let dtf = create_date_time_format( locales, options, @@ -184,11 +189,6 @@ impl BuiltInConstructor for DateTimeFormat { FormatDefaults::Date, context, )?; - let prototype = get_prototype_from_constructor( - new_target_inner, - StandardConstructors::date_time_format, - context, - )?; let date_time_format = JsObject::from_proto_and_data(prototype, dtf); // 3. If the implementation supports the normative optional constructor mode of 4.3 Note 1, then @@ -534,6 +534,11 @@ pub(crate) fn create_date_time_format( defaults: FormatDefaults, context: &mut Context, ) -> JsResult { + // NOTE: The below step's code was moved out into constructor to prevent unnecessary JsObject allocation when we create dtf internally + // (e.g. toLocaleString methods of Date and Temporal objects) + // 1. Let dateTimeFormat be ? OrdinaryCreateFromConstructor(newTarget, "%Intl.DateTimeFormat.prototype%", + // « [[InitializedDateTimeFormat]], [[Locale]], [[Calendar]], [[NumberingSystem]], [[TimeZone]], + // [[HourCycle]], [[DateStyle]], [[TimeStyle]], [[DateTimeFormat]], [[BoundFormat]] »). // 2. Let hour12 be undefined. <- TODO // 3. Let modifyResolutionOptions be a new Abstract Closure with parameters (options) that captures hour12 and performs the following steps when called: // a. Set hour12 to options.[[hour12]]. diff --git a/core/engine/src/builtins/intl/date_time_format/tests.rs b/core/engine/src/builtins/intl/date_time_format/tests.rs index f8af4902267..2d0e83a41e4 100644 --- a/core/engine/src/builtins/intl/date_time_format/tests.rs +++ b/core/engine/src/builtins/intl/date_time_format/tests.rs @@ -88,3 +88,31 @@ fn date_to_locale_string() { TestAction::assert_eq("result === '6:07\u{202f}AM'", true), ]); } + +#[cfg(feature = "intl_bundled")] +#[test] +fn dtf_ctor_observable_behavior() { + run_test_actions([ + TestAction::run(indoc! {" + const expected = []; + + const proxyConstructor = new Proxy(Intl.DateTimeFormat, { + get(target, prop) { + if (prop === 'prototype') { + expected.push('prototype-access'); + } + return target[prop]; + } + }); + + try { + new proxyConstructor('en', { timeZone: 'Invalid/Zone' }); + } catch (e) { + expected.push('error-thrown'); + } + "}), + TestAction::assert_eq("expected.length === 2", true), + TestAction::assert_eq("expected[0] === 'prototype-access'", true), + TestAction::assert_eq("expected[1] === 'error-thrown'", true), + ]); +}