Cake

Timezone prop not working in MUI_

Fixing your DateTimePicker when it won't follow the timezone prop

more random bugs

Quick one here, talking about a minor thing I did at work for a form which displays a <DateTimePicker /> component from the React @mui/x-date-pickers library.

Specifically I was trying to use the timezone prop to auto display the date in a specific timezone. I found changing the prop did nothing, even when I'd added a localization provider above it. (REMEMBER TO ADD A LOCALIZATION PROVIDER!!!!)

I'll quickly explain what I did wrong...

The code I was using

Here's a minimal reproduction of the code I was using, it had a LocalizationProvider and TimePicker from MUI, and the AdapaterDateFns to work with our projects date-fns dependency.


don't use this.
jsx
1import { DateTimePicker, LocalizationProvider } from '@mui/x-date-pickers'
2import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'
3
4const EXAMPLE_TIMEZONES = [
5    'Australia/Brisbane',
6    'America/New_York',
7    'America/Los_Angeles',
8    'Europe/London',
9    'Europe/Paris',
10]
11
12const exampleDate = new Date()
13
14export default function MyComponent() {
15    const [exampleTzIndex, setExampleTzIndex] = useState(0)
16
17    const handleTimezoneChange = (event) => {
18        // Loop our example TZ's
19        setExampleTzIndex((prev) => (prev + 1) % EXAMPLE_TIMEZONES.length)
20    }
21
22    // Example timezone
23    const exampleTz = EXAMPLE_TIMEZONES[exampleTzIndex]
24
25    return (
26        <LocalizationProvider dateAdapter={AdapterDateFns}>
27            <TimePicker
28                timezone={exampleTz}
29                value={exampleDate}
30                label={exampleTz}
31            />
32            <div>
33                <button onClick={handleTimezoneChange}>
34                    Change Timezone
35                </button>
36            </div>
37        </LocalizationProvider>
38    )
39}

An easy little component that will cycle endlessly through our example timezones when clicked.

Can you already see what I've done wrong?

It's painfully obvious now.

We should see the input change its timezone when we click the button, but we don't. What's wrong with this code?

We're using AdapterDateFns in our LocalizationProvider, date-fns does not support timezones by default.

The MUI docs do actually explicitly include this caution on the subpage about Localization and Timezone. The info can be easy to miss in my opinion.

At least, I missed it.

Maybe a console warn would be helpful if the adapter doesn't support timezones when the prop is used?

The solution

I had to add the dayjs library to the project, and then use the AdapterDayjs for this single input.

Let's look at that code again...


use this one.
jsx
1import { DateTimePicker, LocalizationProvider } from '@mui/x-date-pickers'
2import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'
3import dayjs, { Dayjs } from 'dayjs'
4import utc from 'dayjs/plugin/utc'
5import timezone from 'dayjs/plugin/timezone'
6
7// We need to now "extend" DayJS with its TZ support
8dayjs.extend(utc)
9dayjs.extend(timezone)
10
11const EXAMPLE_TIMEZONES = [
12    'Australia/Brisbane',
13    'America/New_York',
14    'America/Los_Angeles',
15    'Europe/London',
16    'Europe/Paris',
17]
18
19// Initialize our Date as a DayJS object
20const exampleDate = dayjs.utc(new Date())
21
22export default function MyComponent() {
23    const [exampleTzIndex, setExampleTzIndex] = useState(0)
24
25    const handleTimezoneChange = (event) => {
26        // Loop our example TZ's
27        setExampleTzIndex((prev) => (prev + 1) % EXAMPLE_TIMEZONES.length)
28    }
29
30    // Example timezone
31    const exampleTz = EXAMPLE_TIMEZONES[exampleTzIndex]
32
33    return (
34        <LocalizationProvider dateAdapter={AdapterDateFns}>
35            <TimePicker
36                timezone={exampleTz}
37                value={exampleDate}
38                label={exampleTz}
39                onChange={(d: Dayjs | null) => {
40                    if (!d) {
41                        // Could be null, bail out
42                        return
43                    }
44
45                    console.log('You changed the date to', d)
46              }}
47            />
48            <div>
49                <button onClick={handleTimezoneChange}>
50                    Change Timezone
51                </button>
52            </div>
53        </LocalizationProvider>
54    )
55}

Everything should work as expected now, if required you can transform your DayJs object back to a JS Date object with d.toDate().

Blew an hour on this one, hope it helps someone else!

You were lucky reader number 594 🎉