mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-11-10 01:59:54 +05:30
feat: add Epoch Converter tool for converting between Unix timestamps and human-readable dates
This commit is contained in:
parent
816a098971
commit
549f35e27d
7 changed files with 232 additions and 2 deletions
|
|
@ -7,11 +7,13 @@ import InputFooter from './InputFooter';
|
||||||
export default function ToolTextInput({
|
export default function ToolTextInput({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
title = 'Input text'
|
title = 'Input text',
|
||||||
|
placeholder
|
||||||
}: {
|
}: {
|
||||||
title?: string;
|
title?: string;
|
||||||
value: string;
|
value: string;
|
||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
|
placeholder?: string;
|
||||||
}) {
|
}) {
|
||||||
const { showSnackBar } = useContext(CustomSnackBarContext);
|
const { showSnackBar } = useContext(CustomSnackBarContext);
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
@ -48,6 +50,7 @@ export default function ToolTextInput({
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(event) => onChange(event.target.value)}
|
onChange={(event) => onChange(event.target.value)}
|
||||||
fullWidth
|
fullWidth
|
||||||
|
placeholder={placeholder}
|
||||||
multiline
|
multiline
|
||||||
rows={10}
|
rows={10}
|
||||||
sx={{
|
sx={{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { expect, describe, it } from 'vitest';
|
||||||
|
import { epochToDate, dateToEpoch, main } from './service';
|
||||||
|
|
||||||
|
describe('epoch-converter service', () => {
|
||||||
|
it('converts epoch (seconds) to date', () => {
|
||||||
|
expect(epochToDate('1609459200')).toBe('Fri, 01 Jan 2021 00:00:00 GMT');
|
||||||
|
});
|
||||||
|
it('converts epoch (milliseconds) to date', () => {
|
||||||
|
expect(epochToDate('1609459200000')).toBe('Fri, 01 Jan 2021 00:00:00 GMT');
|
||||||
|
});
|
||||||
|
it('returns error for invalid epoch', () => {
|
||||||
|
expect(epochToDate('notanumber')).toMatch(/Invalid epoch/);
|
||||||
|
});
|
||||||
|
it('converts date string to epoch', () => {
|
||||||
|
expect(dateToEpoch('2021-01-01T00:00:00Z')).toBe('1609459200');
|
||||||
|
});
|
||||||
|
it('returns error for invalid date string', () => {
|
||||||
|
expect(dateToEpoch('notadate')).toMatch(/Invalid date/);
|
||||||
|
});
|
||||||
|
it('main: detects and converts epoch', () => {
|
||||||
|
expect(main('1609459200', {})).toBe('Fri, 01 Jan 2021 00:00:00 GMT');
|
||||||
|
});
|
||||||
|
it('main: detects and converts date', () => {
|
||||||
|
expect(main('2021-01-01T00:00:00Z', {})).toBe('1609459200');
|
||||||
|
});
|
||||||
|
it('main: returns error for invalid input', () => {
|
||||||
|
expect(main('notadate', {})).toMatch(/Invalid date/);
|
||||||
|
expect(main('notanumber', {})).toMatch(/Invalid date/);
|
||||||
|
});
|
||||||
|
});
|
||||||
143
src/pages/tools/time/epoch-converter/index.tsx
Normal file
143
src/pages/tools/time/epoch-converter/index.tsx
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
import { Box, Stack, Button, Alert } from '@mui/material';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import ToolContent from '@components/ToolContent';
|
||||||
|
import { ToolComponentProps } from '@tools/defineTool';
|
||||||
|
import ToolTextInput from '@components/input/ToolTextInput';
|
||||||
|
import ToolTextResult from '@components/result/ToolTextResult';
|
||||||
|
import { GetGroupsType } from '@components/options/ToolOptions';
|
||||||
|
import { CardExampleType } from '@components/examples/ToolExamples';
|
||||||
|
import { main, epochToDate, dateToEpoch } from './service';
|
||||||
|
import { InitialValuesType } from './types';
|
||||||
|
|
||||||
|
const initialValues: InitialValuesType = {};
|
||||||
|
|
||||||
|
const exampleCards: CardExampleType<InitialValuesType>[] = [
|
||||||
|
{
|
||||||
|
title: 'Epoch to Date (seconds)',
|
||||||
|
description: 'Convert Unix timestamp (seconds) to date',
|
||||||
|
sampleText: '1609459200',
|
||||||
|
sampleResult: 'Fri, 01 Jan 2021 00:00:00 GMT',
|
||||||
|
sampleOptions: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Epoch to Date (milliseconds)',
|
||||||
|
description: 'Convert Unix timestamp (milliseconds) to date',
|
||||||
|
sampleText: '1609459200000',
|
||||||
|
sampleResult: 'Fri, 01 Jan 2021 00:00:00 GMT',
|
||||||
|
sampleOptions: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Date to Epoch',
|
||||||
|
description: 'Convert date string to Unix timestamp (seconds)',
|
||||||
|
sampleText: '2021-01-01T00:00:00Z',
|
||||||
|
sampleResult: '1609459200',
|
||||||
|
sampleOptions: {}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function EpochConverter({
|
||||||
|
title,
|
||||||
|
longDescription
|
||||||
|
}: ToolComponentProps) {
|
||||||
|
const [input, setInput] = useState<string>('');
|
||||||
|
const [result, setResult] = useState<string>('');
|
||||||
|
const [hasInteracted, setHasInteracted] = useState<boolean>(false);
|
||||||
|
const [isValid, setIsValid] = useState<boolean | null>(null);
|
||||||
|
|
||||||
|
const compute = (_values: InitialValuesType, input: string) => {
|
||||||
|
let output = main(input, {});
|
||||||
|
const invalid = output.startsWith('Invalid');
|
||||||
|
setIsValid(!invalid);
|
||||||
|
setResult(output);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleExample = (expr: string) => {
|
||||||
|
setInput(expr);
|
||||||
|
setHasInteracted(true);
|
||||||
|
compute({}, expr);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInputChange = (val: string) => {
|
||||||
|
if (!hasInteracted) setHasInteracted(true);
|
||||||
|
setInput(val);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToolContent
|
||||||
|
title={title}
|
||||||
|
input={input}
|
||||||
|
inputComponent={
|
||||||
|
<>
|
||||||
|
<ToolTextInput
|
||||||
|
value={input}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Enter epoch timestamp or date string (e.g. 1609459200 or 2021-01-01T00:00:00Z)"
|
||||||
|
/>
|
||||||
|
<Stack direction="row" spacing={1} mt={1}>
|
||||||
|
{exampleCards.map((ex, i) => (
|
||||||
|
<Button
|
||||||
|
key={i}
|
||||||
|
size="small"
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => ex.sampleText && handleExample(ex.sampleText)}
|
||||||
|
disabled={!ex.sampleText}
|
||||||
|
>
|
||||||
|
{ex.title}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
resultComponent={
|
||||||
|
<div style={{ position: 'relative', minHeight: 80 }}>
|
||||||
|
{hasInteracted && isValid === false && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
zIndex: 2,
|
||||||
|
pointerEvents: 'auto',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
background: 'transparent'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Alert
|
||||||
|
severity="error"
|
||||||
|
style={{
|
||||||
|
width: '80%',
|
||||||
|
opacity: 0.85,
|
||||||
|
textAlign: 'center',
|
||||||
|
pointerEvents: 'none'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Invalid input. Please enter a valid epoch timestamp or date
|
||||||
|
string.
|
||||||
|
</Alert>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
filter: hasInteracted && isValid === false ? 'blur(1px)' : 'none',
|
||||||
|
transition: 'filter 0.2s'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ToolTextResult
|
||||||
|
value={hasInteracted && isValid === false ? '' : result}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
initialValues={initialValues}
|
||||||
|
exampleCards={exampleCards}
|
||||||
|
getGroups={null}
|
||||||
|
setInput={setInput}
|
||||||
|
compute={compute}
|
||||||
|
toolInfo={{ title: `What is a ${title}?`, description: longDescription }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
23
src/pages/tools/time/epoch-converter/meta.ts
Normal file
23
src/pages/tools/time/epoch-converter/meta.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { defineTool } from '@tools/defineTool';
|
||||||
|
import { lazy } from 'react';
|
||||||
|
|
||||||
|
export const tool = defineTool('time', {
|
||||||
|
name: 'Epoch Converter',
|
||||||
|
path: 'epoch-converter',
|
||||||
|
icon: 'mdi:clock-time-four-outline',
|
||||||
|
description:
|
||||||
|
'Convert Unix epoch timestamps to human-readable dates and vice versa.',
|
||||||
|
shortDescription: 'Convert between Unix timestamps and dates.',
|
||||||
|
keywords: [
|
||||||
|
'epoch',
|
||||||
|
'converter',
|
||||||
|
'timestamp',
|
||||||
|
'date',
|
||||||
|
'unix',
|
||||||
|
'time',
|
||||||
|
'convert'
|
||||||
|
],
|
||||||
|
longDescription:
|
||||||
|
'Enter a Unix timestamp (in seconds or milliseconds) to get a human-readable date, or enter a date string (e.g., 2021-01-01T00:00:00Z) to get the corresponding Unix timestamp. Useful for developers, sysadmins, and anyone working with time data.',
|
||||||
|
component: lazy(() => import('./index'))
|
||||||
|
});
|
||||||
26
src/pages/tools/time/epoch-converter/service.ts
Normal file
26
src/pages/tools/time/epoch-converter/service.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { InitialValuesType } from './types';
|
||||||
|
|
||||||
|
export function epochToDate(input: string): string {
|
||||||
|
const num = Number(input);
|
||||||
|
if (isNaN(num)) return 'Invalid epoch timestamp.';
|
||||||
|
// Support both seconds and milliseconds
|
||||||
|
const date = new Date(num > 1e12 ? num : num * 1000);
|
||||||
|
if (isNaN(date.getTime())) return 'Invalid epoch timestamp.';
|
||||||
|
return date.toUTCString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function dateToEpoch(input: string): string {
|
||||||
|
const date = new Date(input);
|
||||||
|
if (isNaN(date.getTime())) return 'Invalid date string.';
|
||||||
|
return Math.floor(date.getTime() / 1000).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function main(input: string, _options: any): string {
|
||||||
|
if (!input.trim()) return '';
|
||||||
|
// If input is a number, treat as epoch
|
||||||
|
if (/^-?\d+(\.\d+)?$/.test(input.trim())) {
|
||||||
|
return epochToDate(input.trim());
|
||||||
|
}
|
||||||
|
// Otherwise, treat as date string
|
||||||
|
return dateToEpoch(input.trim());
|
||||||
|
}
|
||||||
3
src/pages/tools/time/epoch-converter/types.ts
Normal file
3
src/pages/tools/time/epoch-converter/types.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export type InitialValuesType = {
|
||||||
|
// splitSeparator: string;
|
||||||
|
};
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { tool as timeEpochConverter } from './epoch-converter/meta';
|
||||||
import { tool as timeBetweenDates } from './time-between-dates/meta';
|
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';
|
||||||
|
|
@ -11,5 +12,6 @@ export const timeTools = [
|
||||||
convertSecondsToTime,
|
convertSecondsToTime,
|
||||||
convertTimetoSeconds,
|
convertTimetoSeconds,
|
||||||
truncateClockTime,
|
truncateClockTime,
|
||||||
timeBetweenDates
|
timeBetweenDates,
|
||||||
|
timeEpochConverter
|
||||||
];
|
];
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue