added time between 2 dates

This commit is contained in:
Lukas Herajt
2025-03-27 10:14:05 -04:00
parent 4b2d38cae3
commit a3d75e57ed
7 changed files with 574 additions and 1 deletions

View File

@@ -0,0 +1,3 @@
import TimeBetweenDates from '../tools/time/time-between-dates';
export default TimeBetweenDates;

View File

@@ -1,13 +1,16 @@
import { tool as timeBetweenDates } from './time-between-dates/meta';
import { tool as daysDoHours } from './convert-days-to-hours/meta'; import { tool as daysDoHours } from './convert-days-to-hours/meta';
import { tool as hoursToDays } from './convert-hours-to-days/meta'; import { tool as hoursToDays } from './convert-hours-to-days/meta';
import { tool as convertSecondsToTime } from './convert-seconds-to-time/meta'; import { tool as convertSecondsToTime } from './convert-seconds-to-time/meta';
import { tool as convertTimetoSeconds } from './convert-time-to-seconds/meta'; import { tool as convertTimetoSeconds } from './convert-time-to-seconds/meta';
import { tool as truncateClockTime } from './truncate-clock-time/meta'; import { tool as truncateClockTime } from './truncate-clock-time/meta';
export const timeTools = [ export const timeTools = [
daysDoHours, daysDoHours,
hoursToDays, hoursToDays,
convertSecondsToTime, convertSecondsToTime,
convertTimetoSeconds, convertTimetoSeconds,
truncateClockTime truncateClockTime,
timeBetweenDates
]; ];

View File

