From 88ecf60587207ab6c937d50953773bc127d8cd03 Mon Sep 17 00:00:00 2001 From: sharevb Date: Sun, 25 Feb 2024 12:52:34 +0100 Subject: [PATCH] fix(date-time-converter): handle timestamp in microseconds Fix #783 --- .../date-time-converter.models.test.ts | 41 +++++++++++++++++++ .../date-time-converter.models.ts | 18 +++++++- .../date-time-converter.vue | 4 +- 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/tools/date-time-converter/date-time-converter.models.test.ts b/src/tools/date-time-converter/date-time-converter.models.test.ts index c2c7bee9..c375e3d2 100644 --- a/src/tools/date-time-converter/date-time-converter.models.test.ts +++ b/src/tools/date-time-converter/date-time-converter.models.test.ts @@ -2,6 +2,7 @@ import { describe, expect, test } from 'vitest'; import { dateToExcelFormat, excelFormatToDate, + fromTimestamp, isExcelFormat, isISO8601DateTimeString, isISO9075DateString, @@ -113,6 +114,46 @@ describe('date-time-converter models', () => { expect(isTimestamp('foo')).toBe(false); expect(isTimestamp('')).toBe(false); }); + + test('should return true for valid Unix timestamps in microseconds', () => { + expect(isTimestamp('1701227351995845')).toBe(true); + }); + + test('should return false for invalid Unix timestamps in microseconds', () => { + expect(isTimestamp('170122735199584')).toBe(false); + expect(isTimestamp('17012273519958')).toBe(false); + }); + }); + + describe('isTimestampMicroSeconds', () => { + test('should return true for valid Unix timestamps in microseconds', () => { + expect(isTimestamp('1649792026123123')).toBe(true); + expect(isTimestamp('1701227351995845')).toBe(true); + expect(isTimestamp('0')).toBe(true); + }); + + test('should return false for invalid Unix timestamps in microseconds', () => { + expect(isTimestamp('foo')).toBe(false); + expect(isTimestamp('')).toBe(false); + }); + + test('should return false for invalid Unix timestamps not in microseconds', () => { + expect(isTimestamp('170122735199584')).toBe(false); + expect(isTimestamp('17012273519958')).toBe(false); + }); + }); + + describe('fromTimestamp', () => { + test('should return valid Date for valid Unix timestamps in microseconds', () => { + expect(fromTimestamp('1649792026123123').toString()).toBe(new Date(1649792026123).toString()); + expect(fromTimestamp('1701227351995845').toString()).toBe(new Date(1701227351995).toString()); + expect(fromTimestamp('0').toString()).toBe(new Date(0).toString()); + }); + + test('should return Date(0) for invalid Unix timestamps not in microseconds', () => { + expect(fromTimestamp('170122735199584').toString()).toBe(new Date(0).toString()); + expect(fromTimestamp('17012273519958').toString()).toBe(new Date(0).toString()); + }); }); describe('isUTCDateString', () => { diff --git a/src/tools/date-time-converter/date-time-converter.models.ts b/src/tools/date-time-converter/date-time-converter.models.ts index f5eedbfa..330c16ec 100644 --- a/src/tools/date-time-converter/date-time-converter.models.ts +++ b/src/tools/date-time-converter/date-time-converter.models.ts @@ -1,4 +1,5 @@ import _ from 'lodash'; +import { addMilliseconds } from 'date-fns'; export { isISO8601DateTimeString, @@ -12,6 +13,8 @@ export { dateToExcelFormat, excelFormatToDate, isExcelFormat, + fromTimestamp, + isTimestampMicroSeconds, }; const ISO8601_REGEX @@ -35,7 +38,9 @@ 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 isTimestamp = createRegexMatcher(/^([0-9]{1,13}|[0-9]{16})$/); +const isTimestampMilliSeconds = createRegexMatcher(/^[0-9]{1,13}$/); +const isTimestampMicroSeconds = createRegexMatcher(/^[0-9]{16}$/); const isMongoObjectId = createRegexMatcher(/^[0-9a-fA-F]{24}$/); const isExcelFormat = createRegexMatcher(EXCEL_FORMAT_REGEX); @@ -60,3 +65,14 @@ function dateToExcelFormat(date: Date) { function excelFormatToDate(excelFormat: string | number) { return new Date((Number(excelFormat) - 25569) * 86400 * 1000); } + +function fromTimestamp(timestamp: string, type: 'auto' | 'milliseconds' | 'microseconds' = 'auto') { + let milliSeconds = 0; + if (type === 'microseconds' || isTimestampMicroSeconds(timestamp)) { + milliSeconds = Number(timestamp) / 1000; + } + else if (type === 'milliseconds' || isTimestampMilliSeconds(timestamp)) { + milliSeconds = Number(timestamp); + } + return addMilliseconds(new Date(0), milliSeconds); +} diff --git a/src/tools/date-time-converter/date-time-converter.vue b/src/tools/date-time-converter/date-time-converter.vue index 5636ed46..06aa7e3e 100644 --- a/src/tools/date-time-converter/date-time-converter.vue +++ b/src/tools/date-time-converter/date-time-converter.vue @@ -10,12 +10,12 @@ import { isDate, isValid, parseISO, - parseJSON, } from 'date-fns'; import type { DateFormat, ToDateMapper } from './date-time-converter.types'; import { dateToExcelFormat, excelFormatToDate, + fromTimestamp, isExcelFormat, isISO8601DateTimeString, isISO9075DateString, @@ -73,7 +73,7 @@ const formats: DateFormat[] = [ { name: 'Timestamp', fromDate: date => String(getTime(date)), - toDate: ms => parseJSON(+ms), + toDate: ms => fromTimestamp(ms), formatMatcher: date => isTimestamp(date), }, {