fix(date-time-converter): add UTC ISO Display and JS Date Constructor

Fix #1198
This commit is contained in:
ShareVB 2024-09-01 18:14:36 +02:00
parent 88ecf60587
commit 6fd79d6e06
5 changed files with 81 additions and 8 deletions

View file

@ -35,6 +35,7 @@
"release": "node ./scripts/release.mjs" "release": "node ./scripts/release.mjs"
}, },
"dependencies": { "dependencies": {
"@date-fns/utc": "^1.2.0",
"@it-tools/bip39": "^0.0.4", "@it-tools/bip39": "^0.0.4",
"@it-tools/oggen": "^1.3.0", "@it-tools/oggen": "^1.3.0",
"@sindresorhus/slugify": "^2.2.1", "@sindresorhus/slugify": "^2.2.1",

24
pnpm-lock.yaml generated
View file

@ -5,6 +5,9 @@ settings:
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
dependencies: dependencies:
'@date-fns/utc':
specifier: ^1.2.0
version: 1.2.0
'@it-tools/bip39': '@it-tools/bip39':
specifier: ^0.0.4 specifier: ^0.0.4
version: 0.0.4 version: 0.0.4
@ -1905,6 +1908,10 @@ packages:
vue: 3.3.4 vue: 3.3.4
dev: false dev: false
/@date-fns/utc@1.2.0:
resolution: {integrity: sha512-YLq+crMPJiBmIdkRmv9nZuZy1mVtMlDcUKlg4mvI0UsC/dZeIaGoGB5p/C4FrpeOhZ7zBTK03T58S0DFkRNMnw==}
dev: false
/@emotion/hash@0.8.0: /@emotion/hash@0.8.0:
resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==}
dev: false dev: false
@ -3341,7 +3348,7 @@ packages:
dependencies: dependencies:
'@unhead/dom': 0.5.1 '@unhead/dom': 0.5.1
'@unhead/schema': 0.5.1 '@unhead/schema': 0.5.1
'@vueuse/shared': 10.7.2(vue@3.3.4) '@vueuse/shared': 11.0.3(vue@3.3.4)
unhead: 0.5.1 unhead: 0.5.1
vue: 3.3.4 vue: 3.3.4
transitivePeerDependencies: transitivePeerDependencies:
@ -3983,10 +3990,10 @@ packages:
- vue - vue
dev: false dev: false
/@vueuse/shared@10.7.2(vue@3.3.4): /@vueuse/shared@11.0.3(vue@3.3.4):
resolution: {integrity: sha512-qFbXoxS44pi2FkgFjPvF4h7c9oMDutpyBdcJdMYIMg9XyXli2meFMuaKn+UMgsClo//Th6+beeCgqweT/79BVA==} resolution: {integrity: sha512-0rY2m6HS5t27n/Vp5cTDsKTlNnimCqsbh/fmT2LgE+aaU42EMfXo8+bNX91W9I7DDmxfuACXMmrd7d79JxkqWA==}
dependencies: dependencies:
vue-demi: 0.14.6(vue@3.3.4) vue-demi: 0.14.10(vue@3.3.4)
transitivePeerDependencies: transitivePeerDependencies:
- '@vue/composition-api' - '@vue/composition-api'
- vue - vue
@ -9120,8 +9127,8 @@ packages:
vue: 3.3.4 vue: 3.3.4
dev: false dev: false
/vue-demi@0.14.5(vue@3.3.4): /vue-demi@0.14.10(vue@3.3.4):
resolution: {integrity: sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==} resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
engines: {node: '>=12'} engines: {node: '>=12'}
hasBin: true hasBin: true
requiresBuild: true requiresBuild: true
@ -9135,8 +9142,8 @@ packages:
vue: 3.3.4 vue: 3.3.4
dev: false dev: false
/vue-demi@0.14.6(vue@3.3.4): /vue-demi@0.14.5(vue@3.3.4):
resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==} resolution: {integrity: sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==}
engines: {node: '>=12'} engines: {node: '>=12'}
hasBin: true hasBin: true
requiresBuild: true requiresBuild: true
@ -9426,6 +9433,7 @@ packages:
/workbox-google-analytics@7.0.0: /workbox-google-analytics@7.0.0:
resolution: {integrity: sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==} resolution: {integrity: sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==}
deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained
dependencies: dependencies:
workbox-background-sync: 7.0.0 workbox-background-sync: 7.0.0
workbox-core: 7.0.0 workbox-core: 7.0.0

View file

@ -2,16 +2,19 @@ import { describe, expect, test } from 'vitest';
import { import {
dateToExcelFormat, dateToExcelFormat,
excelFormatToDate, excelFormatToDate,
fromJSDate,
fromTimestamp, fromTimestamp,
isExcelFormat, isExcelFormat,
isISO8601DateTimeString, isISO8601DateTimeString,
isISO9075DateString, isISO9075DateString,
isJSDate,
isMongoObjectId, isMongoObjectId,
isRFC3339DateString, isRFC3339DateString,
isRFC7231DateString, isRFC7231DateString,
isTimestamp, isTimestamp,
isUTCDateString, isUTCDateString,
isUnixTimestamp, isUnixTimestamp,
toJSDate,
} from './date-time-converter.models'; } from './date-time-converter.models';
describe('date-time-converter models', () => { describe('date-time-converter models', () => {
@ -218,4 +221,36 @@ describe('date-time-converter models', () => {
expect(excelFormatToDate('-1000')).toEqual(new Date('1897-04-04T00:00:00.000Z')); expect(excelFormatToDate('-1000')).toEqual(new Date('1897-04-04T00:00:00.000Z'));
}); });
}); });
describe('isJSDate', () => {
test('a JS date is a new Date()', () => {
expect(isJSDate('new Date(2000, 0)')).toBe(true);
expect(isJSDate('new Date(2000, 0, 1, 12, 12)')).toBe(true);
expect(isJSDate('new Date(2000, 0, 1, 12, 12, 12)')).toBe(true);
expect(isJSDate('new Date(2000, 0, 1, 12, 12, 12, 1)')).toBe(true);
expect(isJSDate('new Date(2000)')).toBe(false);
expect(isJSDate('')).toBe(false);
expect(isJSDate('foo')).toBe(false);
expect(isJSDate('1.1.1')).toBe(false);
});
});
describe('fromJSDate', () => {
test('convert a JS new Date() to date', () => {
expect(fromJSDate('new Date(2000, 0)')).toEqual(new Date(2000, 0));
expect(fromJSDate('new Date(2000, 0, 1, 12, 12)')).toEqual(new Date(2000, 0, 1, 12, 12));
expect(fromJSDate('new Date(2000, 0, 1, 12, 12, 12)')).toEqual(new Date(2000, 0, 1, 12, 12, 12));
expect(fromJSDate('new Date(2000, 0, 1, 12, 12, 12, 1)')).toEqual(new Date(2000, 0, 1, 12, 12, 12, 1));
});
});
describe('toJSDate', () => {
test('convert a date to JS new Date()', () => {
expect(toJSDate(new Date(2000, 0))).toEqual('new Date(2000, 0, 1, 0, 0, 0, 0);');
expect(toJSDate(new Date(2000, 0, 1, 12, 12))).toEqual('new Date(2000, 0, 1, 12, 12, 0, 0);');
expect(toJSDate(new Date(2000, 0, 1, 12, 12, 12))).toEqual('new Date(2000, 0, 1, 12, 12, 12, 0);');
expect(toJSDate(new Date(2000, 0, 1, 12, 12, 12, 1))).toEqual('new Date(2000, 0, 1, 12, 12, 12, 1);');
});
});
}); });

