Search code examples
javascriptreactjsmaterial-uidatepickerdatetimepicker

How do I set a custom time zone with MUI date picker and dayjs?


I have an application where a user's time zone is provided by the server, not utilized via browser built-in methods.

Therefore, when I get the unix stamp from the server and pass it to the date picker, it displays the "wrong" date because the date picker uses the browser time zone settings, not the one I get from the server. I use dayjs and adapterdayjs for time conversions.

<LocalizationProvider dateAdapter={AdapterDayjs}>
 <DatePicker />
</LocalizationProvider>

I tried to set a default time zone with dayjs:

 dayjs.extend(utc);
  dayjs.extend(timezone);

  dayjs.tz.setDefault('Asia/Makassar');

But it's not affecting the MUI Date Picker itself. Is there a way to override the browser time zone settings and plug in server-side provided values with AdapterDayjs, dayjs and MUI Date Picker?

Edit:

So I figured out how to do it...but the calendar is slow now.

I set the default time zone like this:

  dayjs.extend(utc);
  dayjs.extend(timezone);

  dayjs.tz.setDefault('Asia/Makassar');

Then passed dayjs.tz as the dateLibInstance param of LocalizationProvider, so now every time any dayjs method is called, it's done via dayjs.tz

 <LocalizationProvider
        dateLibInstance={dayjs.tz}
        dateAdapter={AdapterDayjs}>
        <DatePicker />
      </LocalizationProvider>

Only problem now is that the calendar is really slow.


Solution

  • If your solution is laggy, why don't you just convert the unix stamp into the relevant timezone before storing it in state and providing it to the datepicker? Do this based on the timezone returned for the user from the server.

    Parse a unix stamp based on timezone frmo dayjs: dayjs(unixStamp).tz(timezoneString)

    This example sandbox shows two Datepickers to simulate how the date would displayed for a user in Toronto vs a user in Sydney. You can see how the same unix stamp shows up 31/12/2021 for the Toronto user but 01/01/2022 for the Sydney user.

    https://codesandbox.io/s/mui-5-forked-8hznux?file=/src/ArrowPopper.js:0-2022

    import * as React from "react";
    import dayjs from "dayjs";
    import TextField from "@mui/material/TextField";
    import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
    import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
    import { DatePicker } from "@mui/x-date-pickers/DatePicker";
    import utc from "dayjs/plugin/utc";
    import timezone from "dayjs/plugin/timezone";
    dayjs.extend(utc);
    dayjs.extend(timezone);
    
    export default function BasicDatePicker() {
      const [torontoUser, setTorontoUser] = React.useState(null);
      const [sydneyUser, setSydneyUser] = React.useState(null);
    
      React.useEffect(() => {
        const getServerData = async () => {
          // Simulate if the user was from Toronto
          const dataFromServerToronto = {
            unix: 1640959201000,
            timezone: "America/Toronto"
          };
          const datetimeToronto = dayjs(dataFromServerToronto.unix).tz(
            dataFromServerToronto.timezone
          );
          setTorontoUser(datetimeToronto);
    
          // Simulate if the use was from Sydney
          const dataFromServerSydney = {
            unix: 1640959201000, // same Unix stamp as above
            timezone: "Australia/Sydney"
          };
          const datetimeSydney = dayjs(dataFromServerSydney.unix).tz(
            dataFromServerSydney.timezone
          );
          setSydneyUser(datetimeSydney);
        };
        void getServerData();
      }, []);
    
      return (
        <LocalizationProvider dateAdapter={AdapterDayjs}>
          <DatePicker
            label="User From Toronto"
            value={torontoUser}
            onChange={(newValue) => {
              setTorontoUser(newValue);
            }}
            renderInput={(params) => (
              <TextField
                sx={{
                  mb: 2
                }}
                {...params}
              />
            )}
          />
          <DatePicker
            label="User From Sydney"
            value={sydneyUser}
            onChange={(newValue) => {
              setSydneyUser(newValue);
            }}
            renderInput={(params) => <TextField {...params} />}
          />
        </LocalizationProvider>
      );
    }