diff --git a/spiffworkflow-frontend/src/rjsf/carbon_theme/BaseInputTemplate/BaseInputTemplate.tsx b/spiffworkflow-frontend/src/rjsf/carbon_theme/BaseInputTemplate/BaseInputTemplate.tsx index 874b3b61..4ecbb0dd 100644 --- a/spiffworkflow-frontend/src/rjsf/carbon_theme/BaseInputTemplate/BaseInputTemplate.tsx +++ b/spiffworkflow-frontend/src/rjsf/carbon_theme/BaseInputTemplate/BaseInputTemplate.tsx @@ -6,10 +6,15 @@ import { StrictRJSFSchema, WidgetProps, } from '@rjsf/utils'; +import { parse } from 'date-fns'; import { useCallback } from 'react'; import { useDebouncedCallback } from 'use-debounce'; -import { DATE_FORMAT_CARBON, DATE_FORMAT_FOR_DISPLAY } from '../../../config'; +import { + DATE_FORMAT, + DATE_FORMAT_CARBON, + DATE_FORMAT_FOR_DISPLAY, +} from '../../../config'; import DateAndTimeService from '../../../services/DateAndTimeService'; import { getCommonAttributes } from '../../helpers'; @@ -73,8 +78,12 @@ export default function BaseInputTemplate< ); const addDebouncedOnChangeDate = useDebouncedCallback( - (target: React.ChangeEvent) => { - _onChange(target); + (fullObject: React.ChangeEvent) => { + fullObject.target.value = + DateAndTimeService.attemptToConvertUnknownDateStringFormatToKnownFormat( + fullObject.target.value + ); + _onChange(fullObject); }, // delay in ms 1000 @@ -93,6 +102,8 @@ export default function BaseInputTemplate< // it should in be y-m-d when it gets here. let dateValue: string | null = value; if (value || value === 0) { + // it would be good if we could compare against the length of the desired format but that doesn't work in all cases and causes some issues. + // 10 seems to be a good value check against. if (value.length < 10) { dateValue = value; } else { diff --git a/spiffworkflow-frontend/src/services/DateAndTimeService.test.tsx b/spiffworkflow-frontend/src/services/DateAndTimeService.test.tsx index 6eb38828..95965bf8 100644 --- a/spiffworkflow-frontend/src/services/DateAndTimeService.test.tsx +++ b/spiffworkflow-frontend/src/services/DateAndTimeService.test.tsx @@ -5,6 +5,7 @@ test('it can keep the correct date when converting seconds to date', () => { DateAndTimeService.convertSecondsToFormattedDateString(1666325400); expect(dateString).toEqual('2022-10-21'); }); + test('it can properly format a duration', () => { expect(DateAndTimeService.formatDurationForDisplay('0')).toEqual('0s'); expect(DateAndTimeService.formatDurationForDisplay('60')).toEqual('1m'); @@ -20,3 +21,82 @@ test('it can properly format a duration', () => { '365d 12m 45s' ); }); + +test('it can get the correct date format from string', () => { + const expectedDateString = '2024-03-04'; + const newDateFormat = 'dd-MMM-yyyy'; + + expect( + DateAndTimeService.attemptToConvertUnknownDateStringFormatToKnownFormat( + expectedDateString + ) + ).toEqual(expectedDateString); + expect( + DateAndTimeService.attemptToConvertUnknownDateStringFormatToKnownFormat( + '03-04-2024' + ) + ).toEqual(expectedDateString); + expect( + DateAndTimeService.attemptToConvertUnknownDateStringFormatToKnownFormat( + 'March 4, 2024' + ) + ).toEqual(expectedDateString); + expect( + DateAndTimeService.attemptToConvertUnknownDateStringFormatToKnownFormat( + 'mar-4-2024' + ) + ).toEqual(expectedDateString); + expect( + DateAndTimeService.attemptToConvertUnknownDateStringFormatToKnownFormat( + '03/04/2024' + ) + ).toEqual(expectedDateString); + expect( + DateAndTimeService.attemptToConvertUnknownDateStringFormatToKnownFormat( + '03.04.2024' + ) + ).toEqual(expectedDateString); + + expect( + DateAndTimeService.attemptToConvertUnknownDateStringFormatToKnownFormat( + '04-03-2024', + newDateFormat + ) + ).toEqual(expectedDateString); + expect( + DateAndTimeService.attemptToConvertUnknownDateStringFormatToKnownFormat( + 'March 4, 2024', + newDateFormat + ) + ).toEqual(expectedDateString); + expect( + DateAndTimeService.attemptToConvertUnknownDateStringFormatToKnownFormat( + '4-mar-2024', + newDateFormat + ) + ).toEqual(expectedDateString); + expect( + DateAndTimeService.attemptToConvertUnknownDateStringFormatToKnownFormat( + '4 March 2024', + newDateFormat + ) + ).toEqual(expectedDateString); + expect( + DateAndTimeService.attemptToConvertUnknownDateStringFormatToKnownFormat( + '04/03/2024', + newDateFormat + ) + ).toEqual(expectedDateString); + expect( + DateAndTimeService.attemptToConvertUnknownDateStringFormatToKnownFormat( + '04.03.2024', + newDateFormat + ) + ).toEqual(expectedDateString); + expect( + DateAndTimeService.attemptToConvertUnknownDateStringFormatToKnownFormat( + expectedDateString, + newDateFormat + ) + ).toEqual(expectedDateString); +}); diff --git a/spiffworkflow-frontend/src/services/DateAndTimeService.tsx b/spiffworkflow-frontend/src/services/DateAndTimeService.tsx index 107d21f5..e9f1153a 100644 --- a/spiffworkflow-frontend/src/services/DateAndTimeService.tsx +++ b/spiffworkflow-frontend/src/services/DateAndTimeService.tsx @@ -147,6 +147,46 @@ const secondsToDuration = (secNum: number) => { return duration; }; +const attemptToConvertUnknownDateStringFormatToKnownFormat = ( + dateString: string, + targetDateFormat?: string +) => { + let dateFormat = targetDateFormat; + if (!dateFormat) { + dateFormat = DATE_FORMAT; + } + let newDateString = dateString; + // if the date starts with 4 digits then assume in y-m-d format and avoid all of this + if (dateString.length >= 10 && !dateString.match(/^\d{4}/)) { + // if the date format should contain month names or abbreviations but does not have letters + // then attempt to parse in the same format but with digit months instead of letters + + if (!dateString.match(/[a-zA-Z]+/) && dateFormat.match(/MMM/)) { + const numericalDateFormat = dateFormat.replaceAll(/MMM*/g, 'MM'); + const dateFormatRegex = new RegExp( + numericalDateFormat + .replace('dd', '\\d{2}') + .replace('MM', '\\d{2}') + .replace('yyyy', '\\d{4}') + ); + const normalizedDateString = dateString.replaceAll(/[.-/]+/g, '-'); + if (normalizedDateString.match(dateFormatRegex)) { + const newDate = parse( + normalizedDateString, + dateFormat.replaceAll(/MMM*/g, 'MM'), + new Date() + ); + newDateString = convertDateObjectToFormattedString(newDate) || ''; + } + } else { + // NOTE: do not run Date.parse with y-m-d formats since it returns dates in a different timezone from other formats + const newDate = new Date(Date.parse(`${dateString}`)); + newDateString = convertDateObjectToFormattedString(newDate) || ''; + } + } + return newDateString; +}; + const formatDurationForDisplay = (value: any) => { if (value === undefined) { return undefined; @@ -193,6 +233,7 @@ const DateAndTimeService = { REFRESH_INTERVAL_SECONDS, REFRESH_TIMEOUT_SECONDS, + attemptToConvertUnknownDateStringFormatToKnownFormat, convertDateAndTimeStringsToDate, convertDateAndTimeStringsToSeconds, convertDateObjectToFormattedHoursMinutes,