added a DateAndTimeService on the frontend to get those functions out of helpers w/ burnettk (#560)

Co-authored-by: jasquat <jasquat@users.noreply.github.com>
This commit is contained in:
jasquat 2023-10-20 16:07:00 -04:00 committed by GitHub
parent 54b7c5c3ec
commit 5907339918
16 changed files with 328 additions and 285 deletions

View File

@ -2,7 +2,8 @@ import { useEffect, useState } from 'react';
import HttpService from '../services/HttpService';
import { User } from '../interfaces';
import { refreshAtInterval, REFRESH_TIMEOUT_SECONDS } from '../helpers';
import { refreshAtInterval } from '../helpers';
import DateAndTimeService from '../services/DateAndTimeService';
async function sha256(message: string) {
// encode as UTF-8
@ -48,7 +49,7 @@ export default function ActiveUsers() {
return refreshAtInterval(
15,
REFRESH_TIMEOUT_SECONDS,
DateAndTimeService.REFRESH_TIMEOUT_SECONDS,
updateActiveUsers,
unregisterUser
);

View File

@ -7,13 +7,13 @@ import { Link, useSearchParams } from 'react-router-dom';
import PaginationForTable from './PaginationForTable';
import ProcessBreadcrumb from './ProcessBreadcrumb';
import {
convertSecondsToFormattedDateTime,
getPageInfoFromSearchParams,
modifyProcessIdentifierForPathParam,
} from '../helpers';
import HttpService from '../services/HttpService';
import { FormatProcessModelDisplayName } from './MiniComponents';
import { MessageInstance } from '../interfaces';
import DateAndTimeService from '../services/DateAndTimeService';
type OwnProps = {
processInstanceId?: number;
@ -128,7 +128,9 @@ export default function MessageInstanceList({ processInstanceId }: OwnProps) {
</td>
<td>{row.status}</td>
<td>
{convertSecondsToFormattedDateTime(row.created_at_in_seconds)}
{DateAndTimeService.convertSecondsToFormattedDateTime(
row.created_at_in_seconds
)}
</td>
</tr>
);

View File

@ -36,22 +36,13 @@ import {
DATE_FORMAT_FOR_DISPLAY,
} from '../config';
import {
convertDateAndTimeStringsToSeconds,
convertDateObjectToFormattedHoursMinutes,
convertSecondsToFormattedDateString,
convertSecondsToFormattedDateTime,
convertSecondsToFormattedTimeHoursMinutes,
getKeyByValue,
getLastMilestoneFromProcessInstance,
getPageInfoFromSearchParams,
modifyProcessIdentifierForPathParam,
refreshAtInterval,
REFRESH_INTERVAL_SECONDS,
REFRESH_TIMEOUT_SECONDS,
titleizeString,
truncateString,
formatDurationForDisplay,
formatDateTime,
} from '../helpers';
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
@ -86,6 +77,7 @@ import { Can } from '../contexts/Can';
import TableCellWithTimeAgoInWords from './TableCellWithTimeAgoInWords';
import UserService from '../services/UserService';
import Filters from './Filters';
import DateAndTimeService from '../services/DateAndTimeService';
type OwnProps = {
filtersEnabled?: boolean;
@ -443,13 +435,15 @@ export default function ProcessInstanceListTable({
const timeFunctionToCall =
dateParametersToAlwaysFilterBy[reportFilter.field_name][1];
if (reportFilter.field_value) {
const dateString = convertSecondsToFormattedDateString(
reportFilter.field_value as any
);
const dateString =
DateAndTimeService.convertSecondsToFormattedDateString(
reportFilter.field_value as any
);
dateFunctionToCall(dateString);
const timeString = convertSecondsToFormattedTimeHoursMinutes(
reportFilter.field_value as any
);
const timeString =
DateAndTimeService.convertSecondsToFormattedTimeHoursMinutes(
reportFilter.field_value as any
);
timeFunctionToCall(timeString);
}
}
@ -566,8 +560,8 @@ export default function ProcessInstanceListTable({
checkFiltersAndRun();
if (autoReload) {
clearRefreshRef.current = refreshAtInterval(
REFRESH_INTERVAL_SECONDS,
REFRESH_TIMEOUT_SECONDS,
DateAndTimeService.REFRESH_INTERVAL_SECONDS,
DateAndTimeService.REFRESH_TIMEOUT_SECONDS,
checkFiltersAndRun
);
return clearRefreshRef.current;
@ -627,19 +621,22 @@ export default function ProcessInstanceListTable({
// with the use of the setErrorMessageSafely function. we are not sure why the context not
// changing still causes things to rerender when we call its setter without our extra check.
const calculateStartAndEndSeconds = (validate: boolean = true) => {
const startFromSeconds = convertDateAndTimeStringsToSeconds(
startFromDate,
startFromTime || '00:00:00'
);
const startToSeconds = convertDateAndTimeStringsToSeconds(
startToDate,
startToTime || '00:00:00'
);
const endFromSeconds = convertDateAndTimeStringsToSeconds(
endFromDate,
endFromTime || '00:00:00'
);
const endToSeconds = convertDateAndTimeStringsToSeconds(
const startFromSeconds =
DateAndTimeService.convertDateAndTimeStringsToSeconds(
startFromDate,
startFromTime || '00:00:00'
);
const startToSeconds =
DateAndTimeService.convertDateAndTimeStringsToSeconds(
startToDate,
startToTime || '00:00:00'
);
const endFromSeconds =
DateAndTimeService.convertDateAndTimeStringsToSeconds(
endFromDate,
endFromTime || '00:00:00'
);
const endToSeconds = DateAndTimeService.convertDateAndTimeStringsToSeconds(
endToDate,
endToTime || '00:00:00'
);
@ -872,7 +869,9 @@ export default function ProcessInstanceListTable({
onChange={(dateChangeEvent: any) => {
if (!initialDate && !initialTime) {
onChangeTimeFunction(
convertDateObjectToFormattedHoursMinutes(new Date())
DateAndTimeService.convertDateObjectToFormattedHoursMinutes(
new Date()
)
);
}
onChangeDateFunction(dateChangeEvent.srcElement.value);
@ -1691,7 +1690,7 @@ export default function ProcessInstanceListTable({
};
const formatSecondsForDisplay = (_row: ProcessInstance, seconds: any) => {
return convertSecondsToFormattedDateTime(seconds) || '-';
return DateAndTimeService.convertSecondsToFormattedDateTime(seconds) || '-';
};
const defaultFormatter = (_row: ProcessInstance, value: any) => {
return value;
@ -1710,8 +1709,8 @@ export default function ProcessInstanceListTable({
last_milestone_bpmn_name: formatLastMilestone,
};
const displayTypeFormatters: Record<string, any> = {
date_time: formatDateTime,
duration: formatDurationForDisplay,
date_time: DateAndTimeService.formatDateTime,
duration: DateAndTimeService.formatDurationForDisplay,
};
const columnAccessor = column.accessor as keyof ProcessInstance;
const formatter = column.display_type

View File

@ -15,7 +15,6 @@ import { createSearchParams, Link, useSearchParams } from 'react-router-dom';
import PaginationForTable from './PaginationForTable';
import {
getPageInfoFromSearchParams,
convertSecondsToFormattedDateTime,
selectKeysFromSearchParams,
} from '../helpers';
import HttpService from '../services/HttpService';
@ -32,6 +31,7 @@ import {
childrenForErrorObject,
errorForDisplayFromProcessInstanceErrorDetail,
} from './ErrorDisplay';
import DateAndTimeService from '../services/DateAndTimeService';
type OwnProps = {
variant: string; // 'all' or 'for-me'
@ -306,7 +306,11 @@ export default function ProcessInstanceLogList({
}
let timestampComponent = (
<td>{convertSecondsToFormattedDateTime(logEntry.timestamp)}</td>
<td>
{DateAndTimeService.convertSecondsToFormattedDateTime(
logEntry.timestamp
)}
</td>
);
if (logEntry.spiff_task_guid && logEntry.event_type !== 'task_cancelled') {
timestampComponent = (
@ -317,7 +321,9 @@ export default function ProcessInstanceLogList({
to={`${processInstanceShowPageBaseUrl}/${logEntry.process_instance_id}/${logEntry.spiff_task_guid}`}
title="View state when task was completed"
>
{convertSecondsToFormattedDateTime(logEntry.timestamp)}
{DateAndTimeService.convertSecondsToFormattedDateTime(
logEntry.timestamp
)}
</Link>
</td>
);

View File

@ -1,6 +1,6 @@
// @ts-ignore
import { TimeAgo } from '../helpers/timeago';
import { convertSecondsToFormattedDateTime } from '../helpers';
import DateAndTimeService from '../services/DateAndTimeService';
type OwnProps = {
timeInSeconds: number;
@ -16,7 +16,10 @@ export default function TableCellWithTimeAgoInWords({
return (
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
<td
title={convertSecondsToFormattedDateTime(timeInSeconds) || '-'}
title={
DateAndTimeService.convertSecondsToFormattedDateTime(timeInSeconds) ||
'-'
}
onClick={onClick}
onKeyDown={onKeyDown}
>

View File

@ -6,18 +6,16 @@ import { TimeAgo } from '../helpers/timeago';
import UserService from '../services/UserService';
import PaginationForTable from './PaginationForTable';
import {
convertSecondsToFormattedDateTime,
getPageInfoFromSearchParams,
modifyProcessIdentifierForPathParam,
refreshAtInterval,
REFRESH_INTERVAL_SECONDS,
REFRESH_TIMEOUT_SECONDS,
} from '../helpers';
import HttpService from '../services/HttpService';
import { PaginationObject, ProcessInstanceTask, Task } from '../interfaces';
import TableCellWithTimeAgoInWords from './TableCellWithTimeAgoInWords';
import CustomForm from './CustomForm';
import InstructionsForEndUser from './InstructionsForEndUser';
import DateAndTimeService from '../services/DateAndTimeService';
const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5;
@ -100,8 +98,8 @@ export default function TaskListTable({
getTasks();
if (autoReload) {
return refreshAtInterval(
REFRESH_INTERVAL_SECONDS,
REFRESH_TIMEOUT_SECONDS,
DateAndTimeService.REFRESH_INTERVAL_SECONDS,
DateAndTimeService.REFRESH_TIMEOUT_SECONDS,
getTasks
);
}
@ -288,7 +286,7 @@ export default function TaskListTable({
if (showDateStarted) {
rowElements.push(
<td>
{convertSecondsToFormattedDateTime(
{DateAndTimeService.convertSecondsToFormattedDateTime(
processInstanceTask.created_at_in_seconds
) || '-'}
</td>

View File

@ -1,10 +1,8 @@
import {
convertSecondsToFormattedDateString,
isANumber,
slugifyString,
underscorizeString,
recursivelyChangeNullAndUndefined,
formatDurationForDisplay,
} from './helpers';
test('it can slugify a string', () => {
@ -19,11 +17,6 @@ test('it can underscorize a string', () => {
);
});
test('it can keep the correct date when converting seconds to date', () => {
const dateString = convertSecondsToFormattedDateString(1666325400);
expect(dateString).toEqual('2022-10-21');
});
test('it can validate numeric values', () => {
expect(isANumber('11')).toEqual(true);
expect(isANumber('hey')).toEqual(false);
@ -102,13 +95,3 @@ test('it can replace null values in object with undefined', () => {
expect(result.contacts.awesome).toEqual(false);
expect(result.contacts.info).toEqual('');
});
test('it can properly format a duration', () => {
expect(formatDurationForDisplay(null, '0')).toEqual('0s');
expect(formatDurationForDisplay(null, '60')).toEqual('1m');
expect(formatDurationForDisplay(null, '65')).toEqual('1m 5s');
expect(formatDurationForDisplay(null, 65)).toEqual('1m 5s');
expect(formatDurationForDisplay(null, 86500)).toEqual('1d 1m 40s');
expect(formatDurationForDisplay(null, 2629746)).toEqual('30d 10h 29m 6s');
expect(formatDurationForDisplay(null, 31536765)).toEqual('365d 12m 45s');
});

View File

@ -1,11 +1,5 @@
import { Duration, format } from 'date-fns';
import { Buffer } from 'buffer';
import {
DATE_TIME_FORMAT,
DATE_FORMAT,
TIME_FORMAT_HOURS_MINUTES,
} from './config';
import { ProcessInstance } from './interfaces';
export const DEFAULT_PER_PAGE = 50;
@ -87,128 +81,6 @@ export const titleizeString = (string: any) => {
return capitalizeFirstLetter((string || '').replaceAll('_', ' '));
};
export const convertDateToSeconds = (
date: any,
onChangeFunction: any = null
) => {
let dateInSeconds = date;
if (date !== null) {
let dateInMilliseconds = date;
if (typeof date.getTime === 'function') {
dateInMilliseconds = date.getTime();
}
dateInSeconds = Math.floor(dateInMilliseconds / 1000);
}
if (onChangeFunction) {
onChangeFunction(dateInSeconds);
} else {
return dateInSeconds;
}
return null;
};
export const convertDateObjectToFormattedString = (dateObject: Date) => {
if (dateObject) {
return format(dateObject, DATE_FORMAT);
}
return null;
};
export const dateStringToYMDFormat = (dateString: string) => {
if (dateString && dateString.match(/^\d{2}-\d{2}-\d{4}$/)) {
if (DATE_FORMAT.startsWith('dd')) {
const d = dateString.split('-');
return `${d[2]}-${d[1]}-${d[0]}`;
}
if (DATE_FORMAT.startsWith('MM')) {
const d = dateString.split('-');
return `${d[2]}-${d[0]}-${d[1]}`;
}
}
return dateString;
};
export const convertDateAndTimeStringsToDate = (
dateString: string,
timeString: string
) => {
if (dateString && timeString) {
return new Date(`${dateStringToYMDFormat(dateString)}T${timeString}`);
}
return null;
};
export const convertDateAndTimeStringsToSeconds = (
dateString: string,
timeString: string
) => {
const dateObject = convertDateAndTimeStringsToDate(dateString, timeString);
if (dateObject) {
return convertDateToSeconds(dateObject);
}
return null;
};
export const convertStringToDate = (dateString: string) => {
return convertDateAndTimeStringsToDate(dateString, '00:10:00');
};
export const ymdDateStringToConfiguredFormat = (dateString: string) => {
const dateObject = convertStringToDate(dateString);
if (dateObject) {
return convertDateObjectToFormattedString(dateObject);
}
return null;
};
export const convertSecondsToDateObject = (seconds: number) => {
if (seconds) {
return new Date(seconds * 1000);
}
return null;
};
export const convertSecondsToFormattedDateTime = (seconds: number) => {
const dateObject = convertSecondsToDateObject(seconds);
if (dateObject) {
return format(dateObject, DATE_TIME_FORMAT);
}
return null;
};
export const convertDateObjectToFormattedHoursMinutes = (dateObject: Date) => {
if (dateObject) {
return format(dateObject, TIME_FORMAT_HOURS_MINUTES);
}
return null;
};
export const convertSecondsToFormattedTimeHoursMinutes = (seconds: number) => {
const dateObject = convertSecondsToDateObject(seconds);
if (dateObject) {
return convertDateObjectToFormattedHoursMinutes(dateObject);
}
return null;
};
export const convertSecondsToFormattedDateString = (seconds: number) => {
const dateObject = convertSecondsToDateObject(seconds);
if (dateObject) {
return convertDateObjectToFormattedString(dateObject);
}
return null;
};
export const convertDateStringToSeconds = (dateString: string) => {
const dateObject = convertStringToDate(dateString);
if (dateObject) {
return convertDateToSeconds(dateObject);
}
return null;
};
export const objectIsEmpty = (obj: object) => {
return Object.keys(obj).length === 0;
};
@ -368,14 +240,6 @@ export const decodeBase64 = (data: string) => {
return Buffer.from(data, 'base64').toString('ascii');
};
const MINUTES_IN_HOUR = 60;
const SECONDS_IN_MINUTE = 60;
const SECONDS_IN_HOUR = MINUTES_IN_HOUR * SECONDS_IN_MINUTE;
const FOUR_HOURS_IN_SECONDS = SECONDS_IN_HOUR * 4;
export const REFRESH_INTERVAL_SECONDS = 5;
export const REFRESH_TIMEOUT_SECONDS = FOUR_HOURS_IN_SECONDS;
export const getLastMilestoneFromProcessInstance = (
processInstance: ProcessInstance,
value: any
@ -402,61 +266,3 @@ export const getLastMilestoneFromProcessInstance = (
}
return [valueToUse, truncatedValue];
};
// logic from https://stackoverflow.com/a/28510323/6090676
export const secondsToDuration = (secNum: number) => {
const days = Math.floor(secNum / 86400);
const hours = Math.floor(secNum / 3600) % 24;
const minutes = Math.floor(secNum / 60) % 60;
const seconds = secNum % 60;
const duration: Duration = {
days,
hours,
minutes,
seconds,
};
return duration;
};
export const formatDurationForDisplay = (_row: any, value: any) => {
if (value === undefined) {
return undefined;
}
const duration = secondsToDuration(parseInt(value, 10));
const durationTimes = [];
if (duration.seconds !== undefined && duration.seconds > 0) {
durationTimes.unshift(`${duration.seconds}s`);
}
if (duration.minutes !== undefined && duration.minutes > 0) {
durationTimes.unshift(`${duration.minutes}m`);
}
if (duration.hours !== undefined && duration.hours > 0) {
durationTimes.unshift(`${duration.hours}h`);
}
if (duration.days !== undefined && duration.days > 0) {
durationTimes.unshift(`${duration.days}d`);
}
if (durationTimes.length < 1) {
durationTimes.push('0s');
}
return durationTimes.join(' ');
};
export const formatDateTime = (_row: any, value: any) => {
if (value === undefined || value === null) {
return value;
}
let dateInSeconds = value;
if (!isANumber(value)) {
const timeArgs = value.split('T');
dateInSeconds = convertDateAndTimeStringsToSeconds(
timeArgs[0],
timeArgs[1]
);
}
if (dateInSeconds) {
return convertSecondsToFormattedDateTime(dateInSeconds);
}
return null;
};

View File

@ -9,7 +9,7 @@ import {
import { useCallback } from 'react';
import { DATE_FORMAT_CARBON, DATE_FORMAT_FOR_DISPLAY } from '../../../config';
import { ymdDateStringToConfiguredFormat } from '../../../helpers';
import DateAndTimeService from '../../../services/DateAndTimeService';
import { getCommonAttributes } from '../../helpers';
/** The `BaseInputTemplate` is the template to use to render the basic `<input>` component for the `core` theme.
@ -87,7 +87,7 @@ export default function BaseInputTemplate<
dateValue = value;
} else {
try {
dateValue = ymdDateStringToConfiguredFormat(value);
dateValue = DateAndTimeService.ymdDateStringToConfiguredFormat(value);
// let the date component and form validators handle bad dates and do not blow up
} catch (RangeError) {}
}

View File

@ -1,6 +1,6 @@
import React, { useCallback } from 'react';
import { getTemplate, WidgetProps } from '@rjsf/utils';
import { dateStringToYMDFormat } from '../../../helpers';
import DateAndTimeService from '../../../services/DateAndTimeService';
function DateWidget(props: WidgetProps) {
const { onChange, options, registry } = props;
@ -12,7 +12,7 @@ function DateWidget(props: WidgetProps) {
const handleChange = useCallback(
(value: any) => {
// react json schema forces y-m-d format for dates
const newValue = dateStringToYMDFormat(value);
const newValue = DateAndTimeService.dateStringToYMDFormat(value);
onChange(newValue || undefined);
},
[onChange]

View File

@ -5,12 +5,8 @@ import {
DATE_FORMAT_FOR_DISPLAY,
DATE_RANGE_DELIMITER,
} from '../../../config';
import {
convertDateObjectToFormattedString,
convertStringToDate,
dateStringToYMDFormat,
} from '../../../helpers';
import { getCommonAttributes } from '../../helpers';
import DateAndTimeService from '../../../services/DateAndTimeService';
interface widgetArgs {
id: string;
@ -52,13 +48,18 @@ export default function DateRangePickerWidget({
const onChangeLocal = useCallback(
(dateRange: Date[]) => {
let dateRangeString;
const startDate = convertDateObjectToFormattedString(dateRange[0]);
const startDate = DateAndTimeService.convertDateObjectToFormattedString(
dateRange[0]
);
if (startDate) {
const startDateYMD = dateStringToYMDFormat(startDate);
const endDate = convertDateObjectToFormattedString(dateRange[1]);
const startDateYMD =
DateAndTimeService.dateStringToYMDFormat(startDate);
const endDate = DateAndTimeService.convertDateObjectToFormattedString(
dateRange[1]
);
dateRangeString = startDateYMD;
if (endDate) {
const endDateYMD = dateStringToYMDFormat(endDate);
const endDateYMD = DateAndTimeService.dateStringToYMDFormat(endDate);
dateRangeString = `${dateRangeString}${DATE_RANGE_DELIMITER}${endDateYMD}`;
}
}
@ -73,11 +74,11 @@ export default function DateRangePickerWidget({
let startDate = null;
let endDate = null;
try {
startDate = convertStringToDate(startDateString);
startDate = DateAndTimeService.convertStringToDate(startDateString);
// eslint-disable-next-line no-empty
} catch (RangeError) {}
try {
endDate = convertStringToDate(endDateString);
endDate = DateAndTimeService.convertStringToDate(endDateString);
// eslint-disable-next-line no-empty
} catch (RangeError) {}

View File

@ -8,8 +8,6 @@ import {
getPageInfoFromSearchParams,
modifyProcessIdentifierForPathParam,
refreshAtInterval,
REFRESH_INTERVAL_SECONDS,
REFRESH_TIMEOUT_SECONDS,
} from '../helpers';
import HttpService from '../services/HttpService';
import {
@ -19,6 +17,7 @@ import {
RecentProcessModel,
} from '../interfaces';
import ProcessInstanceRun from '../components/ProcessInstanceRun';
import DateAndTimeService from '../services/DateAndTimeService';
const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5;
@ -47,8 +46,8 @@ export default function MyTasks() {
getTasks();
refreshAtInterval(
REFRESH_INTERVAL_SECONDS,
REFRESH_TIMEOUT_SECONDS,
DateAndTimeService.REFRESH_INTERVAL_SECONDS,
DateAndTimeService.REFRESH_TIMEOUT_SECONDS,
getTasks
);
}, [searchParams]);

View File

@ -45,7 +45,6 @@ import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import HttpService from '../services/HttpService';
import ReactDiagramEditor from '../components/ReactDiagramEditor';
import {
convertSecondsToFormattedDateTime,
getLastMilestoneFromProcessInstance,
HUMAN_TASK_TYPES,
modifyProcessIdentifierForPathParam,
@ -78,6 +77,7 @@ import {
errorForDisplayFromString,
} from '../components/ErrorDisplay';
import { Notification } from '../components/Notification';
import DateAndTimeService from '../services/DateAndTimeService';
type OwnProps = {
variant: string;
@ -380,7 +380,9 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
<dl>
<dt>{lastUpdatedTimeLabel}:</dt>
<dd>
{convertSecondsToFormattedDateTime(lastUpdatedTime || 0) || 'N/A'}
{DateAndTimeService.convertSecondsToFormattedDateTime(
lastUpdatedTime || 0
) || 'N/A'}
</dd>
</dl>
);
@ -442,7 +444,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
<dl>
<dt>Started:</dt>
<dd>
{convertSecondsToFormattedDateTime(
{DateAndTimeService.convertSecondsToFormattedDateTime(
processInstance.start_in_seconds || 0
)}
</dd>

View File

@ -0,0 +1,26 @@
import DateAndTimeService from './DateAndTimeService';
test('it can keep the correct date when converting seconds to date', () => {
const dateString =
DateAndTimeService.convertSecondsToFormattedDateString(1666325400);
expect(dateString).toEqual('2022-10-21');
});
test('it can properly format a duration', () => {
expect(DateAndTimeService.formatDurationForDisplay(null, '0')).toEqual('0s');
expect(DateAndTimeService.formatDurationForDisplay(null, '60')).toEqual('1m');
expect(DateAndTimeService.formatDurationForDisplay(null, '65')).toEqual(
'1m 5s'
);
expect(DateAndTimeService.formatDurationForDisplay(null, 65)).toEqual(
'1m 5s'
);
expect(DateAndTimeService.formatDurationForDisplay(null, 86500)).toEqual(
'1d 1m 40s'
);
expect(DateAndTimeService.formatDurationForDisplay(null, 2629746)).toEqual(
'30d 10h 29m 6s'
);
expect(DateAndTimeService.formatDurationForDisplay(null, 31536765)).toEqual(
'365d 12m 45s'
);
});

View File

@ -0,0 +1,216 @@
import { Duration, format } from 'date-fns';
import {
DATE_TIME_FORMAT,
DATE_FORMAT,
TIME_FORMAT_HOURS_MINUTES,
} from '../config';
import { isANumber } from '../helpers';
const MINUTES_IN_HOUR = 60;
const SECONDS_IN_MINUTE = 60;
const SECONDS_IN_HOUR = MINUTES_IN_HOUR * SECONDS_IN_MINUTE;
const FOUR_HOURS_IN_SECONDS = SECONDS_IN_HOUR * 4;
const REFRESH_INTERVAL_SECONDS = 5;
const REFRESH_TIMEOUT_SECONDS = FOUR_HOURS_IN_SECONDS;
const convertDateToSeconds = (date: any, onChangeFunction: any = null) => {
let dateInSeconds = date;
if (date !== null) {
let dateInMilliseconds = date;
if (typeof date.getTime === 'function') {
dateInMilliseconds = date.getTime();
}
dateInSeconds = Math.floor(dateInMilliseconds / 1000);
}
if (onChangeFunction) {
onChangeFunction(dateInSeconds);
} else {
return dateInSeconds;
}
return null;
};
const convertDateObjectToFormattedString = (dateObject: Date) => {
if (dateObject) {
return format(dateObject, DATE_FORMAT);
}
return null;
};
const dateStringToYMDFormat = (dateString: string) => {
if (dateString && dateString.match(/^\d{2}-\d{2}-\d{4}$/)) {
if (DATE_FORMAT.startsWith('dd')) {
const d = dateString.split('-');
return `${d[2]}-${d[1]}-${d[0]}`;
}
if (DATE_FORMAT.startsWith('MM')) {
const d = dateString.split('-');
return `${d[2]}-${d[0]}-${d[1]}`;
}
}
return dateString;
};
const convertDateAndTimeStringsToDate = (
dateString: string,
timeString: string
) => {
if (dateString && timeString) {
return new Date(`${dateStringToYMDFormat(dateString)}T${timeString}`);
}
return null;
};
const convertDateAndTimeStringsToSeconds = (
dateString: string,
timeString: string
) => {
const dateObject = convertDateAndTimeStringsToDate(dateString, timeString);
if (dateObject) {
return convertDateToSeconds(dateObject);
}
return null;
};
const convertStringToDate = (dateString: string) => {
return convertDateAndTimeStringsToDate(dateString, '00:10:00');
};
const ymdDateStringToConfiguredFormat = (dateString: string) => {
const dateObject = convertStringToDate(dateString);
if (dateObject) {
return convertDateObjectToFormattedString(dateObject);
}
return null;
};
const convertSecondsToDateObject = (seconds: number) => {
if (seconds) {
return new Date(seconds * 1000);
}
return null;
};
const convertSecondsToFormattedDateTime = (seconds: number) => {
const dateObject = convertSecondsToDateObject(seconds);
if (dateObject) {
return format(dateObject, DATE_TIME_FORMAT);
}
return null;
};
const convertDateObjectToFormattedHoursMinutes = (dateObject: Date) => {
if (dateObject) {
return format(dateObject, TIME_FORMAT_HOURS_MINUTES);
}
return null;
};
const convertSecondsToFormattedTimeHoursMinutes = (seconds: number) => {
const dateObject = convertSecondsToDateObject(seconds);
if (dateObject) {
return convertDateObjectToFormattedHoursMinutes(dateObject);
}
return null;
};
const convertSecondsToFormattedDateString = (seconds: number) => {
const dateObject = convertSecondsToDateObject(seconds);
if (dateObject) {
return convertDateObjectToFormattedString(dateObject);
}
return null;
};
const convertDateStringToSeconds = (dateString: string) => {
const dateObject = convertStringToDate(dateString);
if (dateObject) {
return convertDateToSeconds(dateObject);
}
return null;
};
// logic from https://stackoverflow.com/a/28510323/6090676
const secondsToDuration = (secNum: number) => {
const days = Math.floor(secNum / 86400);
const hours = Math.floor(secNum / 3600) % 24;
const minutes = Math.floor(secNum / 60) % 60;
const seconds = secNum % 60;
const duration: Duration = {
days,
hours,
minutes,
seconds,
};
return duration;
};
const formatDurationForDisplay = (_row: any, value: any) => {
if (value === undefined) {
return undefined;
}
const duration = secondsToDuration(parseInt(value, 10));
const durationTimes = [];
if (duration.seconds !== undefined && duration.seconds > 0) {
durationTimes.unshift(`${duration.seconds}s`);
}
if (duration.minutes !== undefined && duration.minutes > 0) {
durationTimes.unshift(`${duration.minutes}m`);
}
if (duration.hours !== undefined && duration.hours > 0) {
durationTimes.unshift(`${duration.hours}h`);
}
if (duration.days !== undefined && duration.days > 0) {
durationTimes.unshift(`${duration.days}d`);
}
if (durationTimes.length < 1) {
durationTimes.push('0s');
}
return durationTimes.join(' ');
};
const formatDateTime = (_row: any, value: any) => {
if (value === undefined || value === null) {
return value;
}
let dateInSeconds = value;
if (!isANumber(value)) {
const timeArgs = value.split('T');
dateInSeconds = convertDateAndTimeStringsToSeconds(
timeArgs[0],
timeArgs[1]
);
}
if (dateInSeconds) {
return convertSecondsToFormattedDateTime(dateInSeconds);
}
return null;
};
const DateAndTimeService = {
REFRESH_INTERVAL_SECONDS,
REFRESH_TIMEOUT_SECONDS,
convertDateAndTimeStringsToDate,
convertDateAndTimeStringsToSeconds,
convertDateObjectToFormattedHoursMinutes,
convertDateObjectToFormattedString,
convertDateStringToSeconds,
convertDateToSeconds,
convertSecondsToDateObject,
convertSecondsToFormattedDateString,
convertSecondsToFormattedDateTime,
convertSecondsToFormattedTimeHoursMinutes,
convertStringToDate,
dateStringToYMDFormat,
formatDateTime,
formatDurationForDisplay,
secondsToDuration,
ymdDateStringToConfiguredFormat,
};
export default DateAndTimeService;

View File

@ -1,8 +1,9 @@
import { formatDateTime, formatDurationForDisplay } from '../helpers';
import DateAndTimeService from './DateAndTimeService';
const spiffFormatFunctions: { [key: string]: Function } = {
convert_seconds_to_date_time_for_display: formatDateTime,
convert_seconds_to_duration_for_display: formatDurationForDisplay,
convert_seconds_to_date_time_for_display: DateAndTimeService.formatDateTime,
convert_seconds_to_duration_for_display:
DateAndTimeService.formatDurationForDisplay,
};
const checkForSpiffFormats = (markdown: string) => {