Skip to content

Commit 187625e

Browse files
committed
Merge branch 'fix/date-time-converter' into chore/all-my-stuffs
# Conflicts: # package.json # pnpm-lock.yaml
2 parents 5016782 + 3ef7e50 commit 187625e

File tree

6 files changed

+134
-5
lines changed

6 files changed

+134
-5
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"@guolao/vue-monaco-editor": "^1.4.1",
4242
"@guyplusplus/turndown-plugin-gfm": "^1.0.7",
4343
"@huggingface/transformers": "3.0.0-alpha.14",
44+
"@date-fns/utc": "^1.2.0",
4445
"@it-tools/bip39": "^0.0.4",
4546
"@it-tools/oggen": "^1.3.0",
4647
"@regexper/render": "^1.0.0",

pnpm-lock.yaml

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/tools/date-time-converter/date-time-converter.e2e.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ test.describe('Date time converter - json to yaml', () => {
1111

1212
test('Format is auto detected from a date and the date is correctly converted', async ({ page }) => {
1313
const initialFormat = await page.getByTestId('date-time-converter-format-select').innerText();
14-
expect(initialFormat.trim()).toEqual('Timestamp');
14+
expect(initialFormat.trim()).toEqual('Unix timestamp');
1515

1616
await page.getByTestId('date-time-converter-input').fill('2023-04-12T23:10:24+02:00');
1717

@@ -22,6 +22,7 @@ test.describe('Date time converter - json to yaml', () => {
2222
'Wed Apr 12 2023 23:10:24 GMT+0200 (Central European Summer Time)',
2323
);
2424
expect((await page.getByTestId('ISO 8601').inputValue()).trim()).toEqual('2023-04-12T23:10:24+02:00');
25+
expect((await page.getByTestId('ISO 8601 UTC').inputValue()).trim()).toEqual('2023-04-12T21:10:24.000Z');
2526
expect((await page.getByTestId('ISO 9075').inputValue()).trim()).toEqual('2023-04-12 23:10:24');
2627
expect((await page.getByTestId('Unix timestamp').inputValue()).trim()).toEqual('1681333824');
2728
expect((await page.getByTestId('RFC 7231').inputValue()).trim()).toEqual('Wed, 12 Apr 2023 21:10:24 GMT');

src/tools/date-time-converter/date-time-converter.models.test.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,20 @@ import { describe, expect, test } from 'vitest';
22
import {
33
dateToExcelFormat,
44
excelFormatToDate,
5+
fromJSDate,
6+
fromTimestamp,
57
isExcelFormat,
68
isISO8601DateTimeString,
79
isISO9075DateString,
10+
isJSDate,
811
isMongoObjectId,
912
isRFC3339DateString,
1013
isRFC7231DateString,
1114
isTimestamp,
15+
isTimestampMicroSeconds,
1216
isUTCDateString,
1317
isUnixTimestamp,
18+
toJSDate,
1419
} from './date-time-converter.models';
1520

1621
describe('date-time-converter models', () => {
@@ -113,6 +118,45 @@ describe('date-time-converter models', () => {
113118
expect(isTimestamp('foo')).toBe(false);
114119
expect(isTimestamp('')).toBe(false);
115120
});
121+
122+
test('should return true for valid Unix timestamps in microseconds', () => {
123+
expect(isTimestamp('1701227351995845')).toBe(true);
124+
});
125+
126+
test('should return false for invalid Unix timestamps in microseconds', () => {
127+
expect(isTimestamp('170122735199584')).toBe(false);
128+
expect(isTimestamp('17012273519958')).toBe(false);
129+
});
130+
});
131+
132+
describe('isTimestampMicroSeconds', () => {
133+
test('should return true for valid Unix timestamps in microseconds', () => {
134+
expect(isTimestampMicroSeconds('1649792026123123')).toBe(true);
135+
expect(isTimestampMicroSeconds('1701227351995845')).toBe(true);
136+
});
137+
138+
test('should return false for invalid Unix timestamps in microseconds', () => {
139+
expect(isTimestampMicroSeconds('foo')).toBe(false);
140+
expect(isTimestampMicroSeconds('')).toBe(false);
141+
});
142+
143+
test('should return false for invalid Unix timestamps not in microseconds', () => {
144+
expect(isTimestampMicroSeconds('170122735199584')).toBe(false);
145+
expect(isTimestampMicroSeconds('17012273519958')).toBe(false);
146+
});
147+
});
148+
149+
describe('fromTimestamp', () => {
150+
test('should return valid Date for valid Unix timestamps in microseconds', () => {
151+
expect(fromTimestamp('1649792026123123').toString()).toBe(new Date(1649792026123).toString());
152+
expect(fromTimestamp('1701227351995845').toString()).toBe(new Date(1701227351995).toString());
153+
expect(fromTimestamp('0').toString()).toBe(new Date(0).toString());
154+
});
155+
156+
test('should return Date(0) for invalid Unix timestamps not in microseconds', () => {
157+
expect(fromTimestamp('170122735199584').toString()).toBe(new Date(0).toString());
158+
expect(fromTimestamp('17012273519958').toString()).toBe(new Date(0).toString());
159+
});
116160
});
117161

118162
describe('isUTCDateString', () => {
@@ -177,4 +221,36 @@ describe('date-time-converter models', () => {
177221
expect(excelFormatToDate('-1000')).toEqual(new Date('1897-04-04T00:00:00.000Z'));
178222
});
179223
});
224+
225+
describe('isJSDate', () => {
226+
test('a JS date is a new Date()', () => {
227+
expect(isJSDate('new Date(2000, 0)')).toBe(true);
228+
expect(isJSDate('new Date(2000, 0, 1, 12, 12)')).toBe(true);
229+
expect(isJSDate('new Date(2000, 0, 1, 12, 12, 12)')).toBe(true);
230+
expect(isJSDate('new Date(2000, 0, 1, 12, 12, 12, 1)')).toBe(true);
231+
232+
expect(isJSDate('new Date(2000)')).toBe(false);
233+
expect(isJSDate('')).toBe(false);
234+
expect(isJSDate('foo')).toBe(false);
235+
expect(isJSDate('1.1.1')).toBe(false);
236+
});
237+
});
238+
239+
describe('fromJSDate', () => {
240+
test('convert a JS new Date() to date', () => {
241+
expect(fromJSDate('new Date(2000, 0)')).toEqual(new Date(2000, 0));
242+
expect(fromJSDate('new Date(2000, 0, 1, 12, 12)')).toEqual(new Date(2000, 0, 1, 12, 12));
243+
expect(fromJSDate('new Date(2000, 0, 1, 12, 12, 12)')).toEqual(new Date(2000, 0, 1, 12, 12, 12));
244+
expect(fromJSDate('new Date(2000, 0, 1, 12, 12, 12, 1)')).toEqual(new Date(2000, 0, 1, 12, 12, 12, 1));
245+
});
246+
});
247+
248+
describe('toJSDate', () => {
249+
test('convert a date to JS new Date()', () => {
250+
expect(toJSDate(new Date(2000, 0))).toEqual('new Date(2000, 0, 1, 0, 0, 0, 0);');
251+
expect(toJSDate(new Date(2000, 0, 1, 12, 12))).toEqual('new Date(2000, 0, 1, 12, 12, 0, 0);');
252+
expect(toJSDate(new Date(2000, 0, 1, 12, 12, 12))).toEqual('new Date(2000, 0, 1, 12, 12, 12, 0);');
253+
expect(toJSDate(new Date(2000, 0, 1, 12, 12, 12, 1))).toEqual('new Date(2000, 0, 1, 12, 12, 12, 1);');
254+
});
255+
});
180256
});

src/tools/date-time-converter/date-time-converter.models.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import _ from 'lodash';
2+
import { addMilliseconds } from 'date-fns';
23

34
export {
45
isISO8601DateTimeString,
@@ -12,6 +13,11 @@ export {
1213
dateToExcelFormat,
1314
excelFormatToDate,
1415
isExcelFormat,
16+
fromTimestamp,
17+
isTimestampMicroSeconds,
18+
isJSDate,
19+
fromJSDate,
20+
toJSDate,
1521
};
1622

1723
const ISO8601_REGEX
@@ -26,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}:
2632

2733
const EXCEL_FORMAT_REGEX = /^-?\d+(\.\d+)?$/;
2834

35+
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*;?$/;
36+
2937
function createRegexMatcher(regex: RegExp) {
3038
return (date?: string) => !_.isNil(date) && regex.test(date);
3139
}
@@ -35,9 +43,19 @@ const isISO9075DateString = createRegexMatcher(ISO9075_REGEX);
3543
const isRFC3339DateString = createRegexMatcher(RFC3339_REGEX);
3644
const isRFC7231DateString = createRegexMatcher(RFC7231_REGEX);
3745
const isUnixTimestamp = createRegexMatcher(/^[0-9]{1,10}$/);
38-
const isTimestamp = createRegexMatcher(/^[0-9]{1,13}$/);
46+
const isTimestamp = createRegexMatcher(/^([0-9]{1,13}|[0-9]{16})$/);
47+
const isTimestampMilliSeconds = createRegexMatcher(/^[0-9]{1,13}$/);
48+
const isTimestampMicroSeconds = createRegexMatcher(/^[0-9]{16}$/);
3949
const isMongoObjectId = createRegexMatcher(/^[0-9a-fA-F]{24}$/);
4050

51+
const isJSDate = createRegexMatcher(JS_DATE_REGEX);
52+
function fromJSDate(date: string): Date {
53+
const res = JS_DATE_REGEX.exec(date);
54+
const parts = (res || []).filter(p => p !== undefined).map(p => Number.parseInt(p, 10)).slice(1);
55+
return new (Function.prototype.bind.apply(Date, [null, ...parts]))();
56+
}
57+
const toJSDate = (date: Date) => `new Date(${date.getFullYear()}, ${date.getMonth()}, ${date.getDate()}, ${date.getHours()}, ${date.getMinutes()}, ${date.getSeconds()}, ${date.getMilliseconds()});`;
58+
4159
const isExcelFormat = createRegexMatcher(EXCEL_FORMAT_REGEX);
4260

4361
function isUTCDateString(date?: string) {
@@ -60,3 +78,14 @@ function dateToExcelFormat(date: Date) {
6078
function excelFormatToDate(excelFormat: string | number) {
6179
return new Date((Number(excelFormat) - 25569) * 86400 * 1000);
6280
}
81+
82+
function fromTimestamp(timestamp: string, type: 'auto' | 'milliseconds' | 'microseconds' = 'auto') {
83+
let milliSeconds = 0;
84+
if (type === 'microseconds' || isTimestampMicroSeconds(timestamp)) {
85+
milliSeconds = Number(timestamp) / 1000;
86+
}
87+
else if (type === 'milliseconds' || isTimestampMilliSeconds(timestamp)) {
88+
milliSeconds = Number(timestamp);
89+
}
90+
return addMilliseconds(new Date(0), milliSeconds);
91+
}

src/tools/date-time-converter/date-time-converter.vue

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,25 @@ import {
1010
isDate,
1111
isValid,
1212
parseISO,
13-
parseJSON,
1413
} from 'date-fns';
14+
import { UTCDate } from '@date-fns/utc';
1515
import type { DateFormat, ToDateMapper } from './date-time-converter.types';
1616
import {
1717
dateToExcelFormat,
1818
excelFormatToDate,
19+
fromJSDate,
20+
fromTimestamp,
1921
isExcelFormat,
2022
isISO8601DateTimeString,
2123
isISO9075DateString,
24+
isJSDate,
2225
isMongoObjectId,
2326
isRFC3339DateString,
2427
isRFC7231DateString,
2528
isTimestamp,
2629
isUTCDateString,
2730
isUnixTimestamp,
31+
toJSDate,
2832
} from './date-time-converter.models';
2933
import { withDefaultOnError } from '@/utils/defaults';
3034
import { useValidation } from '@/composable/validation';
@@ -46,6 +50,12 @@ const formats: DateFormat[] = [
4650
toDate: parseISO,
4751
formatMatcher: date => isISO8601DateTimeString(date),
4852
},
53+
{
54+
name: 'ISO 8601 UTC',
55+
fromDate: date => (new UTCDate(date)).toISOString(),
56+
toDate: parseISO,
57+
formatMatcher: date => isISO8601DateTimeString(date),
58+
},
4959
{
5060
name: 'ISO 9075',
5161
fromDate: formatISO9075,
@@ -73,7 +83,7 @@ const formats: DateFormat[] = [
7383
{
7484
name: 'Timestamp',
7585
fromDate: date => String(getTime(date)),
76-
toDate: ms => parseJSON(+ms),
86+
toDate: ms => fromTimestamp(ms),
7787
formatMatcher: date => isTimestamp(date),
7888
},
7989
{
@@ -94,6 +104,12 @@ const formats: DateFormat[] = [
94104
toDate: excelFormatToDate,
95105
formatMatcher: isExcelFormat,
96106
},
107+
{
108+
name: 'JS Date',
109+
fromDate: date => toJSDate(date),
110+
toDate: date => fromJSDate(date),
111+
formatMatcher: isJSDate,
112+
},
97113
];
98114
99115
const formatIndex = ref(6);

0 commit comments

Comments
 (0)