View file

@ -15,6 +15,9 @@ export {
isExcelFormat, isExcelFormat,
fromTimestamp, fromTimestamp,
isTimestampMicroSeconds, isTimestampMicroSeconds,
isJSDate,
fromJSDate,
toJSDate,
}; };
const ISO8601_REGEX const ISO8601_REGEX
@ -29,6 +32,8 @@ const RFC7231_REGEX = /^[A-Za-z]{3},\s[0-9]{2}\s[A-Za-z]{3}\s[0-9]{4}\s[0-9]{2}:
const EXCEL_FORMAT_REGEX = /^-?\d+(\.\d+)?$/; const EXCEL_FORMAT_REGEX = /^-?\d+(\.\d+)?$/;
const JS_DATE_REGEX = /^new\s+Date\(\s*(?:(\d+)\s*,\s*)(?:(\d|11)\s*,\s*(?:(\d+)\s*,\s*(?:(\d+)\s*,\s*(?:(\d+)\s*,\s*(?:(\d+)\s*,\s*)?)?)?)?)?(\d+)\)\s*;?$/;
function createRegexMatcher(regex: RegExp) { function createRegexMatcher(regex: RegExp) {
return (date?: string) => !_.isNil(date) && regex.test(date); return (date?: string) => !_.isNil(date) && regex.test(date);
} }
@ -43,6 +48,14 @@ const isTimestampMilliSeconds = createRegexMatcher(/^[0-9]{1,13}$/);
const isTimestampMicroSeconds = createRegexMatcher(/^[0-9]{16}$/); const isTimestampMicroSeconds = createRegexMatcher(/^[0-9]{16}$/);
const isMongoObjectId = createRegexMatcher(/^[0-9a-fA-F]{24}$/); const isMongoObjectId = createRegexMatcher(/^[0-9a-fA-F]{24}$/);
const isJSDate = createRegexMatcher(JS_DATE_REGEX);
function fromJSDate(date: string): Date {
const res = JS_DATE_REGEX.exec(date);
const parts = (res || []).filter(p => p !== undefined).map(p => Number.parseInt(p, 10)).slice(1);
return new (Function.prototype.bind.apply(Date, [null, ...parts]))();
}
const toJSDate = (date: Date) => `new Date(${date.getFullYear()}, ${date.getMonth()}, ${date.getDate()}, ${date.getHours()}, ${date.getMinutes()}, ${date.getSeconds()}, ${date.getMilliseconds()});`;
const isExcelFormat = createRegexMatcher(EXCEL_FORMAT_REGEX); const isExcelFormat = createRegexMatcher(EXCEL_FORMAT_REGEX);
function isUTCDateString(date?: string) { function isUTCDateString(date?: string) {

View file

@ -11,20 +11,24 @@ import {
isValid, isValid,
parseISO, parseISO,
} from 'date-fns'; } from 'date-fns';
import { UTCDate } from '@date-fns/utc';
import type { DateFormat, ToDateMapper } from './date-time-converter.types'; import type { DateFormat, ToDateMapper } from './date-time-converter.types';
import { import {
dateToExcelFormat, dateToExcelFormat,
excelFormatToDate, excelFormatToDate,
fromJSDate,
fromTimestamp, fromTimestamp,
isExcelFormat, isExcelFormat,
isISO8601DateTimeString, isISO8601DateTimeString,
isISO9075DateString, isISO9075DateString,
isJSDate,
isMongoObjectId, isMongoObjectId,
isRFC3339DateString, isRFC3339DateString,
isRFC7231DateString, isRFC7231DateString,
isTimestamp, isTimestamp,
isUTCDateString, isUTCDateString,
isUnixTimestamp, isUnixTimestamp,
toJSDate,
} from './date-time-converter.models'; } from './date-time-converter.models';
import { withDefaultOnError } from '@/utils/defaults'; import { withDefaultOnError } from '@/utils/defaults';
import { useValidation } from '@/composable/validation'; import { useValidation } from '@/composable/validation';
@ -46,6 +50,12 @@ const formats: DateFormat[] = [
toDate: parseISO, toDate: parseISO,
formatMatcher: date => isISO8601DateTimeString(date), formatMatcher: date => isISO8601DateTimeString(date),
}, },
{
name: 'ISO 8601 UTC',
fromDate: date => (new UTCDate(date)).toISOString(),
toDate: parseISO,
formatMatcher: date => isISO8601DateTimeString(date),
},
{ {
name: 'ISO 9075', name: 'ISO 9075',
fromDate: formatISO9075, fromDate: formatISO9075,
@ -94,6 +104,12 @@ const formats: DateFormat[] = [
toDate: excelFormatToDate, toDate: excelFormatToDate,
formatMatcher: isExcelFormat, formatMatcher: isExcelFormat,
}, },
{
name: 'JS Date',
fromDate: date => toJSDate(date),
toDate: date => fromJSDate(date),
formatMatcher: isJSDate,
},
]; ];
const formatIndex = ref(6); const formatIndex = ref(6);