1
0
Fork 0
mirror of https://github.com/CorentinTh/it-tools.git synced 2025-08-06 14:05:18 +02:00

Add new tool - epoch converter

This commit is contained in:
Bryan Liao 2025-06-23 13:31:38 -07:00
parent d7578e23a5
commit 00b1148e3a
7 changed files with 220 additions and 1 deletions

1
components.d.ts vendored
View file

@ -77,6 +77,7 @@ declare module '@vue/runtime-core' {
EmojiGrid: typeof import('./src/tools/emoji-picker/emoji-grid.vue')['default']
EmojiPicker: typeof import('./src/tools/emoji-picker/emoji-picker.vue')['default']
Encryption: typeof import('./src/tools/encryption/encryption.vue')['default']
EpochConverter: typeof import('./src/tools/epoch-converter/epoch-converter.vue')['default']
EtaCalculator: typeof import('./src/tools/eta-calculator/eta-calculator.vue')['default']
FavoriteButton: typeof import('./src/components/FavoriteButton.vue')['default']
FormatTransformer: typeof import('./src/components/FormatTransformer.vue')['default']

View file

@ -0,0 +1,31 @@
import { expect, test } from '@playwright/test';
test.describe('Tool - Epoch converter', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/epoch-converter');
});
test('Has correct title', async ({ page }) => {
await expect(page).toHaveTitle('Epoch converter - IT Tools');
});
test('Converts epoch to human-readable date', async ({ page }) => {
await page.getByPlaceholder('Enter epoch timestamp').fill('1750452480');
await page.getByRole('button', { name: 'Convert to Date' }).click();
await expect(page.getByText('6/23/2025, 12:31:00 PM')).toBeVisible();
});
test('Converts known date to epoch timestamp', async ({ page }) => {
await page.getByPlaceholder('YYYY').fill('2025');
await page.getByPlaceholder('MM').first().fill('06');
await page.getByPlaceholder('DD').fill('20');
await page.getByPlaceholder('HH').fill('13');
await page.getByPlaceholder('MM').nth(1).fill('48');
await page.getByPlaceholder('SS').fill('00');
await page.getByRole('button', { name: 'Convert to Epoch' }).click();
await expect(page.getByText('1750452480')).toBeVisible();
});
});

View file

@ -0,0 +1,36 @@
import { describe, expect, it } from 'vitest';
import { dateToEpoch, epochToDate } from './epoch-converter.service';
describe('epochToDate', () => {
it('converts known epoch seconds to correct local date string', () => {
const epoch = 1750707060;
const expectedDate = '6/23/2025, 12:31:00 PM';
const result = epochToDate(epoch);
expect(result).toBe(expectedDate);
});
it('throws for invalid string input', () => {
expect(() => epochToDate('not-a-number')).toThrowError(TypeError);
});
it('throws for NaN input', () => {
expect(() => epochToDate(Number.NaN)).toThrowError(TypeError);
});
});
describe('dateToEpoch', () => {
it('converts a known date to correct epoch (2025-06-20 13:48:00)', () => {
const input = '2025-06-20T13:48:00';
const expectedEpoch = 1750452480;
const result = dateToEpoch(input);
expect(result).toBe(expectedEpoch);
});
it('throws for invalid date string', () => {
expect(() => dateToEpoch('not-a-date')).toThrowError(TypeError);
});
it('throws for empty string', () => {
expect(() => dateToEpoch('')).toThrowError(TypeError);
});
});

View file

@ -0,0 +1,23 @@
// Convert Epoch to Human Readable Date
export function epochToDate(epoch: string | number): string {
const num = typeof epoch === 'string' ? Number.parseInt(epoch, 10) : epoch;
if (Number.isNaN(num)) {
throw new TypeError('Invalid epoch timestamp');
}
const timestamp = num < 1e12 ? num * 1000 : num;
return new Date(timestamp).toLocaleString();
}
// Convert Human Readable Date to Epoch
export function dateToEpoch(dateString: string): number {
const date = new Date(dateString);
if (Number.isNaN(date.getTime())) {
throw new TypeError('Invalid date string');
}
return Math.floor(date.getTime() / 1000);
}

View file

