Feat: add support for day-first and year-first date formats (DD/MM/YYYY, YYYY/MM/DD) (#12333)
Closes #12152 https://github.com/user-attachments/assets/53640777-578f-4de8-a1f8-52d409a7582d --------- Co-authored-by: etiennejouan <jouan.etienne@gmail.com>
This commit is contained in:
@ -11,23 +11,14 @@ import {
|
|||||||
} from '@/ui/input/components/internal/date/components/InternalDatePicker';
|
} from '@/ui/input/components/internal/date/components/InternalDatePicker';
|
||||||
import { MAX_DATE } from '@/ui/input/components/internal/date/constants/MaxDate';
|
import { MAX_DATE } from '@/ui/input/components/internal/date/constants/MaxDate';
|
||||||
import { MIN_DATE } from '@/ui/input/components/internal/date/constants/MinDate';
|
import { MIN_DATE } from '@/ui/input/components/internal/date/constants/MinDate';
|
||||||
import { parseDateToString } from '@/ui/input/components/internal/date/utils/parseDateToString';
|
import { useDateParser } from '@/ui/input/components/internal/hooks/useDateParser';
|
||||||
import { parseStringToDate } from '@/ui/input/components/internal/date/utils/parseStringToDate';
|
|
||||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||||
import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
|
import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
|
||||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||||
import { UserContext } from '@/users/contexts/UserContext';
|
|
||||||
import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString';
|
import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString';
|
||||||
import { css } from '@emotion/react';
|
import { css } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import {
|
import { ChangeEvent, KeyboardEvent, useId, useRef, useState } from 'react';
|
||||||
ChangeEvent,
|
|
||||||
KeyboardEvent,
|
|
||||||
useContext,
|
|
||||||
useId,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
import { TEXT_INPUT_STYLE } from 'twenty-ui/theme';
|
import { TEXT_INPUT_STYLE } from 'twenty-ui/theme';
|
||||||
import { Nullable } from 'twenty-ui/utilities';
|
import { Nullable } from 'twenty-ui/utilities';
|
||||||
@ -93,7 +84,9 @@ export const FormDateTimeFieldInput = ({
|
|||||||
readonly,
|
readonly,
|
||||||
placeholder,
|
placeholder,
|
||||||
}: FormDateTimeFieldInputProps) => {
|
}: FormDateTimeFieldInputProps) => {
|
||||||
const { timeZone } = useContext(UserContext);
|
const { parseToString, parseToDate } = useDateParser({
|
||||||
|
isDateTimeInput: !dateOnly,
|
||||||
|
});
|
||||||
|
|
||||||
const inputId = useId();
|
const inputId = useId();
|
||||||
|
|
||||||
@ -121,11 +114,7 @@ export const FormDateTimeFieldInput = ({
|
|||||||
|
|
||||||
const [inputDateTime, setInputDateTime] = useState(
|
const [inputDateTime, setInputDateTime] = useState(
|
||||||
isDefined(draftValueAsDate) && !isStandaloneVariableString(defaultValue)
|
isDefined(draftValueAsDate) && !isStandaloneVariableString(defaultValue)
|
||||||
? parseDateToString({
|
? parseToString(draftValueAsDate)
|
||||||
date: draftValueAsDate,
|
|
||||||
isDateTimeInput: !dateOnly,
|
|
||||||
userTimezone: timeZone,
|
|
||||||
})
|
|
||||||
: '',
|
: '',
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -172,15 +161,7 @@ export const FormDateTimeFieldInput = ({
|
|||||||
value: newDate?.toDateString() ?? null,
|
value: newDate?.toDateString() ?? null,
|
||||||
});
|
});
|
||||||
|
|
||||||
setInputDateTime(
|
setInputDateTime(isDefined(newDate) ? parseToString(newDate) : '');
|
||||||
isDefined(newDate)
|
|
||||||
? parseDateToString({
|
|
||||||
date: newDate,
|
|
||||||
isDateTimeInput: !dateOnly,
|
|
||||||
userTimezone: timeZone,
|
|
||||||
})
|
|
||||||
: '',
|
|
||||||
);
|
|
||||||
|
|
||||||
setPickerDate(newDate);
|
setPickerDate(newDate);
|
||||||
|
|
||||||
@ -230,15 +211,7 @@ export const FormDateTimeFieldInput = ({
|
|||||||
|
|
||||||
setPickerDate(newDate);
|
setPickerDate(newDate);
|
||||||
|
|
||||||
setInputDateTime(
|
setInputDateTime(isDefined(newDate) ? parseToString(newDate) : '');
|
||||||
isDefined(newDate)
|
|
||||||
? parseDateToString({
|
|
||||||
date: newDate,
|
|
||||||
isDateTimeInput: !dateOnly,
|
|
||||||
userTimezone: timeZone,
|
|
||||||
})
|
|
||||||
: '',
|
|
||||||
);
|
|
||||||
|
|
||||||
persistDate(newDate);
|
persistDate(newDate);
|
||||||
};
|
};
|
||||||
@ -264,15 +237,10 @@ export const FormDateTimeFieldInput = ({
|
|||||||
|
|
||||||
if (inputDateTimeTrimmed === '') {
|
if (inputDateTimeTrimmed === '') {
|
||||||
handlePickerClear();
|
handlePickerClear();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedInputDateTime = parseStringToDate({
|
const parsedInputDateTime = parseToDate(inputDateTimeTrimmed);
|
||||||
dateAsString: inputDateTimeTrimmed,
|
|
||||||
isDateTimeInput: !dateOnly,
|
|
||||||
userTimezone: timeZone,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!isDefined(parsedInputDateTime)) {
|
if (!isDefined(parsedInputDateTime)) {
|
||||||
return;
|
return;
|
||||||
@ -293,13 +261,7 @@ export const FormDateTimeFieldInput = ({
|
|||||||
|
|
||||||
setPickerDate(validatedDate);
|
setPickerDate(validatedDate);
|
||||||
|
|
||||||
setInputDateTime(
|
setInputDateTime(parseToString(validatedDate));
|
||||||
parseDateToString({
|
|
||||||
date: validatedDate,
|
|
||||||
isDateTimeInput: !dateOnly,
|
|
||||||
userTimezone: timeZone,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
persistDate(validatedDate);
|
persistDate(validatedDate);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,12 +5,12 @@ import { Select } from '@/ui/input/components/Select';
|
|||||||
import { DateTimeInput } from '@/ui/input/components/internal/date/components/DateTimeInput';
|
import { DateTimeInput } from '@/ui/input/components/internal/date/components/DateTimeInput';
|
||||||
|
|
||||||
import { getMonthSelectOptions } from '@/ui/input/components/internal/date/utils/getMonthSelectOptions';
|
import { getMonthSelectOptions } from '@/ui/input/components/internal/date/utils/getMonthSelectOptions';
|
||||||
|
import { IconChevronLeft, IconChevronRight } from 'twenty-ui/display';
|
||||||
|
import { LightIconButton } from 'twenty-ui/input';
|
||||||
import {
|
import {
|
||||||
MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID,
|
MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID,
|
||||||
MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID,
|
MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID,
|
||||||
} from './InternalDatePicker';
|
} from './InternalDatePicker';
|
||||||
import { IconChevronLeft, IconChevronRight } from 'twenty-ui/display';
|
|
||||||
import { LightIconButton } from 'twenty-ui/input';
|
|
||||||
|
|
||||||
const StyledCustomDatePickerHeader = styled.div`
|
const StyledCustomDatePickerHeader = styled.div`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -38,7 +38,6 @@ type AbsoluteDatePickerHeaderProps = {
|
|||||||
prevMonthButtonDisabled: boolean;
|
prevMonthButtonDisabled: boolean;
|
||||||
nextMonthButtonDisabled: boolean;
|
nextMonthButtonDisabled: boolean;
|
||||||
isDateTimeInput?: boolean;
|
isDateTimeInput?: boolean;
|
||||||
timeZone: string;
|
|
||||||
hideInput?: boolean;
|
hideInput?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -52,7 +51,6 @@ export const AbsoluteDatePickerHeader = ({
|
|||||||
prevMonthButtonDisabled,
|
prevMonthButtonDisabled,
|
||||||
nextMonthButtonDisabled,
|
nextMonthButtonDisabled,
|
||||||
isDateTimeInput,
|
isDateTimeInput,
|
||||||
timeZone,
|
|
||||||
hideInput = false,
|
hideInput = false,
|
||||||
}: AbsoluteDatePickerHeaderProps) => {
|
}: AbsoluteDatePickerHeaderProps) => {
|
||||||
const endOfDayDateTimeInLocalTimezone = DateTime.now().set({
|
const endOfDayDateTimeInLocalTimezone = DateTime.now().set({
|
||||||
@ -74,7 +72,6 @@ export const AbsoluteDatePickerHeader = ({
|
|||||||
date={date}
|
date={date}
|
||||||
isDateTimeInput={isDateTimeInput}
|
isDateTimeInput={isDateTimeInput}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
userTimezone={timeZone}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@ -1,18 +1,19 @@
|
|||||||
import { css } from '@emotion/react';
|
import { css } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useIMask } from 'react-imask';
|
import { useIMask } from 'react-imask';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { dateTimeFormatState } from '@/localization/states/dateTimeFormatState';
|
||||||
import { DATE_BLOCKS } from '@/ui/input/components/internal/date/constants/DateBlocks';
|
import { DATE_BLOCKS } from '@/ui/input/components/internal/date/constants/DateBlocks';
|
||||||
import { DATE_MASK } from '@/ui/input/components/internal/date/constants/DateMask';
|
|
||||||
import { DATE_TIME_BLOCKS } from '@/ui/input/components/internal/date/constants/DateTimeBlocks';
|
import { DATE_TIME_BLOCKS } from '@/ui/input/components/internal/date/constants/DateTimeBlocks';
|
||||||
import { DATE_TIME_MASK } from '@/ui/input/components/internal/date/constants/DateTimeMask';
|
|
||||||
import { MAX_DATE } from '@/ui/input/components/internal/date/constants/MaxDate';
|
import { MAX_DATE } from '@/ui/input/components/internal/date/constants/MaxDate';
|
||||||
import { MIN_DATE } from '@/ui/input/components/internal/date/constants/MinDate';
|
import { MIN_DATE } from '@/ui/input/components/internal/date/constants/MinDate';
|
||||||
import { parseDateToString } from '@/ui/input/components/internal/date/utils/parseDateToString';
|
import { getDateMask } from '@/ui/input/components/internal/date/utils/getDateMask';
|
||||||
import { parseStringToDate } from '@/ui/input/components/internal/date/utils/parseStringToDate';
|
import { getDateTimeMask } from '@/ui/input/components/internal/date/utils/getDateTimeMask';
|
||||||
import { isNull } from '@sniptt/guards';
|
import { isNull } from '@sniptt/guards';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
import { useDateParser } from '../../hooks/useDateParser';
|
||||||
|
|
||||||
const StyledInputContainer = styled.div`
|
const StyledInputContainer = styled.div`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -44,42 +45,30 @@ type DateTimeInputProps = {
|
|||||||
onChange?: (date: Date | null) => void;
|
onChange?: (date: Date | null) => void;
|
||||||
date: Date | null;
|
date: Date | null;
|
||||||
isDateTimeInput?: boolean;
|
isDateTimeInput?: boolean;
|
||||||
userTimezone?: string;
|
|
||||||
onError?: (error: Error) => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DateTimeInput = ({
|
export const DateTimeInput = ({
|
||||||
date,
|
date,
|
||||||
onChange,
|
onChange,
|
||||||
isDateTimeInput,
|
isDateTimeInput,
|
||||||
userTimezone,
|
|
||||||
}: DateTimeInputProps) => {
|
}: DateTimeInputProps) => {
|
||||||
const [hasError, setHasError] = useState(false);
|
const [hasError, setHasError] = useState(false);
|
||||||
|
const { dateFormat } = useRecoilValue(dateTimeFormatState);
|
||||||
const handleParseDateToString = useCallback(
|
const { parseToString, parseToDate } = useDateParser({
|
||||||
(date: any) => {
|
isDateTimeInput: isDateTimeInput === true,
|
||||||
return parseDateToString({
|
});
|
||||||
date,
|
|
||||||
isDateTimeInput: isDateTimeInput === true,
|
|
||||||
userTimezone,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[isDateTimeInput, userTimezone],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleParseStringToDate = (str: string) => {
|
const handleParseStringToDate = (str: string) => {
|
||||||
const date = parseStringToDate({
|
const date = parseToDate(str);
|
||||||
dateAsString: str,
|
|
||||||
isDateTimeInput: isDateTimeInput === true,
|
|
||||||
userTimezone,
|
|
||||||
});
|
|
||||||
|
|
||||||
setHasError(isNull(date) === true);
|
setHasError(isNull(date) === true);
|
||||||
|
|
||||||
return date;
|
return date;
|
||||||
};
|
};
|
||||||
|
|
||||||
const pattern = isDateTimeInput ? DATE_TIME_MASK : DATE_MASK;
|
const pattern = isDateTimeInput
|
||||||
|
? getDateTimeMask(dateFormat)
|
||||||
|
: getDateMask(dateFormat);
|
||||||
const blocks = isDateTimeInput ? DATE_TIME_BLOCKS : DATE_BLOCKS;
|
const blocks = isDateTimeInput ? DATE_TIME_BLOCKS : DATE_BLOCKS;
|
||||||
|
|
||||||
const { ref, setValue, value } = useIMask(
|
const { ref, setValue, value } = useIMask(
|
||||||
@ -89,18 +78,14 @@ export const DateTimeInput = ({
|
|||||||
blocks,
|
blocks,
|
||||||
min: MIN_DATE,
|
min: MIN_DATE,
|
||||||
max: MAX_DATE,
|
max: MAX_DATE,
|
||||||
format: handleParseDateToString,
|
format: (date: any) => parseToString(date),
|
||||||
parse: handleParseStringToDate,
|
parse: handleParseStringToDate,
|
||||||
lazy: false,
|
lazy: false,
|
||||||
autofix: true,
|
autofix: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onComplete: (value) => {
|
onComplete: (value) => {
|
||||||
const parsedDate = parseStringToDate({
|
const parsedDate = parseToDate(value);
|
||||||
dateAsString: value,
|
|
||||||
isDateTimeInput: isDateTimeInput === true,
|
|
||||||
userTimezone,
|
|
||||||
});
|
|
||||||
|
|
||||||
onChange?.(parsedDate);
|
onChange?.(parsedDate);
|
||||||
},
|
},
|
||||||
@ -115,23 +100,14 @@ export const DateTimeInput = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setValue(
|
setValue(parseToString(date));
|
||||||
parseDateToString({
|
}, [date, setValue, parseToString]);
|
||||||
date: date,
|
|
||||||
isDateTimeInput: isDateTimeInput === true,
|
|
||||||
userTimezone,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}, [date, setValue, isDateTimeInput, userTimezone]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledInputContainer>
|
<StyledInputContainer>
|
||||||
<StyledInput
|
<StyledInput
|
||||||
type="text"
|
type="text"
|
||||||
ref={ref as any}
|
ref={ref as any}
|
||||||
placeholder={`Type date${
|
|
||||||
isDateTimeInput ? ' and time' : ' (mm/dd/yyyy)'
|
|
||||||
}`}
|
|
||||||
value={value}
|
value={value}
|
||||||
onChange={() => {}} // Prevent React warning
|
onChange={() => {}} // Prevent React warning
|
||||||
hasError={hasError}
|
hasError={hasError}
|
||||||
|
|||||||
@ -511,7 +511,6 @@ export const DateTimePicker = ({
|
|||||||
date={internalDate}
|
date={internalDate}
|
||||||
isDateTimeInput={isDateTimeInput}
|
isDateTimeInput={isDateTimeInput}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
userTimezone={timeZone}
|
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
renderCustomHeader={({
|
renderCustomHeader={({
|
||||||
@ -536,7 +535,6 @@ export const DateTimePicker = ({
|
|||||||
prevMonthButtonDisabled={prevMonthButtonDisabled}
|
prevMonthButtonDisabled={prevMonthButtonDisabled}
|
||||||
nextMonthButtonDisabled={nextMonthButtonDisabled}
|
nextMonthButtonDisabled={nextMonthButtonDisabled}
|
||||||
isDateTimeInput={isDateTimeInput}
|
isDateTimeInput={isDateTimeInput}
|
||||||
timeZone={timeZone}
|
|
||||||
hideInput={hideHeaderInput}
|
hideInput={hideHeaderInput}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
export const DATE_MASK = 'm`/d`/Y`'; // See https://imask.js.org/guide.html#masked-date
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export const DATE_PARSER_FORMAT = 'MM/dd/yyyy';
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
import { TIME_MASK } from '@/ui/input/components/internal/date/constants/TimeMask';
|
|
||||||
|
|
||||||
export const DATE_TIME_MASK = `m\`/d\`/Y\` ${TIME_MASK}`;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export const DATE_TIME_PARSER_FORMAT = 'MM/dd/yyyy HH:mm';
|
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { DateFormat } from '~/modules/localization/constants/DateFormat';
|
||||||
|
|
||||||
|
export const getDateMask = (dateFormat: DateFormat): string => {
|
||||||
|
switch (dateFormat) {
|
||||||
|
case DateFormat.DAY_FIRST:
|
||||||
|
return 'd`/m`/Y`';
|
||||||
|
case DateFormat.YEAR_FIRST:
|
||||||
|
return 'Y`-m`-d`';
|
||||||
|
case DateFormat.MONTH_FIRST:
|
||||||
|
default:
|
||||||
|
return 'm`/d`/Y`';
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import { TIME_MASK } from '@/ui/input/components/internal/date/constants/TimeMask';
|
||||||
|
|
||||||
|
import { DateFormat } from '@/localization/constants/DateFormat';
|
||||||
|
import { getDateMask } from './getDateMask';
|
||||||
|
|
||||||
|
export const getDateTimeMask = (dateFormat: DateFormat): string => {
|
||||||
|
return `${getDateMask(dateFormat)} ${TIME_MASK}`;
|
||||||
|
};
|
||||||
@ -1,21 +1,21 @@
|
|||||||
import { DATE_PARSER_FORMAT } from '@/ui/input/components/internal/date/constants/DateParserFormat';
|
import { DateFormat } from '@/localization/constants/DateFormat';
|
||||||
import { DATE_TIME_PARSER_FORMAT } from '@/ui/input/components/internal/date/constants/DateTimeParserFormat';
|
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
|
import { getDateFormatString } from '~/utils/date-utils';
|
||||||
|
|
||||||
type ParseDateToStringArgs = {
|
type ParseDateToStringArgs = {
|
||||||
date: Date;
|
date: Date;
|
||||||
isDateTimeInput: boolean;
|
isDateTimeInput: boolean;
|
||||||
userTimezone: string | undefined;
|
userTimezone: string | undefined;
|
||||||
|
dateFormat?: DateFormat;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const parseDateToString = ({
|
export const parseDateToString = ({
|
||||||
date,
|
date,
|
||||||
isDateTimeInput,
|
isDateTimeInput,
|
||||||
userTimezone,
|
userTimezone,
|
||||||
|
dateFormat = DateFormat.MONTH_FIRST,
|
||||||
}: ParseDateToStringArgs) => {
|
}: ParseDateToStringArgs) => {
|
||||||
const parsingFormat = isDateTimeInput
|
const parsingFormat = getDateFormatString(dateFormat, isDateTimeInput);
|
||||||
? DATE_TIME_PARSER_FORMAT
|
|
||||||
: DATE_PARSER_FORMAT;
|
|
||||||
|
|
||||||
const dateParsed = DateTime.fromJSDate(date, { zone: userTimezone });
|
const dateParsed = DateTime.fromJSDate(date, { zone: userTimezone });
|
||||||
|
|
||||||
|
|||||||
@ -1,21 +1,21 @@
|
|||||||
import { DATE_PARSER_FORMAT } from '@/ui/input/components/internal/date/constants/DateParserFormat';
|
import { DateFormat } from '@/localization/constants/DateFormat';
|
||||||
import { DATE_TIME_PARSER_FORMAT } from '@/ui/input/components/internal/date/constants/DateTimeParserFormat';
|
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
|
import { getDateFormatString } from '~/utils/date-utils';
|
||||||
|
|
||||||
type ParseStringToDateArgs = {
|
type ParseStringToDateArgs = {
|
||||||
dateAsString: string;
|
dateAsString: string;
|
||||||
isDateTimeInput: boolean;
|
isDateTimeInput: boolean;
|
||||||
userTimezone: string | undefined;
|
userTimezone: string | undefined;
|
||||||
|
dateFormat: DateFormat;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const parseStringToDate = ({
|
export const parseStringToDate = ({
|
||||||
dateAsString,
|
dateAsString,
|
||||||
isDateTimeInput,
|
isDateTimeInput,
|
||||||
userTimezone,
|
userTimezone,
|
||||||
|
dateFormat,
|
||||||
}: ParseStringToDateArgs) => {
|
}: ParseStringToDateArgs) => {
|
||||||
const parsingFormat = isDateTimeInput
|
const parsingFormat = getDateFormatString(dateFormat, isDateTimeInput);
|
||||||
? DATE_TIME_PARSER_FORMAT
|
|
||||||
: DATE_PARSER_FORMAT;
|
|
||||||
|
|
||||||
const parsedDate = isDateTimeInput
|
const parsedDate = isDateTimeInput
|
||||||
? DateTime.fromFormat(dateAsString, parsingFormat, { zone: userTimezone })
|
? DateTime.fromFormat(dateAsString, parsingFormat, { zone: userTimezone })
|
||||||
|
|||||||
@ -0,0 +1,44 @@
|
|||||||
|
import { dateTimeFormatState } from '@/localization/states/dateTimeFormatState';
|
||||||
|
import { UserContext } from '@/users/contexts/UserContext';
|
||||||
|
import { useCallback, useContext } from 'react';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { parseDateToString } from '../date/utils/parseDateToString';
|
||||||
|
import { parseStringToDate } from '../date/utils/parseStringToDate';
|
||||||
|
|
||||||
|
type UseDateParserProps = {
|
||||||
|
isDateTimeInput: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useDateParser = ({ isDateTimeInput }: UseDateParserProps) => {
|
||||||
|
const { dateFormat } = useRecoilValue(dateTimeFormatState);
|
||||||
|
const { timeZone } = useContext(UserContext);
|
||||||
|
|
||||||
|
const parseToString = useCallback(
|
||||||
|
(date: Date) => {
|
||||||
|
return parseDateToString({
|
||||||
|
date,
|
||||||
|
isDateTimeInput,
|
||||||
|
userTimezone: timeZone,
|
||||||
|
dateFormat,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[dateFormat, isDateTimeInput, timeZone],
|
||||||
|
);
|
||||||
|
|
||||||
|
const parseToDate = useCallback(
|
||||||
|
(dateAsString: string) => {
|
||||||
|
return parseStringToDate({
|
||||||
|
dateAsString,
|
||||||
|
isDateTimeInput,
|
||||||
|
userTimezone: timeZone,
|
||||||
|
dateFormat,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[dateFormat, isDateTimeInput, timeZone],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
parseToString,
|
||||||
|
parseToDate,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -4,9 +4,11 @@ import { differenceInCalendarDays, formatDistanceToNow } from 'date-fns';
|
|||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import moize from 'moize';
|
import moize from 'moize';
|
||||||
|
|
||||||
import { logError } from './logError';
|
import { DateFormat } from '@/localization/constants/DateFormat';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
|
||||||
|
import { logError } from './logError';
|
||||||
|
|
||||||
export const DEFAULT_DATE_LOCALE = 'en-EN';
|
export const DEFAULT_DATE_LOCALE = 'en-EN';
|
||||||
|
|
||||||
export const parseDate = (dateToParse: Date | string | number) => {
|
export const parseDate = (dateToParse: Date | string | number) => {
|
||||||
@ -212,3 +214,20 @@ export const formatToHumanReadableDateTime = (date: Date | string) => {
|
|||||||
minute: 'numeric',
|
minute: 'numeric',
|
||||||
}).format(parsedJSDate);
|
}).format(parsedJSDate);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getDateFormatString = (
|
||||||
|
dateFormat: DateFormat,
|
||||||
|
isDateTimeInput: boolean,
|
||||||
|
): string => {
|
||||||
|
const timePart = isDateTimeInput ? ' HH:mm' : '';
|
||||||
|
|
||||||
|
switch (dateFormat) {
|
||||||
|
case DateFormat.DAY_FIRST:
|
||||||
|
return `dd/MM/yyyy${timePart}`;
|
||||||
|
case DateFormat.YEAR_FIRST:
|
||||||
|
return `yyyy-MM-dd${timePart}`;
|
||||||
|
case DateFormat.MONTH_FIRST:
|
||||||
|
default:
|
||||||
|
return `MM/dd/yyyy${timePart}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user