@@ -0,0 +1,388 @@
import { Box, Typography, Paper } from '@mui/material';
import React, { useState } from 'react';
import ToolContent from '@components/ToolContent';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import SelectWithDesc from '@components/options/SelectWithDesc';
import {
calculateTimeBetweenDates,
formatTimeDifference,
getTimeWithTimezone
} from './service';
import * as Yup from 'yup';
import { CardExampleType } from '@components/examples/ToolExamples';
type TimeUnit =
| 'milliseconds'
| 'seconds'
| 'minutes'
| 'hours'
| 'days'
| 'months'
| 'years';
type InitialValuesType = {
startDate: string;
startTime: string;
endDate: string;
endTime: string;
startTimezone: string;
endTimezone: string;
};
// Helper function to format time based on largest unit
const formatTimeWithLargestUnit = (
difference: any,
largestUnit: TimeUnit
): string => {
const unitHierarchy: TimeUnit[] = [
'years',
'months',
'days',
'hours',
'minutes',
'seconds',
'milliseconds'
];
const largestUnitIndex = unitHierarchy.indexOf(largestUnit);
const unitsToInclude = unitHierarchy.slice(largestUnitIndex);
// Make a deep copy of the difference object to avoid modifying the original
const convertedDifference = { ...difference };
// Constants for time conversions - more precise values
const HOURS_PER_DAY = 24;
const DAYS_PER_MONTH = 30; // Approximation
const MONTHS_PER_YEAR = 12;
const DAYS_PER_YEAR = 365; // Approximation
const MINUTES_PER_HOUR = 60;
const SECONDS_PER_MINUTE = 60;
const MS_PER_SECOND = 1000;
// Apply conversions based on the selected largest unit
if (largestUnit === 'months') {
// Convert years to months
convertedDifference.months += convertedDifference.years * MONTHS_PER_YEAR;
convertedDifference.years = 0;
} else if (largestUnit === 'days') {
// Convert years and months to days
convertedDifference.days +=
convertedDifference.years * DAYS_PER_YEAR +
convertedDifference.months * DAYS_PER_MONTH;
convertedDifference.months = 0;
convertedDifference.years = 0;
} else if (largestUnit === 'hours') {
// Convert years, months, and days to hours
convertedDifference.hours +=
convertedDifference.years * DAYS_PER_YEAR * HOURS_PER_DAY +
convertedDifference.months * DAYS_PER_MONTH * HOURS_PER_DAY +
convertedDifference.days * HOURS_PER_DAY;
convertedDifference.days = 0;
convertedDifference.months = 0;
convertedDifference.years = 0;
} else if (largestUnit === 'minutes') {
// Convert years, months, days, and hours to minutes
convertedDifference.minutes +=
convertedDifference.years *
DAYS_PER_YEAR *
HOURS_PER_DAY *
MINUTES_PER_HOUR +
convertedDifference.months *
DAYS_PER_MONTH *
HOURS_PER_DAY *
MINUTES_PER_HOUR +
convertedDifference.days * HOURS_PER_DAY * MINUTES_PER_HOUR +
convertedDifference.hours * MINUTES_PER_HOUR;
convertedDifference.hours = 0;
convertedDifference.days = 0;
convertedDifference.months = 0;
convertedDifference.years = 0;
} else if (largestUnit === 'seconds') {
// Convert years, months, days, hours, and minutes to seconds
convertedDifference.seconds +=
convertedDifference.years *
DAYS_PER_YEAR *
HOURS_PER_DAY *
MINUTES_PER_HOUR *
SECONDS_PER_MINUTE +
convertedDifference.months *
DAYS_PER_MONTH *
HOURS_PER_DAY *
MINUTES_PER_HOUR *
SECONDS_PER_MINUTE +
convertedDifference.days *
HOURS_PER_DAY *
MINUTES_PER_HOUR *
SECONDS_PER_MINUTE +
convertedDifference.hours * MINUTES_PER_HOUR * SECONDS_PER_MINUTE +
convertedDifference.minutes * SECONDS_PER_MINUTE;
convertedDifference.minutes = 0;
convertedDifference.hours = 0;
convertedDifference.days = 0;
convertedDifference.months = 0;
convertedDifference.years = 0;
} else if (largestUnit === 'milliseconds') {
// Convert everything to milliseconds
convertedDifference.milliseconds +=
convertedDifference.years *
DAYS_PER_YEAR *
HOURS_PER_DAY *
MINUTES_PER_HOUR *
SECONDS_PER_MINUTE *
MS_PER_SECOND +
convertedDifference.months *
DAYS_PER_MONTH *
HOURS_PER_DAY *
MINUTES_PER_HOUR *
SECONDS_PER_MINUTE *
MS_PER_SECOND +
convertedDifference.days *
HOURS_PER_DAY *
MINUTES_PER_HOUR *
SECONDS_PER_MINUTE *
MS_PER_SECOND +
convertedDifference.hours *
MINUTES_PER_HOUR *
SECONDS_PER_MINUTE *
MS_PER_SECOND +
convertedDifference.minutes * SECONDS_PER_MINUTE * MS_PER_SECOND +
convertedDifference.seconds * MS_PER_SECOND;
convertedDifference.seconds = 0;
convertedDifference.minutes = 0;
convertedDifference.hours = 0;
convertedDifference.days = 0;
convertedDifference.months = 0;
convertedDifference.years = 0;
}
return formatTimeDifference(convertedDifference, unitsToInclude);
};
const initialValues: InitialValuesType = {
startDate: new Date().toISOString().split('T')[0],
startTime: '00:00',
endDate: new Date().toISOString().split('T')[0],
endTime: '12:00',
startTimezone: 'local',
endTimezone: 'local'
};
const validationSchema = Yup.object({
startDate: Yup.string().required('Start date is required'),
startTime: Yup.string().required('Start time is required'),
endDate: Yup.string().required('End date is required'),
endTime: Yup.string().required('End time is required'),
startTimezone: Yup.string(),
endTimezone: Yup.string()
});
const timezoneOptions = [
{ value: 'local', label: 'Local Time' },
{ value: 'GMT+0000', label: 'GMT+0000 (UTC)' },
{ value: 'GMT-0500', label: 'GMT-0500 (Eastern Standard Time)' },
{ value: 'GMT-0600', label: 'GMT-0600 (Central Standard Time)' },
{ value: 'GMT-0700', label: 'GMT-0700 (Mountain Standard Time)' },
{ value: 'GMT-0800', label: 'GMT-0800 (Pacific Standard Time)' },
{ value: 'GMT+0100', label: 'GMT+0100 (Central European Time)' },
{ value: 'GMT+0200', label: 'GMT+0200 (Eastern European Time)' },
{ value: 'GMT+0530', label: 'GMT+0530 (Indian Standard Time)' },
{ value: 'GMT+0800', label: 'GMT+0800 (China Standard Time)' },
{ value: 'GMT+0900', label: 'GMT+0900 (Japan Standard Time)' },
{ value: 'GMT+1000', label: 'GMT+1000 (Australian Eastern Standard Time)' }
];
const exampleCards: CardExampleType<InitialValuesType>[] = [
{
title: 'One Year Difference',
description: 'Calculate the time between dates that are one year apart',
sampleOptions: {
startDate: '2023-01-01',
startTime: '12:00',
endDate: '2024-01-01',
endTime: '12:00',
startTimezone: 'local',
endTimezone: 'local'
},
sampleResult: '1 year'
},
{
title: 'Different Timezones',
description: 'Calculate the time difference between New York and London',
sampleOptions: {
startDate: '2023-01-01',
startTime: '12:00',
endDate: '2023-01-01',
endTime: '12:00',
startTimezone: 'GMT-0500',
endTimezone: 'GMT+0000'
},
sampleResult: '5 hours'
},
{
title: 'Detailed Time Breakdown',
description: 'Show a detailed breakdown of a time difference',
sampleOptions: {
startDate: '2023-01-01',
startTime: '09:30',
endDate: '2023-01-03',
endTime: '14:45',
startTimezone: 'local',
endTimezone: 'local'
},
sampleResult: '2 days, 5 hours, 15 minutes'
}
];
export default function TimeBetweenDates() {
const [result, setResult] = useState<string>('');
return (
<ToolContent
inputComponent={null}
resultComponent={
result ? (
<Paper
elevation={3}
sx={{
p: 3,
borderLeft: '5px solid',
borderColor: 'primary.main',
bgcolor: 'background.paper',
maxWidth: '100%',
mx: 'auto'
}}
>
<Typography
variant="h4"
align="center"
sx={{ fontWeight: 'bold', color: 'primary.main' }}
>
{result}
</Typography>
</Paper>
) : null
}
initialValues={initialValues}
validationSchema={validationSchema}
exampleCards={exampleCards}
toolInfo={{
title: 'Time Between Dates Calculator',
description:
'Calculate the exact time difference between two dates and times, with support for different timezones. This tool provides a detailed breakdown of the time difference in various units (years, months, days, hours, minutes, and seconds).'
}}
getGroups={({ values, updateField }) => [
{
title: 'Start Date & Time',
component: (
<Box>
<TextFieldWithDesc
description="Start Date"
value={values.startDate}
onOwnChange={(val) => updateField('startDate', val)}
type="date"
/>
<TextFieldWithDesc
description="Start Time"
value={values.startTime}
onOwnChange={(val) => updateField('startTime', val)}
type="time"
/>
<SelectWithDesc
description="Start Timezone"
selected={values.startTimezone}
onChange={(val: string) => updateField('startTimezone', val)}
options={timezoneOptions}
/>
</Box>
)
},
{
title: 'End Date & Time',
component: (
<Box>
<TextFieldWithDesc
description="End Date"
value={values.endDate}
onOwnChange={(val) => updateField('endDate', val)}
type="date"
/>
<TextFieldWithDesc
description="End Time"
value={values.endTime}
onOwnChange={(val) => updateField('endTime', val)}
type="time"
/>
<SelectWithDesc
description="End Timezone"
selected={values.endTimezone}
onChange={(val: string) => updateField('endTimezone', val)}
options={timezoneOptions}
/>
</Box>
)
}
]}
compute={(values) => {
try {
// Create Date objects with timezone consideration
const startDateTime = getTimeWithTimezone(
values.startDate,
values.startTime,
values.startTimezone
);
const endDateTime = getTimeWithTimezone(
values.endDate,
values.endTime,
values.endTimezone
);
// Calculate time difference
const difference = calculateTimeBetweenDates(
startDateTime,
endDateTime
);
// Format the result - use 'years' as the default largest unit
if (typeof difference === 'number') {
setResult(`${difference} milliseconds`);
} else {
// Auto-determine the best unit to display based on the time difference
let bestUnit: TimeUnit = 'years';
if (difference.years > 0) {
bestUnit = 'years';
} else if (difference.months > 0) {
bestUnit = 'months';
} else if (difference.days > 0) {
bestUnit = 'days';
} else if (difference.hours > 0) {
bestUnit = 'hours';
} else if (difference.minutes > 0) {
bestUnit = 'minutes';
} else if (difference.seconds > 0) {
bestUnit = 'seconds';
} else {
bestUnit = 'milliseconds';
}
const formattedDifference = formatTimeWithLargestUnit(
difference,
bestUnit
);
setResult(formattedDifference);
}
} catch (error) {
setResult(
`Error: ${
error instanceof Error
? error.message
: 'Failed to calculate time difference'
}`
);
}
}}
/>
);
}

