diff --git a/codecov.yml b/codecov.yml
index 4a05503c5b9e..b4af65812615 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -10,15 +10,15 @@ coverage:
adapters:
target: 100%
paths:
- - 'packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.ts'
- - 'packages/x-date-pickers/src/AdapterDateFnsV3/AdapterDateFnsV3.ts'
- - 'packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.ts'
- - 'packages/x-date-pickers/src/AdapterDateFnsJalaliV3/AdapterDateFnsJalaliV3.ts'
- - 'packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.ts'
- - 'packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts'
- - 'packages/x-date-pickers/src/AdapterMoment/AdapterMoment.ts'
- - 'packages/x-date-pickers/src/AdapterMomentHijri/AdapterMomentHijri.ts'
- - 'packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.ts'
+ - packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.ts
+ - packages/x-date-pickers/src/AdapterDateFnsV3/AdapterDateFnsV3.ts
+ - packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.ts
+ - packages/x-date-pickers/src/AdapterDateFnsJalaliV3/AdapterDateFnsJalaliV3.ts
+ - packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.ts
+ - packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts
+ - packages/x-date-pickers/src/AdapterMoment/AdapterMoment.ts
+ - packages/x-date-pickers/src/AdapterMomentHijri/AdapterMomentHijri.ts
+ - packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.ts
patch: off
comment: false
diff --git a/packages/x-date-pickers-pro/src/DateRangeCalendar/timezone.DateRangeCalendar.test.tsx b/packages/x-date-pickers-pro/src/DateRangeCalendar/timezone.DateRangeCalendar.test.tsx
new file mode 100644
index 000000000000..2d345ab1cf08
--- /dev/null
+++ b/packages/x-date-pickers-pro/src/DateRangeCalendar/timezone.DateRangeCalendar.test.tsx
@@ -0,0 +1,41 @@
+import * as React from 'react';
+import { expect } from 'chai';
+import { screen, fireEvent } from '@mui/internal-test-utils';
+import { describeAdapters } from 'test/utils/pickers';
+import { DateRangeCalendar } from './DateRangeCalendar';
+
+describe(' - Timezone', () => {
+ describeAdapters('Timezone prop', DateRangeCalendar, ({ adapter, render }) => {
+ if (!adapter.isTimezoneCompatible) {
+ return;
+ }
+
+ it('should correctly render month days when timezone changes', () => {
+ function DateCalendarWithControlledTimezone() {
+ const [timezone, setTimezone] = React.useState('Europe/Paris');
+ return (
+
+
+
+
+ );
+ }
+ render();
+
+ expect(
+ screen.getAllByRole('gridcell', {
+ name: (_, element) => element.nodeName === 'BUTTON',
+ }).length,
+ ).to.equal(30);
+
+ fireEvent.click(screen.getByRole('button', { name: 'Switch timezone' }));
+
+ // the amount of rendered days should remain the same after changing timezone
+ expect(
+ screen.getAllByRole('gridcell', {
+ name: (_, element) => element.nodeName === 'BUTTON',
+ }).length,
+ ).to.equal(30);
+ });
+ });
+});
diff --git a/packages/x-date-pickers/src/DateCalendar/DayCalendar.tsx b/packages/x-date-pickers/src/DateCalendar/DayCalendar.tsx
index 8998300b6407..5348469f3c84 100644
--- a/packages/x-date-pickers/src/DateCalendar/DayCalendar.tsx
+++ b/packages/x-date-pickers/src/DateCalendar/DayCalendar.tsx
@@ -534,9 +534,8 @@ export function DayCalendar(inProps: DayCalendarP
]);
const weeksToDisplay = React.useMemo(() => {
- const currentMonthWithTimezone = utils.setTimezone(currentMonth, timezone);
- const toDisplay = utils.getWeekArray(currentMonthWithTimezone);
- let nextMonth = utils.addMonths(currentMonthWithTimezone, 1);
+ const toDisplay = utils.getWeekArray(currentMonth);
+ let nextMonth = utils.addMonths(currentMonth, 1);
while (fixedWeekNumber && toDisplay.length < fixedWeekNumber) {
const additionalWeeks = utils.getWeekArray(nextMonth);
const hasCommonWeek = utils.isSameDay(
@@ -553,7 +552,7 @@ export function DayCalendar(inProps: DayCalendarP
nextMonth = utils.addMonths(nextMonth, 1);
}
return toDisplay;
- }, [currentMonth, fixedWeekNumber, utils, timezone]);
+ }, [currentMonth, fixedWeekNumber, utils]);
return (
diff --git a/packages/x-date-pickers/src/DateCalendar/tests/timezone.DateCalendar.test.tsx b/packages/x-date-pickers/src/DateCalendar/tests/timezone.DateCalendar.test.tsx
index 0f00aeee3039..a8624667be44 100644
--- a/packages/x-date-pickers/src/DateCalendar/tests/timezone.DateCalendar.test.tsx
+++ b/packages/x-date-pickers/src/DateCalendar/tests/timezone.DateCalendar.test.tsx
@@ -29,6 +29,49 @@ describe(' - Timezone', () => {
expect(actualDate).toEqualDateTime(expectedDate);
});
+ it('should use "default" timezone for onChange when provided', () => {
+ const onChange = spy();
+ const value = adapter.date('2022-04-25T15:30');
+
+ render();
+
+ fireEvent.click(screen.getByRole('gridcell', { name: '25' }));
+ const expectedDate = adapter.setDate(value, 25);
+
+ // Check the `onChange` value (uses timezone prop)
+ const actualDate = onChange.lastCall.firstArg;
+ expect(adapter.getTimezone(actualDate)).to.equal(adapter.lib === 'dayjs' ? 'UTC' : 'system');
+ expect(actualDate).toEqualDateTime(expectedDate);
+ });
+
+ it('should correctly render month days when timezone changes', () => {
+ function DateCalendarWithControlledTimezone() {
+ const [timezone, setTimezone] = React.useState('Europe/Paris');
+ return (
+
+
+
+
+ );
+ }
+ render();
+
+ expect(
+ screen.getAllByRole('gridcell', {
+ name: (_, element) => element.nodeName === 'BUTTON',
+ }).length,
+ ).to.equal(30);
+
+ fireEvent.click(screen.getByRole('button', { name: 'Switch timezone' }));
+
+ // the amount of rendered days should remain the same after changing timezone
+ expect(
+ screen.getAllByRole('gridcell', {
+ name: (_, element) => element.nodeName === 'BUTTON',
+ }).length,
+ ).to.equal(30);
+ });
+
TIMEZONE_TO_TEST.forEach((timezone) => {
describe(`Timezone: ${timezone}`, () => {
it('should use timezone prop for onChange when no value is provided', () => {
diff --git a/packages/x-date-pickers/src/DateCalendar/useCalendarState.tsx b/packages/x-date-pickers/src/DateCalendar/useCalendarState.tsx
index 29fff983fe3d..5e772c5d742d 100644
--- a/packages/x-date-pickers/src/DateCalendar/useCalendarState.tsx
+++ b/packages/x-date-pickers/src/DateCalendar/useCalendarState.tsx
@@ -43,6 +43,7 @@ export const createCalendarStateReducer =
action:
| ReducerAction<'finishMonthSwitchingAnimation'>
| ReducerAction<'changeMonth', ChangeMonthPayload>
+ | ReducerAction<'changeMonthTimezone', { newTimezone: string }>
| ReducerAction<'changeFocusedDay', ChangeFocusedDayPayload>,
): CalendarState => {
switch (action.type) {
@@ -54,6 +55,21 @@ export const createCalendarStateReducer =
isMonthSwitchingAnimating: !reduceAnimations,
};
+ case 'changeMonthTimezone': {
+ const newTimezone = action.newTimezone;
+ if (utils.getTimezone(state.currentMonth) === newTimezone) {
+ return state;
+ }
+ let newCurrentMonth = utils.setTimezone(state.currentMonth, newTimezone);
+ if (utils.getMonth(newCurrentMonth) !== utils.getMonth(state.currentMonth)) {
+ newCurrentMonth = utils.setMonth(newCurrentMonth, utils.getMonth(state.currentMonth));
+ }
+ return {
+ ...state,
+ currentMonth: newCurrentMonth,
+ };
+ }
+
case 'finishMonthSwitchingAnimation':
return {
...state,
@@ -149,7 +165,9 @@ export const useCalendarState = (
granularity: SECTION_TYPE_GRANULARITY.day,
});
},
- [], // eslint-disable-line react-hooks/exhaustive-deps
+ // We want the `referenceDate` to update on prop and `timezone` change (https://github.com/mui/mui-x/issues/10804)
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ [referenceDateProp, timezone],
);
const [calendarState, dispatch] = React.useReducer(reducerFn, {
@@ -159,6 +177,15 @@ export const useCalendarState = (
slideDirection: 'left',
});
+ // Ensure that `calendarState.currentMonth` timezone is updated when `referenceDate` (or timezone changes)
+ // https://github.com/mui/mui-x/issues/10804
+ React.useEffect(() => {
+ dispatch({
+ type: 'changeMonthTimezone',
+ newTimezone: utils.getTimezone(referenceDate),
+ });
+ }, [referenceDate, utils]);
+
const handleChangeMonth = React.useCallback(
(payload: ChangeMonthPayload) => {
dispatch({