it-tools/src/tools/date-time-converter/date-time-converter.vue
2023-05-15 01:31:22 +02:00

177 lines
4.1 KiB
Vue

<template>
<div>
<n-input-group>
<c-input-text
v-model:value="inputDate"
autofocus
placeholder="Put you date string here..."
clearable
test-id="date-time-converter-input"
:validation="validation"
@update:value="onDateInputChanged"
/>
<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-divider />
<input-copyable
v-for="{ name, fromDate } in formats"
:key="name"
:label="name"
label-width="150px"
label-position="left"
label-align="right"
:value="formatDateUsingFormatter(fromDate, normalizedDate)"
placeholder="Invalid date..."
:test-id="name"
readonly
mt-2
/>
</div>
</template>
<script setup lang="ts">
import {
formatISO,
formatISO9075,
formatRFC3339,
formatRFC7231,
fromUnixTime,
getTime,
getUnixTime,
parseISO,
parseJSON,
isDate,
isValid,
} from 'date-fns';
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 inputDate = ref('');
const toDate: ToDateMapper = (date) => new Date(date);
function formatDateUsingFormatter(formatter: (date: Date) => string, date?: Date) {
if (!date || !validation.isValid) {
return '';
}
return withDefaultOnError(() => formatter(date), '');
}
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,
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>