View File

@@ -0,0 +1,22 @@
import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('time', {
name: 'Time Between Dates',
path: 'time-between-dates',
icon: 'mdi:calendar-clock',
description:
'Calculate the exact time difference between two dates and times, with support for different timezones. This tool provides a detailed breakdown of the time difference in various units (years, months, days, hours, minutes, and seconds).',
shortDescription:
'Calculate the precise time duration between two dates with timezone support.',
keywords: [
'time',
'dates',
'difference',
'duration',
'calculator',
'timezones',
'interval'
],
component: lazy(() => import('./index'))
});

View File

@@ -0,0 +1,150 @@
type TimeUnit =
| 'milliseconds'
| 'seconds'
| 'minutes'
| 'hours'
| 'days'
| 'months'
| 'years';
interface TimeDifference {
milliseconds: number;
seconds: number;
minutes: number;
hours: number;
days: number;
months: number;
years: number;
}
export const calculateTimeBetweenDates = (
startDate: Date,
endDate: Date,
unit: TimeUnit = 'milliseconds'
): TimeDifference | number => {
// Ensure endDate is after startDate
if (endDate < startDate) {
const temp = startDate;
startDate = endDate;
endDate = temp;
}
const milliseconds = endDate.getTime() - startDate.getTime();
const seconds = Math.floor(milliseconds / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
// Approximate months and years
const startYear = startDate.getFullYear();
const startMonth = startDate.getMonth();
const endYear = endDate.getFullYear();
const endMonth = endDate.getMonth();
const months = (endYear - startYear) * 12 + (endMonth - startMonth);
const years = Math.floor(months / 12);
// If specific unit requested, return just that value
if (unit !== 'milliseconds') {
return { milliseconds, seconds, minutes, hours, days, months, years }[unit];
}
// Otherwise return the complete breakdown
return {
milliseconds,
seconds,
minutes,
hours,
days,
months,
years
};
};
export const formatTimeDifference = (
difference: TimeDifference,
includeUnits: TimeUnit[] = [
'years',
'months',
'days',
'hours',
'minutes',
'seconds'
]
): string => {
const parts: string[] = [];
if (includeUnits.includes('years') && difference.years > 0) {
parts.push(
`${difference.years} ${difference.years === 1 ? 'year' : 'years'}`
);
}
if (includeUnits.includes('months') && difference.months % 12 > 0) {
const remainingMonths = difference.months % 12;
parts.push(
`${remainingMonths} ${remainingMonths === 1 ? 'month' : 'months'}`
);
}
if (includeUnits.includes('days') && difference.days % 30 > 0) {
const remainingDays = difference.days % 30;
parts.push(`${remainingDays} ${remainingDays === 1 ? 'day' : 'days'}`);
}
if (includeUnits.includes('hours') && difference.hours % 24 > 0) {
const remainingHours = difference.hours % 24;
parts.push(`${remainingHours} ${remainingHours === 1 ? 'hour' : 'hours'}`);
}
if (includeUnits.includes('minutes') && difference.minutes % 60 > 0) {
const remainingMinutes = difference.minutes % 60;
parts.push(
`${remainingMinutes} ${remainingMinutes === 1 ? 'minute' : 'minutes'}`
);
}
if (includeUnits.includes('seconds') && difference.seconds % 60 > 0) {
const remainingSeconds = difference.seconds % 60;
parts.push(
`${remainingSeconds} ${remainingSeconds === 1 ? 'second' : 'seconds'}`
);
}
if (parts.length === 0) {
if (includeUnits.includes('milliseconds')) {
parts.push(
`${difference.milliseconds} ${
difference.milliseconds === 1 ? 'millisecond' : 'milliseconds'
}`
);
} else {
parts.push('0 seconds');
}
}
return parts.join(', ');
};
export const getTimeWithTimezone = (
dateString: string,
timeString: string,
timezone: string
): Date => {
// Combine date and time
const dateTimeString = `${dateString}T${timeString}`;
// Create a date object in the local timezone
const dateObject = new Date(dateTimeString);
// If timezone is provided, adjust the date
if (timezone && timezone !== 'local') {
// Create date string with the timezone identifier
const dateWithTimezone = new Date(
dateTimeString + timezone.replace('GMT', '')
);
return dateWithTimezone;
}
return dateObject;
};

View File

@@ -21,6 +21,7 @@ export type ToolCategory =
| 'video' | 'video'
| 'list' | 'list'
| 'json' | 'json'
| 'time'
| 'csv' | 'csv'
| 'time' | 'time'
| 'pdf'; | 'pdf';

View File

@@ -66,6 +66,12 @@ const categoriesConfig: {
value: value:
'Tools for working with JSON data structures prettify and minify JSON objects, flatten JSON arrays, stringify JSON values, analyze data, and much more' 'Tools for working with JSON data structures prettify and minify JSON objects, flatten JSON arrays, stringify JSON values, analyze data, and much more'
}, },
{
type: 'time',
icon: 'mdi:clock-time-five',
value:
'Tools for working with time and date calculate time differences, convert between time zones, format dates, generate date sequences, and much more.'
},
{ {
type: 'csv', type: 'csv',
icon: 'material-symbols-light:csv-outline', icon: 'material-symbols-light:csv-outline',