Fix date picker wrong on certain timezones (#5972)
Timezone with a negative offset weren't working good with date pickers. I split the logic for display and parsing between date only and datetime. Date time is sending and displaying using timezone, and date only is sending and displaying by forcing the date to take its UTC day and month and 00:00:00 time. This way its consistent across all timezones.
This commit is contained in:
@ -121,6 +121,7 @@ export const FieldInput = ({
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onClear={onSubmit}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
) : isFieldDate(fieldDefinition) ? (
|
||||
<DateFieldInput
|
||||
@ -128,6 +129,7 @@ export const FieldInput = ({
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onClear={onSubmit}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
) : isFieldNumber(fieldDefinition) ? (
|
||||
<NumberFieldInput
|
||||
|
||||
@ -13,6 +13,7 @@ export type DateFieldInputProps = {
|
||||
onEnter?: FieldInputEvent;
|
||||
onEscape?: FieldInputEvent;
|
||||
onClear?: FieldInputEvent;
|
||||
onSubmit?: FieldInputEvent;
|
||||
};
|
||||
|
||||
export const DateFieldInput = ({
|
||||
@ -20,6 +21,7 @@ export const DateFieldInput = ({
|
||||
onEscape,
|
||||
onClickOutside,
|
||||
onClear,
|
||||
onSubmit,
|
||||
}: DateFieldInputProps) => {
|
||||
const { fieldValue, setDraftValue } = useDateField();
|
||||
|
||||
@ -39,6 +41,10 @@ export const DateFieldInput = ({
|
||||
onEnter?.(() => persistDate(newDate));
|
||||
};
|
||||
|
||||
const handleSubmit = (newDate: Nullable<Date>) => {
|
||||
onSubmit?.(() => persistDate(newDate));
|
||||
};
|
||||
|
||||
const handleEscape = (newDate: Nullable<Date>) => {
|
||||
onEscape?.(() => persistDate(newDate));
|
||||
};
|
||||
@ -69,6 +75,7 @@ export const DateFieldInput = ({
|
||||
clearable
|
||||
onChange={handleChange}
|
||||
onClear={handleClear}
|
||||
onSubmit={handleSubmit}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -12,6 +12,7 @@ export type DateTimeFieldInputProps = {
|
||||
onEnter?: FieldInputEvent;
|
||||
onEscape?: FieldInputEvent;
|
||||
onClear?: FieldInputEvent;
|
||||
onSubmit?: FieldInputEvent;
|
||||
};
|
||||
|
||||
export const DateTimeFieldInput = ({
|
||||
@ -19,6 +20,7 @@ export const DateTimeFieldInput = ({
|
||||
onEscape,
|
||||
onClickOutside,
|
||||
onClear,
|
||||
onSubmit,
|
||||
}: DateTimeFieldInputProps) => {
|
||||
const { fieldValue, setDraftValue } = useDateTimeField();
|
||||
|
||||
@ -57,6 +59,10 @@ export const DateTimeFieldInput = ({
|
||||
onClear?.(() => persistDate(null));
|
||||
};
|
||||
|
||||
const handleSubmit = (newDate: Nullable<Date>) => {
|
||||
onSubmit?.(() => persistDate(newDate));
|
||||
};
|
||||
|
||||
const dateValue = fieldValue ? new Date(fieldValue) : null;
|
||||
|
||||
return (
|
||||
@ -69,6 +75,7 @@ export const DateTimeFieldInput = ({
|
||||
onChange={handleChange}
|
||||
isDateTimeInput
|
||||
onClear={handleClear}
|
||||
onSubmit={handleSubmit}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -142,10 +142,9 @@ export const RecordTableCellContainer = ({
|
||||
[styles.cellBaseContainerSoftFocus]: hasSoftFocus,
|
||||
})}
|
||||
>
|
||||
{isInEditMode && (
|
||||
{isInEditMode ? (
|
||||
<RecordTableCellEditMode>{editModeContent}</RecordTableCellEditMode>
|
||||
)}
|
||||
{hasSoftFocus ? (
|
||||
) : hasSoftFocus ? (
|
||||
<RecordTableCellSoftFocusMode
|
||||
editModeContent={editModeContent}
|
||||
nonEditModeContent={nonEditModeContent}
|
||||
|
||||
@ -33,6 +33,7 @@ export type DateInputProps = {
|
||||
onChange?: (newDate: Nullable<Date>) => void;
|
||||
isDateTimeInput?: boolean;
|
||||
onClear?: () => void;
|
||||
onSubmit?: (newDate: Nullable<Date>) => void;
|
||||
};
|
||||
|
||||
export const DateInput = ({
|
||||
@ -44,6 +45,7 @@ export const DateInput = ({
|
||||
onChange,
|
||||
isDateTimeInput,
|
||||
onClear,
|
||||
onSubmit,
|
||||
}: DateInputProps) => {
|
||||
const [internalValue, setInternalValue] = useState(value);
|
||||
|
||||
@ -59,6 +61,11 @@ export const DateInput = ({
|
||||
onClear?.();
|
||||
};
|
||||
|
||||
const handleMouseSelect = (newDate: Date | null) => {
|
||||
setInternalValue(newDate);
|
||||
onSubmit?.(newDate);
|
||||
};
|
||||
|
||||
const { closeDropdown } = useDropdown(MONTH_AND_YEAR_DROPDOWN_ID);
|
||||
const { closeDropdown: closeDropdownMonthSelect } = useDropdown(
|
||||
MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID,
|
||||
@ -86,9 +93,7 @@ export const DateInput = ({
|
||||
<InternalDatePicker
|
||||
date={internalValue ?? new Date()}
|
||||
onChange={handleChange}
|
||||
onMouseSelect={(newDate: Date | null) => {
|
||||
onEnter(newDate);
|
||||
}}
|
||||
onMouseSelect={handleMouseSelect}
|
||||
clearable={clearable ? clearable : false}
|
||||
isDateTimeInput={isDateTimeInput}
|
||||
onEnter={onEnter}
|
||||
|
||||
@ -57,11 +57,25 @@ export const DateTimeInput = ({
|
||||
(date: any) => {
|
||||
const dateParsed = DateTime.fromJSDate(date);
|
||||
|
||||
const formattedDate = dateParsed.toFormat(parsingFormat);
|
||||
const dateWithoutTime = DateTime.fromJSDate(date)
|
||||
.toLocal()
|
||||
.set({
|
||||
day: date.getUTCDate(),
|
||||
month: date.getUTCMonth() + 1,
|
||||
year: date.getUTCFullYear(),
|
||||
hour: 0,
|
||||
minute: 0,
|
||||
second: 0,
|
||||
millisecond: 0,
|
||||
});
|
||||
|
||||
const formattedDate = isDateTimeInput
|
||||
? dateParsed.toFormat(parsingFormat)
|
||||
: dateWithoutTime.toFormat(parsingFormat);
|
||||
|
||||
return formattedDate;
|
||||
},
|
||||
[parsingFormat],
|
||||
[parsingFormat, isDateTimeInput],
|
||||
);
|
||||
|
||||
const parseStringToDate = (str: string) => {
|
||||
|
||||
@ -383,19 +383,35 @@ export const InternalDatePicker = ({
|
||||
onChange?.(newDate);
|
||||
};
|
||||
|
||||
const dateWithoutTime = DateTime.fromJSDate(date)
|
||||
.toLocal()
|
||||
.set({
|
||||
day: date.getUTCDate(),
|
||||
month: date.getUTCMonth() + 1,
|
||||
year: date.getUTCFullYear(),
|
||||
hour: 0,
|
||||
minute: 0,
|
||||
second: 0,
|
||||
millisecond: 0,
|
||||
})
|
||||
.toJSDate();
|
||||
|
||||
const dateToUse = isDateTimeInput ? date : dateWithoutTime;
|
||||
|
||||
return (
|
||||
<StyledContainer onKeyDown={handleKeyDown}>
|
||||
<div className={clearable ? 'clearable ' : ''}>
|
||||
<ReactDatePicker
|
||||
open={true}
|
||||
selected={internalDate}
|
||||
openToDate={internalDate}
|
||||
selected={dateToUse}
|
||||
openToDate={dateToUse}
|
||||
disabledKeyboardNavigation
|
||||
onChange={(newDate) => {
|
||||
onChange?.(newDate);
|
||||
}}
|
||||
customInput={
|
||||
<DateTimeInput
|
||||
date={internalDate}
|
||||
date={dateToUse}
|
||||
isDateTimeInput={isDateTimeInput}
|
||||
onChange={onChange}
|
||||
/>
|
||||
@ -424,13 +440,13 @@ export const InternalDatePicker = ({
|
||||
options={months}
|
||||
disableBlur
|
||||
onChange={handleChangeMonth}
|
||||
value={date.getMonth()}
|
||||
value={date.getUTCMonth()}
|
||||
fullWidth
|
||||
/>
|
||||
<Select
|
||||
dropdownId={MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID}
|
||||
onChange={handleChangeYear}
|
||||
value={date.getFullYear()}
|
||||
value={date.getUTCFullYear()}
|
||||
options={years}
|
||||
disableBlur
|
||||
fullWidth
|
||||
@ -450,16 +466,24 @@ export const InternalDatePicker = ({
|
||||
</StyledCustomDatePickerHeader>
|
||||
</>
|
||||
)}
|
||||
onSelect={(date: Date, event) => {
|
||||
const dateUTC = DateTime.fromJSDate(date, {
|
||||
zone: 'utc',
|
||||
}).toJSDate();
|
||||
onSelect={(date: Date) => {
|
||||
const dateParsedWithoutTime = DateTime.fromObject(
|
||||
{
|
||||
day: date.getDate(),
|
||||
month: date.getMonth() + 1,
|
||||
year: date.getFullYear(),
|
||||
hour: 0,
|
||||
minute: 0,
|
||||
second: 0,
|
||||
},
|
||||
{ zone: 'utc' },
|
||||
).toJSDate();
|
||||
|
||||
if (event?.type === 'click') {
|
||||
handleMouseSelect?.(dateUTC);
|
||||
} else {
|
||||
onChange?.(dateUTC);
|
||||
}
|
||||
const dateForUpdate = isDateTimeInput
|
||||
? date
|
||||
: dateParsedWithoutTime;
|
||||
|
||||
handleMouseSelect?.(dateForUpdate);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user