Commit 26155aa9745 for woocommerce
commit 26155aa974543821c930e16aafd658d74db1ee0d
Author: Peter Petrov <peter.petrov89@gmail.com>
Date: Wed Aug 12 16:58:32 2026 +0300
Respect the WordPress "Week Starts On" setting in Analytics week ranges (#67629)
* Respect the WordPress "Week Starts On" setting in Analytics week ranges
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Spread the actual @wordpress/date module in the import status bar test mock
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
diff --git a/packages/js/date/changelog/fix-week-start-setting b/packages/js/date/changelog/fix-week-start-setting
new file mode 100644
index 00000000000..f2869a18d52
--- /dev/null
+++ b/packages/js/date/changelog/fix-week-start-setting
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Respect the WordPress "Week Starts On" setting in Analytics week date ranges and calendars.
diff --git a/packages/js/date/src/index.ts b/packages/js/date/src/index.ts
index cd116113e7d..74620e3fdb0 100644
--- a/packages/js/date/src/index.ts
+++ b/packages/js/date/src/index.ts
@@ -3,6 +3,7 @@
*/
import moment from 'moment';
import { getTimezoneOffset } from 'date-fns-tz';
+import { getSettings as getDateSettings } from '@wordpress/date';
import { find, memoize } from 'lodash';
import { __ } from '@wordpress/i18n';
import { parse } from 'qs';
@@ -258,6 +259,34 @@ function anchorRangeToStoreTimeZone( range: DateValue ): DateValue {
};
}
+/**
+ * Aligns the moment locale's start of the week with the WordPress
+ * "Week Starts On" setting. WordPress core applies the setting to the moment
+ * locale, but `wp.date.setSettings` then redefines the locale without a `week`
+ * key, resetting the start of the week to Sunday; without this correction,
+ * week ranges and calendar layouts ignore the setting.
+ */
+function ensureMomentStartOfWeek() {
+ const startOfWeek = getDateSettings().l10n?.startOfWeek;
+
+ if (
+ typeof startOfWeek !== 'number' ||
+ ! Number.isInteger( startOfWeek ) ||
+ startOfWeek < 0 ||
+ startOfWeek > 6
+ ) {
+ return;
+ }
+
+ if ( moment.localeData().firstDayOfWeek() !== startOfWeek ) {
+ moment.updateLocale( moment.locale(), {
+ week: { dow: startOfWeek },
+ } );
+ }
+}
+
+ensureMomentStartOfWeek();
+
/**
* Get a DateValue object for a period prior to the current period.
*
@@ -269,6 +298,8 @@ export function getLastPeriod(
period: moment.DurationInputArg2,
compare: string
) {
+ ensureMomentStartOfWeek();
+
const primaryStart = getStoreTimeZoneMoment()
.startOf( period )
.subtract( 1, period );
@@ -321,6 +352,8 @@ export function getCurrentPeriod(
period: moment.DurationInputArg2,
compare: string
) {
+ ensureMomentStartOfWeek();
+
const primaryStart = getStoreTimeZoneMoment().startOf( period );
const primaryEnd = getStoreTimeZoneMoment();
diff --git a/packages/js/date/src/test/index.ts b/packages/js/date/src/test/index.ts
index 980055f47dd..52088bc4ff7 100644
--- a/packages/js/date/src/test/index.ts
+++ b/packages/js/date/src/test/index.ts
@@ -2,7 +2,11 @@
* External dependencies
*/
import moment from 'moment';
-import { format as formatDate } from '@wordpress/date';
+import {
+ format as formatDate,
+ getSettings as getDateSettings,
+ setSettings as setDateSettings,
+} from '@wordpress/date';
import { timeFormat as d3TimeFormat } from 'd3-time-format';
/**
* Internal dependencies
@@ -732,6 +736,58 @@ describe( 'getLastPeriod', () => {
} );
} );
+describe( 'start of week setting', () => {
+ const originalSettings = getDateSettings();
+ const originalDow = moment.localeData().firstDayOfWeek();
+
+ afterEach( () => {
+ setDateSettings( originalSettings );
+ moment.updateLocale( moment.locale(), {
+ week: { dow: originalDow },
+ } );
+ } );
+
+ it( 'getCurrentPeriod should start the week on the day from the WordPress setting', () => {
+ setDateSettings( {
+ ...originalSettings,
+ l10n: { ...originalSettings.l10n, startOfWeek: 1 },
+ } );
+
+ const dateValue = getCurrentPeriod( 'week', 'previous_period' );
+
+ expect( dateValue.primaryStart.day() ).toBe( 1 );
+ expect( dateValue.primaryStart.isSameOrBefore( moment(), 'day' ) ).toBe(
+ true
+ );
+ } );
+
+ it( 'getLastPeriod should start and end the week on days from the WordPress setting', () => {
+ setDateSettings( {
+ ...originalSettings,
+ l10n: { ...originalSettings.l10n, startOfWeek: 3 },
+ } );
+
+ const dateValue = getLastPeriod( 'week', 'previous_period' );
+
+ expect( dateValue.primaryStart.day() ).toBe( 3 );
+ expect( dateValue.primaryEnd.day() ).toBe( 2 );
+ } );
+
+ it( 'should leave the moment default when the setting is invalid', () => {
+ setDateSettings( {
+ ...originalSettings,
+ l10n: {
+ ...originalSettings.l10n,
+ startOfWeek: 7 as unknown as 0,
+ },
+ } );
+
+ const dateValue = getCurrentPeriod( 'week', 'previous_period' );
+
+ expect( dateValue.primaryStart.day() ).toBe( originalDow );
+ } );
+} );
+
describe( 'getRangeLabel', () => {
it( 'should return correct string for dates on the same day', () => {
const label = getRangeLabel(
diff --git a/plugins/woocommerce/changelog/fix-week-start-setting b/plugins/woocommerce/changelog/fix-week-start-setting
new file mode 100644
index 00000000000..5c853df534a
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-week-start-setting
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+
+Spread the actual @wordpress/date module in the import status bar test mock.
diff --git a/plugins/woocommerce/client/admin/client/analytics/components/import-status-bar/test/import-status-bar.test.tsx b/plugins/woocommerce/client/admin/client/analytics/components/import-status-bar/test/import-status-bar.test.tsx
index 3a3ff7f9d04..ad005f4fd2e 100644
--- a/plugins/woocommerce/client/admin/client/analytics/components/import-status-bar/test/import-status-bar.test.tsx
+++ b/plugins/woocommerce/client/admin/client/analytics/components/import-status-bar/test/import-status-bar.test.tsx
@@ -21,6 +21,7 @@ jest.mock( '@wordpress/data', () => ( {
} ) ),
} ) );
jest.mock( '@wordpress/date', () => ( {
+ ...jest.requireActual( '@wordpress/date' ),
dateI18n: jest.fn( ( format, date ) => {
// Simple mock that returns a date-like string
if ( ! date ) return 'Never';