Commit f9073efaed6 for woocommerce

commit f9073efaed64c1131c7217029d49a28052a35acf
Author: louwie17 <lourensschep@gmail.com>
Date:   Tue Oct 13 10:09:50 2026 +0200

    Fix Analytics custom date range parsing typed dates in the wrong format (#69701)

    * Parse typed Analytics dates with the translated format shown in the hint

    The custom date range picker translated its "MM/DD/YYYY" placeholder but
    always parsed typed input as MM/DD/YYYY, so day-first locales (es_ES, it_IT,
    de_DE) had dates swapped or rejected. Use the translated format for both.

    Fixes #69616.

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01NEJuaZ8xRBcmbC5AsHEBD4

    * Fall back to MM/DD/YYYY when the translated date format isn't a Moment.js format

    Six WooCommerce language packs translate "MM/DD/YYYY" into text Moment.js
    can't parse (cs_CZ "MM/DD/RRRR", et "KK/PP/AAAA", pt_AO, sq, gu, lo). Only use
    the translation when it round-trips a date, so those locales keep working.

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01NEJuaZ8xRBcmbC5AsHEBD4

    * Cover the French JJ/MM/AAAA translation that #61028 fixed

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01NEJuaZ8xRBcmbC5AsHEBD4

    * Drop the unused shortDateFormatPlaceholder prop from DatePickerContent

    Nothing passes it since the hint and parser share one format. DateRange keeps
    its prop because it is a public export.

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01NEJuaZ8xRBcmbC5AsHEBD4

    ---------

    Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

diff --git a/packages/js/components/changelog/fix-69616-date-range-translated-format b/packages/js/components/changelog/fix-69616-date-range-translated-format
new file mode 100644
index 00000000000..f7cacba50b6
--- /dev/null
+++ b/packages/js/components/changelog/fix-69616-date-range-translated-format
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+DateRangeFilterPicker: parse typed dates with the same translated format shown as the field hint.
diff --git a/packages/js/components/src/date-range-filter-picker/content.js b/packages/js/components/src/date-range-filter-picker/content.js
index c29693939c3..686aa89624f 100644
--- a/packages/js/components/src/date-range-filter-picker/content.js
+++ b/packages/js/components/src/date-range-filter-picker/content.js
@@ -61,7 +61,6 @@ class DatePickerContent extends Component {
 			afterError,
 			beforeError,
 			shortDateFormat,
-			shortDateFormatPlaceholder,
 		} = this.props;
 		return (
 			<div>
@@ -112,9 +111,6 @@ class DatePickerContent extends Component {
 										afterError={ afterError }
 										beforeError={ beforeError }
 										shortDateFormat={ shortDateFormat }
-										shortDateFormatPlaceholder={
-											shortDateFormatPlaceholder
-										}
 										losesFocusTo={
 											this.controlsRef.current
 										}
@@ -200,7 +196,6 @@ DatePickerContent.propTypes = {
 	afterError: PropTypes.string,
 	beforeError: PropTypes.string,
 	shortDateFormat: PropTypes.string.isRequired,
-	shortDateFormatPlaceholder: PropTypes.string,
 };

 export default DatePickerContent;
diff --git a/packages/js/components/src/date-range-filter-picker/index.js b/packages/js/components/src/date-range-filter-picker/index.js
index 2b1651cf524..b3cc53dd782 100644
--- a/packages/js/components/src/date-range-filter-picker/index.js
+++ b/packages/js/components/src/date-range-filter-picker/index.js
@@ -7,6 +7,7 @@ import { Dropdown } from '@wordpress/components';
 import PropTypes from 'prop-types';
 import { withViewportMatch } from '@wordpress/viewport';
 import clsx from 'clsx';
+import moment from 'moment';

 /**
  * Internal dependencies
@@ -14,8 +15,21 @@ import clsx from 'clsx';
 import DatePickerContent from './content';
 import DropdownButton from '../dropdown-button';

-const shortDateFormatPlaceholder = __( 'MM/DD/YYYY', 'woocommerce' );
-const shortDateFormat = 'MM/DD/YYYY';
+const defaultShortDateFormat = 'MM/DD/YYYY';
+const formatCheckDate = moment( '2001-02-03', 'YYYY-MM-DD' );
+
+/**
+ * Format shown as the input hint and used to parse typed dates, so both always agree.
+ * Some translations aren't Moment.js formats (e.g. "MM/DD/RRRR"), so fall back unless the translation round-trips a date.
+ */
+const getShortDateFormat = () => {
+	/* translators: Moment.js date format for typed dates. Keep the DD, MM and YYYY tokens. */
+	const format = __( 'MM/DD/YYYY', 'woocommerce' );
+	const parsed = moment( formatCheckDate.format( format ), format, true );
+	return parsed.isSame( formatCheckDate, 'day' )
+		? format
+		: defaultShortDateFormat;
+};

 /**
  * Select a range of dates or single dates.
@@ -46,6 +60,7 @@ class DateRangeFilterPicker extends Component {

 	getResetState() {
 		const { period, compare, before, after } = this.props.dateQuery;
+		const shortDateFormat = getShortDateFormat();

 		return {
 			period,
@@ -171,10 +186,7 @@ class DateRangeFilterPicker extends Component {
 							beforeText={ beforeText }
 							afterError={ afterError }
 							beforeError={ beforeError }
-							shortDateFormat={ shortDateFormat }
-							shortDateFormatPlaceholder={
-								shortDateFormatPlaceholder
-							}
+							shortDateFormat={ getShortDateFormat() }
 						/>
 					) }
 				/>
diff --git a/packages/js/components/src/date-range-filter-picker/test/index.js b/packages/js/components/src/date-range-filter-picker/test/index.js
new file mode 100644
index 00000000000..390f6d3a93d
--- /dev/null
+++ b/packages/js/components/src/date-range-filter-picker/test/index.js
@@ -0,0 +1,90 @@
+/**
+ * External dependencies
+ */
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { createElement } from '@wordpress/element';
+import { resetLocaleData, setLocaleData } from '@wordpress/i18n';
+import moment from 'moment';
+
+/**
+ * Internal dependencies
+ */
+import DateRangeFilterPicker from '../index';
+
+const renderPicker = ( onRangeSelect ) =>
+	render(
+		<DateRangeFilterPicker
+			onRangeSelect={ onRangeSelect }
+			isoDateFormat="YYYY-MM-DD"
+			dateQuery={ {
+				period: 'custom',
+				compare: 'previous_year',
+				after: moment( '2024-09-25' ),
+				before: moment( '2024-10-05' ),
+				primaryDate: { label: 'Custom', range: 'Sep 25 - Oct 5, 2024' },
+				secondaryDate: {
+					label: 'Previous year',
+					range: 'Sep 25 - Oct 5, 2023',
+				},
+			} }
+		/>
+	);
+
+const typeDate = async ( label, value ) => {
+	const input = screen.getByLabelText( label );
+	await userEvent.clear( input );
+	await userEvent.type( input, value );
+};
+
+describe( 'DateRangeFilterPicker', () => {
+	afterEach( () => {
+		resetLocaleData();
+	} );
+
+	it.each( [
+		[ 'untranslated', undefined, 'MM/DD/YYYY', '09/25/2024', '10/04/2024' ],
+		[ 'day-first', 'DD/MM/YYYY', 'DD/MM/YYYY', '25/09/2024', '04/10/2024' ],
+		[ 'dotted', 'DD.MM.YYYY', 'DD.MM.YYYY', '25.09.2024', '04.10.2024' ],
+		// Translations from real language packs that aren't Moment.js formats.
+		// The French one is why #61028 stopped parsing with the translation.
+		[ 'fr_FR', 'JJ/MM/AAAA', 'MM/DD/YYYY', '09/25/2024', '10/04/2024' ],
+		[ 'cs_CZ', 'MM/DD/RRRR', 'MM/DD/YYYY', '09/25/2024', '10/04/2024' ],
+		[ 'et', 'KK/PP/AAAA', 'MM/DD/YYYY', '09/25/2024', '10/04/2024' ],
+		[ 'lowercase', 'dd/mm/aaaa', 'MM/DD/YYYY', '09/25/2024', '10/04/2024' ],
+	] )(
+		'shows and parses typed dates for the %s translation',
+		async ( _, translatedFormat, format, afterText, typedBefore ) => {
+			if ( translatedFormat ) {
+				setLocaleData(
+					{ 'MM/DD/YYYY': [ translatedFormat ] },
+					'woocommerce'
+				);
+			}
+			const onRangeSelect = jest.fn();
+			renderPicker( onRangeSelect );
+
+			await userEvent.click( screen.getByRole( 'button' ) );
+
+			const startInput = screen.getByLabelText( 'Start Date' );
+			expect( startInput ).toHaveValue( afterText );
+			expect( startInput ).toHaveAttribute(
+				'placeholder',
+				format.toLowerCase()
+			);
+
+			await typeDate( 'End Date', typedBefore );
+			expect( screen.queryByText( 'Invalid date' ) ).toBeNull();
+
+			await userEvent.click(
+				screen.getByRole( 'button', { name: 'Update' } )
+			);
+			expect( onRangeSelect ).toHaveBeenCalledWith( {
+				period: 'custom',
+				compare: 'previous_year',
+				after: '2024-09-25',
+				before: '2024-10-04',
+			} );
+		}
+	);
+} );
diff --git a/plugins/woocommerce/changelog/fix-69616-date-range-translated-format b/plugins/woocommerce/changelog/fix-69616-date-range-translated-format
new file mode 100644
index 00000000000..390d19ab682
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-69616-date-range-translated-format
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Analytics: typed custom dates now follow the format shown in the field hint, so day-first locales get the right dates.