@ -0,0 +1,115 @@
<script setup lang="ts">
import { ref } from 'vue';
import { dateToEpoch, epochToDate } from './epoch-converter.service.ts';
const epochInput = ref('');
const dateOutput = ref('');
const epochOutput = ref('');
const error = ref<string | null>(null);
const dateParts = ref({
year: '',
month: '',
day: '',
hour: '',
minute: '',
second: '',
});
function convertEpochToDate() {
error.value = null;
try {
dateOutput.value = epochToDate(epochInput.value);
}
catch (e: any) {
error.value = e.message;
}
}
function convertDateToEpoch() {
error.value = null;
try {
const { year, month, day, hour, minute, second } = dateParts.value;
const isoString = `${year}-${pad(month)}-${pad(day)}T${pad(hour)}:${pad(minute)}:${pad(second)}`;
epochOutput.value = dateToEpoch(isoString);
}
catch (e: any) {
error.value = e.message;
}
}
function pad(value: string): string {
return value.toString().padStart(2, '0');
}
</script>
<template>
<c-card title="Epoch Time Converter (24 hour time format)" class="mx-auto max-w-4xl px-4">
<!-- Epoch to Date -->
<div class="mb-8">
<div class="mb-2 font-semibold">
Epoch to Date
</div>
<c-input-text
v-model:value="epochInput"
placeholder="Enter epoch timestamp (e.g. 1718822594)"
class="mb-4"
raw-text
/>
<c-button @click="convertEpochToDate">
Convert to Date
</c-button>
<div v-if="dateOutput" class="mt-4 text-sm text-green-400">
Human-Readable Date: <strong>{{ dateOutput }}</strong>
</div>
</div>
<!-- Date to Epoch -->
<div class="mb-8">
<div class="mb-2 font-semibold">
Date to Epoch
</div>
<div class="mb-4 max-w-sm space-y-3">
<div class="flex items-center">
<label class="w-24 text-sm font-medium">Year:</label>
<c-input-text v-model:value="dateParts.year" placeholder="YYYY" class="ml-auto w-32" />
</div>
<div class="flex items-center">
<label class="w-24 text-sm font-medium">Month:</label>
<c-input-text v-model:value="dateParts.month" placeholder="MM" class="ml-auto w-24" />
</div>
<div class="flex items-center">
<label class="w-24 text-sm font-medium">Day:</label>
<c-input-text v-model:value="dateParts.day" placeholder="DD" class="ml-auto w-24" />
</div>
<div class="flex items-center">
<label class="w-24 text-sm font-medium">Hour:</label>
<c-input-text v-model:value="dateParts.hour" placeholder="HH" class="ml-auto w-24" />
</div>
<div class="flex items-center">
<label class="w-24 text-sm font-medium">Minute:</label>
<c-input-text v-model:value="dateParts.minute" placeholder="MM" class="ml-auto w-24" />
</div>
<div class="flex items-center">
<label class="w-24 text-sm font-medium">Second:</label>
<c-input-text v-model:value="dateParts.second" placeholder="SS" class="ml-auto w-24" />
</div>
</div>
<c-button @click="convertDateToEpoch">
Convert to Epoch
</c-button>
<div v-if="epochOutput" class="mt-4 text-sm text-green-400">
Epoch Timestamp: <strong>{{ epochOutput }}</strong>
</div>
</div>
<c-alert v-if="error" type="error" class="mt-6">
{{ error }}
</c-alert>
</c-card>
</template>

View file

@ -0,0 +1,12 @@
import { ArrowsShuffle } from '@vicons/tabler';
import { defineTool } from '../tool';
export const tool = defineTool({
name: 'Epoch converter',
path: '/epoch-converter',
description: 'Converts epoch to human readable date and vice versa',
keywords: ['epoch', 'converter'],
component: () => import('./epoch-converter.vue'),
icon: ArrowsShuffle,
createdAt: new Date('2025-06-19'),
});

View file

@ -1,7 +1,7 @@
import { tool as base64FileConverter } from './base64-file-converter';
import { tool as base64StringConverter } from './base64-string-converter';
import { tool as basicAuthGenerator } from './basic-auth-generator';
import { tool as gzipDecompressor } from './gzip-decompressor';
import { tool as epochConverter } from './epoch-converter';
import { tool as emailNormalizer } from './email-normalizer';
import { tool as asciiTextDrawer } from './ascii-text-drawer';
@ -118,6 +118,7 @@ export const toolsByCategory: ToolCategory[] = [
jsonToXml,
markdownToHtml,
gzipDecompressor,
epochConverter,
],
},
{