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..760f9275f3d6
--- /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 64bfdc0b1e8e..ee69bf230153 100644
--- a/packages/x-date-pickers/src/DateCalendar/DayCalendar.tsx
+++ b/packages/x-date-pickers/src/DateCalendar/DayCalendar.tsx
@@ -533,9 +533,8 @@ export function DayCalendar(inProps: DayCalendarProps) {
]);
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(
@@ -552,7 +551,7 @@ export function DayCalendar(inProps: DayCalendarProps) {
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 261af345fe21..43b4ba04ebff 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();
+
+ userEvent.mousePress(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('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);
+
+ userEvent.mousePress(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 440fa628f4dd..3ae9ee25deb6 100644
--- a/packages/x-date-pickers/src/DateCalendar/useCalendarState.tsx
+++ b/packages/x-date-pickers/src/DateCalendar/useCalendarState.tsx
@@ -42,6 +42,7 @@ export const createCalendarStateReducer =
action:
| ReducerAction<'finishMonthSwitchingAnimation'>
| ReducerAction<'changeMonth', ChangeMonthPayload>
+ | ReducerAction<'changeMonthTimezone', { newTimezone: string }>
| ReducerAction<'changeFocusedDay', ChangeFocusedDayPayload>,
): CalendarState => {
switch (action.type) {
@@ -53,6 +54,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,
@@ -156,7 +172,9 @@ export const useCalendarState = (params: 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, {
@@ -166,6 +184,15 @@ export const useCalendarState = (params: 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({