I have created a custom date picker component based on Material UI and Formik, which passes a date value to a parent form component, as follows:
import React from 'react';
import { useField } from 'formik';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import enGB from 'date-fns/locale/en-GB';
import { parseISO } from 'date-fns';
const CustomDateField = ({ name, label, error, helperText, ...props }) => {
const [field, meta, helpers] = useField(name);
const locale = enGB;
const handleDateChange = (date) => {
console.log('date before changes',date)
//this if/else logic is redundant as MUI always passes the data as a date, not a string
if (typeof date === 'string') {
const parsedDate = parseISO(date);
console.log('parsedDate',parsedDate)
helpers.setValue(parsedDate);
} else {
helpers.setValue(date);
console.log('setValueDate',date)
}
};
return (
<div className="pb-4">
<LocalizationProvider dateAdapter={AdapterDateFns} adapterLocale={locale}>
<DatePicker
{...field}
{...props}
name={name}
label={label}
value={field.value || null}
onChange={handleDateChange}
sx={{
width: '100%',
}}
slotProps={{
textField: {
variant: 'outlined',
error: Boolean(meta.touched && meta.error), //error handling
helperText: meta.touched && meta.error ? meta.error : helperText,
},
}} //error handling
/>
</LocalizationProvider>
</div>
);
};
export default CustomDateField;
Running locally in my dev environment, the date selected in the date picker component is the date value that is passed to the parent component. However, when running this on an Azure app service the date passed to the parent component is one day earlier than the day selected in the component.
I've done some logging, and, as an example, when I selected the date (UK date format) 05/08/2023
, the output from the console log titled 'date before changes'
is:
Date Sat Aug 05 2023 00:00:00 GMT+0100 (British Summer Time)
However, by the time that is passed to the parent component, the date is one day prior:
"event_date":"2023-08-04T23:00:00.000Z"
.
I've done some searching on S.O. and found potentially related issues with time zones. I've tried changing the time-zone of my (Linux-based) Azure app server to UK time using the Azure Application Setting
config to TZ=Europe/London
which I believe matches my local PC's timezone of (UTC+00:00) Dublin, Edinburgh, Lisbon, London
and with (+1hr) summer-time difference enabled. This did not resolve the issue.
I also found some posts (e.g. link) about the JavaScript date
object causing the same symptom I'm experiencing, but whilst that symptom is similar, the fact that I have no issue when my code runs locally on my dev environment, but is an issue on Azure, suggested to me that this was probably not the cause of my issue.
Any ideas?
OK, worked this out. The issue is to do with timezones differing across local user PCs and the server - which is irritating, as time is an irrelevance in a date-picker...
The MUI date picker defaults a time of 00:00:00 whenever a date is selected. In my case, on a user's PC, it was being set as 00:00:00 GMT+1, But the server (somewhere, I'm not sure where) is set to GMT/UTC, so when the date was passed to the backend and database, 1 hour was being subtracted. Subtracting 1 hour from 00:00:00 moved the date into the previous day.
There are probably more elegant ways to fix this at a server timezone level, but as all my users are UK based, I just coded it to set a default time of 5am, so subtracting 1 hour will never result in a date change.
New code for anyone interested:
import React from 'react';
import { useField } from 'formik';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import enGB from 'date-fns/locale/en-GB';
import { setHours, setMinutes } from 'date-fns';
const CustomDateField = ({ name, label, error, helperText, ...props }) => {
const [field, meta, helpers] = useField(name);
const locale = enGB;
const handleDateChange = (date) => {
// Set the time to 5am, otherwise it defaults the time to 00:00:00, meaning during BST it will subtract 1 hour, which will result in a date change too.
const dateWithDefaultTime = setMinutes(setHours(date, 5), 0);
helpers.setValue(dateWithDefaultTime);
};
return (
<div className="pb-4">
<LocalizationProvider dateAdapter={AdapterDateFns} adapterLocale={locale}>
<DatePicker
{...field}
{...props}
name={name}
label={label}
value={field.value || null}
onChange={handleDateChange}
sx={{
width: '100%',
}}
slotProps={{
textField: {
variant: 'outlined',
error: Boolean(meta.touched && meta.error),
helperText: meta.touched && meta.error ? meta.error : helperText,
},
}}
/>
</LocalizationProvider>
</div>
);
};
export default CustomDateField;
///