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.