refactor(date-converter): improved ux and layout
This commit is contained in:
		
							parent
							
								
									5fa811a583
								
							
						
					
					
						commit
						e23dd0cdc2
					
				| @ -31,6 +31,8 @@ export default defineConfig({ | ||||
|     trace: 'on-first-retry', | ||||
| 
 | ||||
|     testIdAttribute: 'data-test-id', | ||||
|     locale: 'en-GB', | ||||
|     timezoneId: 'Europe/Paris', | ||||
|   }, | ||||
| 
 | ||||
|   /* Configure projects for major browsers */ | ||||
|  | ||||
| @ -25,7 +25,15 @@ export type ValidationAttrs = { | ||||
|   validationStatus: string | undefined; | ||||
| }; | ||||
| 
 | ||||
| export function useValidation<T>({ source, rules }: { source: Ref<T>; rules: UseValidationRule<T>[] }) { | ||||
| export function useValidation<T>({ | ||||
|   source, | ||||
|   rules, | ||||
|   watch: watchRefs = [], | ||||
| }: { | ||||
|   source: Ref<T>; | ||||
|   rules: UseValidationRule<T>[]; | ||||
|   watch?: Ref<unknown>[]; | ||||
| }) { | ||||
|   const state = reactive<{ | ||||
|     message: string; | ||||
|     status: undefined | 'error'; | ||||
| @ -42,7 +50,7 @@ export function useValidation<T>({ source, rules }: { source: Ref<T>; rules: Use | ||||
|   }); | ||||
| 
 | ||||
|   watch( | ||||
|     [source], | ||||
|     [source, ...watchRefs], | ||||
|     () => { | ||||
|       state.message = ''; | ||||
|       state.status = undefined; | ||||
|  | ||||
| @ -0,0 +1,33 @@ | ||||
| import { test, expect } from '@playwright/test'; | ||||
| 
 | ||||
| test.describe('Date time converter - json to yaml', () => { | ||||
|   test.beforeEach(async ({ page }) => { | ||||
|     await page.goto('/date-converter'); | ||||
|   }); | ||||
| 
 | ||||
|   test('Has correct title', async ({ page }) => { | ||||
|     await expect(page).toHaveTitle('Date-time converter - IT Tools'); | ||||
|   }); | ||||
| 
 | ||||
|   test('Format is auto detected from a date and the date is correctly converted', async ({ page }) => { | ||||
|     const initialFormat = await page.getByTestId('date-time-converter-format-select').innerText(); | ||||
|     expect(initialFormat.trim()).toEqual('Timestamp'); | ||||
| 
 | ||||
|     await page.getByTestId('date-time-converter-input').fill('2023-04-12T23:10:24+02:00'); | ||||
| 
 | ||||
|     const detectedFormat = await page.getByTestId('date-time-converter-format-select').innerText(); | ||||
|     expect(detectedFormat.trim()).toEqual('ISO 8601'); | ||||
| 
 | ||||
|     expect((await page.getByTestId('JS locale date string').inputValue()).trim()).toEqual( | ||||
|       'Wed Apr 12 2023 23:10:24 GMT+0200 (Central European Summer Time)', | ||||
|     ); | ||||
|     expect((await page.getByTestId('ISO 8601').inputValue()).trim()).toEqual('2023-04-12T23:10:24+02:00'); | ||||
|     expect((await page.getByTestId('ISO 9075').inputValue()).trim()).toEqual('2023-04-12 23:10:24'); | ||||
|     expect((await page.getByTestId('Unix timestamp').inputValue()).trim()).toEqual('1681333824'); | ||||
|     expect((await page.getByTestId('RFC 7231').inputValue()).trim()).toEqual('Wed, 12 Apr 2023 21:10:24 GMT'); | ||||
|     expect((await page.getByTestId('RFC 3339').inputValue()).trim()).toEqual('2023-04-12T23:10:24+02:00'); | ||||
|     expect((await page.getByTestId('Timestamp').inputValue()).trim()).toEqual('1681333824000'); | ||||
|     expect((await page.getByTestId('UTC format').inputValue()).trim()).toEqual('Wed, 12 Apr 2023 21:10:24 GMT'); | ||||
|     expect((await page.getByTestId('Mongo ObjectID').inputValue()).trim()).toEqual('64371e400000000000000000'); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										142
									
								
								src/tools/date-time-converter/date-time-converter.models.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								src/tools/date-time-converter/date-time-converter.models.test.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,142 @@ | ||||
| import { describe, test, expect } from 'vitest'; | ||||
| import { | ||||
|   isISO8601DateTimeString, | ||||
|   isISO9075DateString, | ||||
|   isRFC3339DateString, | ||||
|   isRFC7231DateString, | ||||
|   isUnixTimestamp, | ||||
|   isTimestamp, | ||||
|   isUTCDateString, | ||||
|   isMongoObjectId, | ||||
| } from './date-time-converter.models'; | ||||
| 
 | ||||
| describe('date-time-converter models', () => { | ||||
|   describe('isISO8601DateTimeString', () => { | ||||
|     test('should return true for valid ISO 8601 date strings', () => { | ||||
|       expect(isISO8601DateTimeString('2021-01-01T00:00:00.000Z')).toBe(true); | ||||
|       expect(isISO8601DateTimeString('2023-04-12T14:56:00+01:00')).toBe(true); | ||||
|       expect(isISO8601DateTimeString('20230412T145600+0100')).toBe(true); | ||||
|       expect(isISO8601DateTimeString('20230412T145600Z')).toBe(true); | ||||
|       expect(isISO8601DateTimeString('2016-02-01')).toBe(true); | ||||
|       expect(isISO8601DateTimeString('2016')).toBe(true); | ||||
|     }); | ||||
| 
 | ||||
|     test('should return false for invalid ISO 8601 date strings', () => { | ||||
|       expect(isISO8601DateTimeString()).toBe(false); | ||||
|       expect(isISO8601DateTimeString('')).toBe(false); | ||||
|       expect(isISO8601DateTimeString('qsdqsd')).toBe(false); | ||||
|       expect(isISO8601DateTimeString('2016-02-01-')).toBe(false); | ||||
|       expect(isISO8601DateTimeString('2021-01-01T00:00:00.')).toBe(false); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('isISO9075DateString', () => { | ||||
|     test('should return true for valid ISO 9075 date strings', () => { | ||||
|       expect(isISO9075DateString('2022-01-01 12:00:00Z')).toBe(true); | ||||
|       expect(isISO9075DateString('2022-01-01 12:00:00.123456Z')).toBe(true); | ||||
|       expect(isISO9075DateString('2022-01-01 12:00:00+01:00')).toBe(true); | ||||
|       expect(isISO9075DateString('2022-01-01 12:00:00-05:00')).toBe(true); | ||||
|     }); | ||||
| 
 | ||||
|     test('should return false for invalid ISO 9075 date strings', () => { | ||||
|       expect(isISO9075DateString('2022/01/01T12:00:00Z')).toBe(false); | ||||
|       expect(isISO9075DateString('2022-01-01 12:00:00.123456789Z')).toBe(false); | ||||
|       expect(isISO9075DateString('2022-01-01 12:00:00+1:00')).toBe(false); | ||||
|       expect(isISO9075DateString('2022-01-01 12:00:00-05:')).toBe(false); | ||||
|       expect(isISO9075DateString('2022-01-01 12:00:00-05:00:00')).toBe(false); | ||||
|       expect(isISO9075DateString('2022-01-01')).toBe(false); | ||||
|       expect(isISO9075DateString('12:00:00Z')).toBe(false); | ||||
|       expect(isISO9075DateString('2022-01-01T12:00:00Zfoo')).toBe(false); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('isRFC3339DateString', () => { | ||||
|     test('should return true for valid RFC 3339 date strings', () => { | ||||
|       expect(isRFC3339DateString('2022-01-01T12:00:00Z')).toBe(true); | ||||
|       expect(isRFC3339DateString('2022-01-01T12:00:00.123456789Z')).toBe(true); | ||||
|       expect(isRFC3339DateString('2022-01-01T12:00:00.123456789+01:00')).toBe(true); | ||||
|       expect(isRFC3339DateString('2022-01-01T12:00:00-05:00')).toBe(true); | ||||
|     }); | ||||
| 
 | ||||
|     test('should return false for invalid RFC 3339 date strings', () => { | ||||
|       expect(isRFC3339DateString('2022/01/01T12:00:00Z')).toBe(false); | ||||
|       expect(isRFC3339DateString('2022-01-01T12:00:00.123456789+1:00')).toBe(false); | ||||
|       expect(isRFC3339DateString('2022-01-01T12:00:00-05:')).toBe(false); | ||||
|       expect(isRFC3339DateString('2022-01-01T12:00:00-05:00:00')).toBe(false); | ||||
|       expect(isRFC3339DateString('2022-01-01')).toBe(false); | ||||
|       expect(isRFC3339DateString('12:00:00Z')).toBe(false); | ||||
|       expect(isRFC3339DateString('2022-01-01T12:00:00Zfoo')).toBe(false); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('isRFC7231DateString', () => { | ||||
|     test('should return true for valid RFC 7231 date strings', () => { | ||||
|       expect(isRFC7231DateString('Sun, 06 Nov 1994 08:49:37 GMT')).toBe(true); | ||||
|       expect(isRFC7231DateString('Tue, 22 Apr 2014 07:00:00 GMT')).toBe(true); | ||||
|     }); | ||||
| 
 | ||||
|     test('should return false for invalid RFC 7231 date strings', () => { | ||||
|       expect(isRFC7231DateString('06 Nov 1994 08:49:37 GMT')).toBe(false); | ||||
|       expect(isRFC7231DateString('Sun, 06 Nov 94 08:49:37 GMT')).toBe(false); | ||||
|       expect(isRFC7231DateString('Sun, 06 Nov 1994 8:49:37 GMT')).toBe(false); | ||||
|       expect(isRFC7231DateString('Sun, 06 Nov 1994 08:49:37 GMT-0500')).toBe(false); | ||||
|       expect(isRFC7231DateString('Sun, 06 November 1994 08:49:37 GMT')).toBe(false); | ||||
|       expect(isRFC7231DateString('Sunday, 06 Nov 1994 08:49:37 GMT')).toBe(false); | ||||
|       expect(isRFC7231DateString('06 Nov 1994')).toBe(false); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('isUnixTimestamp', () => { | ||||
|     test('should return true for valid Unix timestamps', () => { | ||||
|       expect(isUnixTimestamp('1649789394')).toBe(true); | ||||
|       expect(isUnixTimestamp('1234567890')).toBe(true); | ||||
|       expect(isUnixTimestamp('0')).toBe(true); | ||||
|     }); | ||||
| 
 | ||||
|     test('should return false for invalid Unix timestamps', () => { | ||||
|       expect(isUnixTimestamp('foo')).toBe(false); | ||||
|       expect(isUnixTimestamp('')).toBe(false); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('isTimestamp', () => { | ||||
|     test('should return true for valid Unix timestamps in milliseconds', () => { | ||||
|       expect(isTimestamp('1649792026123')).toBe(true); | ||||
|       expect(isTimestamp('1234567890000')).toBe(true); | ||||
|       expect(isTimestamp('0')).toBe(true); | ||||
|     }); | ||||
| 
 | ||||
|     test('should return false for invalid Unix timestamps in milliseconds', () => { | ||||
|       expect(isTimestamp('foo')).toBe(false); | ||||
|       expect(isTimestamp('')).toBe(false); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('isUTCDateString', () => { | ||||
|     test('should return true for valid UTC date strings', () => { | ||||
|       expect(isUTCDateString('Sun, 06 Nov 1994 08:49:37 GMT')).toBe(true); | ||||
|       expect(isUTCDateString('Tue, 22 Apr 2014 07:00:00 GMT')).toBe(true); | ||||
|     }); | ||||
| 
 | ||||
|     test('should return false for invalid UTC date strings', () => { | ||||
|       expect(isUTCDateString('06 Nov 1994 08:49:37 GMT')).toBe(false); | ||||
|       expect(isUTCDateString('16497920261')).toBe(false); | ||||
|       expect(isUTCDateString('foo')).toBe(false); | ||||
|       expect(isUTCDateString('')).toBe(false); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('isMongoObjectId', () => { | ||||
|     test('should return true for valid Mongo ObjectIds', () => { | ||||
|       expect(isMongoObjectId('507f1f77bcf86cd799439011')).toBe(true); | ||||
|       expect(isMongoObjectId('507f1f77bcf86cd799439012')).toBe(true); | ||||
|     }); | ||||
| 
 | ||||
|     test('should return false for invalid Mongo ObjectIds', () => { | ||||
|       expect(isMongoObjectId('507f1f77bcf86cd79943901')).toBe(false); | ||||
|       expect(isMongoObjectId('507f1f77bcf86cd79943901z')).toBe(false); | ||||
|       expect(isMongoObjectId('foo')).toBe(false); | ||||
|       expect(isMongoObjectId('')).toBe(false); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										46
									
								
								src/tools/date-time-converter/date-time-converter.models.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/tools/date-time-converter/date-time-converter.models.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | ||||
| import _ from 'lodash'; | ||||
| 
 | ||||
| export { | ||||
|   isISO8601DateTimeString, | ||||
|   isISO9075DateString, | ||||
|   isRFC3339DateString, | ||||
|   isRFC7231DateString, | ||||
|   isUnixTimestamp, | ||||
|   isTimestamp, | ||||
|   isUTCDateString, | ||||
|   isMongoObjectId, | ||||
| }; | ||||
| 
 | ||||
| const ISO8601_REGEX = | ||||
|   /^([+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([.,]\d+(?!:))?)?(\17[0-5]\d([.,]\d+)?)?([zZ]|([+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/; | ||||
| const ISO9075_REGEX = | ||||
|   /^([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})(\.[0-9]{1,6})?(([+-])([0-9]{2}):([0-9]{2})|Z)?$/; | ||||
| 
 | ||||
| const RFC3339_REGEX = | ||||
|   /^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(\.[0-9]{1,9})?(([+-])([0-9]{2}):([0-9]{2})|Z)$/; | ||||
| 
 | ||||
| const RFC7231_REGEX = /^[A-Za-z]{3},\s[0-9]{2}\s[A-Za-z]{3}\s[0-9]{4}\s[0-9]{2}:[0-9]{2}:[0-9]{2}\sGMT$/; | ||||
| 
 | ||||
| function createRegexMatcher(regex: RegExp) { | ||||
|   return (date?: string) => !_.isNil(date) && regex.test(date); | ||||
| } | ||||
| 
 | ||||
| const isISO8601DateTimeString = createRegexMatcher(ISO8601_REGEX); | ||||
| const isISO9075DateString = createRegexMatcher(ISO9075_REGEX); | ||||
| const isRFC3339DateString = createRegexMatcher(RFC3339_REGEX); | ||||
| const isRFC7231DateString = createRegexMatcher(RFC7231_REGEX); | ||||
| const isUnixTimestamp = createRegexMatcher(/^[0-9]{1,10}$/); | ||||
| const isTimestamp = createRegexMatcher(/^[0-9]{1,13}$/); | ||||
| const isMongoObjectId = createRegexMatcher(/^[0-9a-fA-F]{24}$/); | ||||
| 
 | ||||
| function isUTCDateString(date?: string) { | ||||
|   if (_.isNil(date)) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   try { | ||||
|     return new Date(date).toUTCString() === date; | ||||
|   } catch (_ignored) { | ||||
|     return false; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,8 @@ | ||||
| export type ToDateMapper = (value: string) => Date; | ||||
| 
 | ||||
| export type DateFormat = { | ||||
|   name: string; | ||||
|   fromDate: (date: Date) => string; | ||||
|   toDate: (value: string) => Date; | ||||
|   formatMatcher: (dateString: string) => boolean; | ||||
| }; | ||||
| @ -1,44 +1,38 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <n-card> | ||||
|       <n-space justify="center"> | ||||
|         <n-form-item label="Use current date-time ?" label-placement="left" :show-feedback="false"> | ||||
|           <n-switch v-model:value="useCurrentDate" /> | ||||
|         </n-form-item> | ||||
|       </n-space> | ||||
|       <n-form-item | ||||
|         :feedback="inputInvalid ? 'Invalid date for the current format' : ''" | ||||
|         :validation-status="inputInvalid ? 'error' : undefined" | ||||
|       > | ||||
|         <n-input-group style="flex-grow: 1"> | ||||
|           <n-select | ||||
|             v-model:value="inputFormat" | ||||
|             style="width: 200px" | ||||
|             :options="formats.map(({ name }, i) => ({ label: name, value: i }))" | ||||
|             :disabled="useCurrentDate" | ||||
|           /> | ||||
|     <n-form-item :show-label="false" v-bind="validation.attrs"> | ||||
|       <n-input-group> | ||||
|         <n-input | ||||
|           v-model:value="inputDate" | ||||
|           :on-input="onDateInputChanged" | ||||
|           placeholder="Put you date string here..." | ||||
|           clearable | ||||
|           :input-props="{ 'data-test-id': 'date-time-converter-input' }" | ||||
|         /> | ||||
| 
 | ||||
|           <n-input | ||||
|             v-model:value="inputDate" | ||||
|             :on-input="onDateInputChanged" | ||||
|             :disabled="useCurrentDate" | ||||
|             placeholder="Your date string..." | ||||
|           /> | ||||
|         </n-input-group> | ||||
|       </n-form-item> | ||||
|       <n-divider style="margin-top: 0" /> | ||||
|       <div v-for="{ name, fromDate } in formats" :key="name" style="margin: 5px 0"> | ||||
|         <n-input-group> | ||||
|           <n-input-group-label style="flex: 0 0 170px"> {{ name }}: </n-input-group-label> | ||||
|           <input-copyable :value="fromDate(baseDate)" /> | ||||
|         </n-input-group> | ||||
|       </div> | ||||
|     </n-card> | ||||
|         <n-select | ||||
|           v-model:value="formatIndex" | ||||
|           style="flex: 0 0 170px" | ||||
|           :options="formats.map(({ name }, i) => ({ label: name, value: i }))" | ||||
|           data-test-id="date-time-converter-format-select" | ||||
|         /> | ||||
|       </n-input-group> | ||||
|     </n-form-item> | ||||
|     <n-divider style="margin-top: 0" /> | ||||
|     <div v-for="{ name, fromDate } in formats" :key="name" mt-1> | ||||
|       <n-input-group> | ||||
|         <n-input-group-label style="flex: 0 0 170px"> {{ name }}: </n-input-group-label> | ||||
|         <input-copyable | ||||
|           :value="formatDateUsingFormatter(fromDate, normalizedDate)" | ||||
|           placeholder="Invalid date..." | ||||
|           :input-props="{ 'data-test-id': name }" | ||||
|         /> | ||||
|       </n-input-group> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script setup lang="ts"> | ||||
| import { useRafFn } from '@vueuse/core'; | ||||
| import { | ||||
|   formatISO, | ||||
|   formatISO9075, | ||||
| @ -47,95 +41,132 @@ import { | ||||
|   fromUnixTime, | ||||
|   getTime, | ||||
|   getUnixTime, | ||||
|   isDate, | ||||
|   parseISO, | ||||
|   parseJSON, | ||||
|   isDate, | ||||
|   isValid, | ||||
| } from 'date-fns'; | ||||
| import { ref } from 'vue'; | ||||
| import InputCopyable from '../../components/InputCopyable.vue'; | ||||
| import { withDefaultOnError } from '@/utils/defaults'; | ||||
| import { useValidation } from '@/composable/validation'; | ||||
| import type { DateFormat, ToDateMapper } from './date-time-converter.types'; | ||||
| import { | ||||
|   isISO8601DateTimeString, | ||||
|   isISO9075DateString, | ||||
|   isRFC3339DateString, | ||||
|   isRFC7231DateString, | ||||
|   isTimestamp, | ||||
|   isUTCDateString, | ||||
|   isUnixTimestamp, | ||||
|   isMongoObjectId, | ||||
| } from './date-time-converter.models'; | ||||
| 
 | ||||
| const useCurrentDate = ref(true); | ||||
| const inputDate = ref(''); | ||||
| const inputFormat = ref(6); | ||||
| const inputInvalid = ref(false); | ||||
| const baseDate = ref(new Date()); | ||||
| 
 | ||||
| useRafFn(() => { | ||||
|   if (useCurrentDate.value) { | ||||
|     baseDate.value = new Date(); | ||||
| const toDate: ToDateMapper = (date) => new Date(date); | ||||
| 
 | ||||
| function formatDateUsingFormatter(formatter: (date: Date) => string, date?: Date) { | ||||
|   if (!date || !validation.isValid) { | ||||
|     return ''; | ||||
|   } | ||||
| }); | ||||
| 
 | ||||
| function onDateInputChanged(value: string) { | ||||
|   const { toDate } = formats[inputFormat.value]; | ||||
|   inputInvalid.value = false; | ||||
| 
 | ||||
|   try { | ||||
|     const formatted: Date | string = toDate(value); | ||||
| 
 | ||||
|     if (!isDate(formatted) || isNaN(formatted.getTime())) { | ||||
|       throw new Error('Invalid date'); | ||||
|     } | ||||
| 
 | ||||
|     baseDate.value = formatted; | ||||
|   } catch (_) { | ||||
|     inputInvalid.value = true; | ||||
|   } | ||||
|   return withDefaultOnError(() => formatter(date), ''); | ||||
| } | ||||
| 
 | ||||
| type Format = { | ||||
|   name: string; | ||||
|   fromDate: (date: Date) => string; | ||||
|   toDate: (value: string) => Date; | ||||
| }; | ||||
| 
 | ||||
| const toDate: Format['toDate'] = (date) => new Date(date); | ||||
| 
 | ||||
| const formats: Format[] = [ | ||||
| const formats: DateFormat[] = [ | ||||
|   { | ||||
|     name: 'JS locale date string', | ||||
|     fromDate: (date) => date.toString(), | ||||
|     toDate, | ||||
|     formatMatcher: () => false, | ||||
|   }, | ||||
|   { | ||||
|     name: 'ISO 8601', | ||||
|     fromDate: formatISO, | ||||
|     toDate: parseISO, | ||||
|     formatMatcher: (date) => isISO8601DateTimeString(date), | ||||
|   }, | ||||
|   { | ||||
|     name: 'ISO 9075', | ||||
|     fromDate: formatISO9075, | ||||
|     toDate: parseISO, | ||||
|     formatMatcher: (date) => isISO9075DateString(date), | ||||
|   }, | ||||
|   { | ||||
|     name: 'RFC 3339', | ||||
|     fromDate: formatRFC3339, | ||||
|     toDate, | ||||
|     formatMatcher: (date) => isRFC3339DateString(date), | ||||
|   }, | ||||
|   { | ||||
|     name: 'RFC 7231', | ||||
|     fromDate: formatRFC7231, | ||||
|     toDate, | ||||
|   }, | ||||
|   { | ||||
|     name: 'Timestamp', | ||||
|     fromDate: (date) => String(getTime(date)), | ||||
|     toDate: (ms) => parseJSON(+ms), | ||||
|     formatMatcher: (date) => isRFC7231DateString(date), | ||||
|   }, | ||||
|   { | ||||
|     name: 'Unix timestamp', | ||||
|     fromDate: (date) => String(getUnixTime(date)), | ||||
|     toDate: (sec) => fromUnixTime(+sec), | ||||
|     formatMatcher: (date) => isUnixTimestamp(date), | ||||
|   }, | ||||
|   { | ||||
|     name: 'Timestamp', | ||||
|     fromDate: (date) => String(getTime(date)), | ||||
|     toDate: (ms) => parseJSON(+ms), | ||||
|     formatMatcher: (date) => isTimestamp(date), | ||||
|   }, | ||||
|   { | ||||
|     name: 'UTC format', | ||||
|     fromDate: (date) => date.toUTCString(), | ||||
|     toDate, | ||||
|     formatMatcher: (date) => isUTCDateString(date), | ||||
|   }, | ||||
|   { | ||||
|     name: 'Mongo ObjectID', | ||||
|     fromDate: (date) => Math.floor(date.getTime() / 1000).toString(16) + '0000000000000000', | ||||
|     toDate: (objectId) => new Date(parseInt(objectId.substring(0, 8), 16) * 1000), | ||||
|     formatMatcher: (date) => isMongoObjectId(date), | ||||
|   }, | ||||
| ]; | ||||
| 
 | ||||
| const formatIndex = ref(6); | ||||
| const now = useNow(); | ||||
| 
 | ||||
| const normalizedDate = computed(() => { | ||||
|   if (!inputDate.value) { | ||||
|     return now.value; | ||||
|   } | ||||
| 
 | ||||
|   const { toDate } = formats[formatIndex.value]; | ||||
| 
 | ||||
|   try { | ||||
|     return toDate(inputDate.value); | ||||
|   } catch (_ignored) { | ||||
|     return undefined; | ||||
|   } | ||||
| }); | ||||
| 
 | ||||
| function onDateInputChanged(value: string) { | ||||
|   const matchingIndex = formats.findIndex(({ formatMatcher }) => formatMatcher(value)); | ||||
|   if (matchingIndex !== -1) { | ||||
|     formatIndex.value = matchingIndex; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| const validation = useValidation({ | ||||
|   source: inputDate, | ||||
|   watch: [formatIndex], | ||||
|   rules: [ | ||||
|     { | ||||
|       message: 'This date is invalid for this format', | ||||
|       validator: (value) => | ||||
|         withDefaultOnError(() => { | ||||
|           if (value === '') return true; | ||||
| 
 | ||||
|           const maybeDate = formats[formatIndex.value].toDate(value); | ||||
|           return isDate(maybeDate) && isValid(maybeDate); | ||||
|         }, false), | ||||
|     }, | ||||
|   ], | ||||
| }); | ||||
| </script> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user