Add new tool - epoch converter
This commit is contained in:
		
							parent
							
								
									d7578e23a5
								
							
						
					
					
						commit
						00b1148e3a
					
				
							
								
								
									
										1
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -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'] | ||||
|  | ||||
							
								
								
									
										31
									
								
								src/tools/epoch-converter/epoch-converter.e2e.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/tools/epoch-converter/epoch-converter.e2e.spec.ts
									
									
									
									
									
										Normal 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(); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										36
									
								
								src/tools/epoch-converter/epoch-converter.service.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/tools/epoch-converter/epoch-converter.service.test.ts
									
									
									
									
									
										Normal 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); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										23
									
								
								src/tools/epoch-converter/epoch-converter.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/tools/epoch-converter/epoch-converter.service.ts
									
									
									
									
									
										Normal 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); | ||||
| } | ||||
							
								
								
									
										115
									
								
								src/tools/epoch-converter/epoch-converter.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								src/tools/epoch-converter/epoch-converter.vue
									
									
									
									
									
										Normal 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> | ||||
							
								
								
									
										12
									
								
								src/tools/epoch-converter/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/tools/epoch-converter/index.ts
									
									
									
									
									
										Normal 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'), | ||||
| }); | ||||
| @ -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, | ||||
|     ], | ||||
|   }, | ||||
|   { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user