From 8efe9529e3a8d28c04edfc01371ad1a89a8ce2a6 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 7 Jan 2024 22:47:09 -0600 Subject: [PATCH] Handle non-numeric timestamps --- .vscode/settings.json | 1 + __tests__/date.test.ts | 33 +++++++++++++++++++++++++++++++++ src/utility/date.ts | 23 ++++++++++++++++++----- 3 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 __tests__/date.test.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 7f46718b..27747e68 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,6 +13,7 @@ "exiftool", "favs", "ghijklmnopqrstuv", + "Hasselblad", "hgetall", "hset", "Lightbox", diff --git a/__tests__/date.test.ts b/__tests__/date.test.ts new file mode 100644 index 00000000..4e29b7da --- /dev/null +++ b/__tests__/date.test.ts @@ -0,0 +1,33 @@ +import { + convertTimestampToNaivePostgresString, + convertTimestampWithOffsetToPostgresString, +} from '../src/utility/date'; + +describe('Date utility', () => { + describe('parses ambiguous timestamps', () => { + it('iPhone 15 Pro', () => { + const timestamp = 1704641450; + const offset = '-06:00'; + expect(convertTimestampWithOffsetToPostgresString(timestamp, offset)) + .toBe('2024-01-07T21:30:50.000Z'); + expect(convertTimestampToNaivePostgresString(timestamp)) + .toBe('2024-01-07 15:30:50'); + }); + it('Fujifilm X-T5', () => { + const timestamp = 1698086000; + const offset = '-05:00'; + expect(convertTimestampWithOffsetToPostgresString(timestamp, offset)) + .toBe('2023-10-23T23:33:20.000Z'); + expect(convertTimestampToNaivePostgresString(timestamp)) + .toBe('2023-10-23 18:33:20'); + }); + it('Hasselblad X2D 100C', () => { + const timestamp = '2023-12-02T16:38:36'; + const offset = '+00:00'; + expect(convertTimestampWithOffsetToPostgresString(timestamp, offset)) + .toBe('2023-12-02T16:38:36.000Z'); + expect(convertTimestampToNaivePostgresString(timestamp)) + .toBe('2023-12-02 16:38:36'); + }); + }); +}); diff --git a/src/utility/date.ts b/src/utility/date.ts index 67b1a165..04124327 100644 --- a/src/utility/date.ts +++ b/src/utility/date.ts @@ -4,6 +4,8 @@ const DATE_STRING_FORMAT_SHORT = 'dd MMM yyyy'; const DATE_STRING_FORMAT = 'd MMM yyyy h:mma'; const DATE_STRING_FORMAT_POSTGRES = 'yyyy-MM-dd HH:mm:ss'; +type AmbiguousTimestamp = number | string; + export const formatDate = (date: Date, short?: boolean) => format(date, short? DATE_STRING_FORMAT_SHORT : DATE_STRING_FORMAT); @@ -16,10 +18,19 @@ export const formatDateForPostgres = (date: Date) => '$1-$2-$3 $4', ); -const dateFromTimestamp = (timestamp?: number) => - timestamp !== undefined ? new Date(timestamp * 1000) : new Date(); +const dateFromTimestamp = (timestamp?: AmbiguousTimestamp): Date => + typeof timestamp === 'number' + ? new Date(timestamp * 1000) + : typeof timestamp === 'string' + ? /.+Z/i.test(timestamp) + ? new Date(timestamp) + : new Date(`${timestamp}Z`) + : new Date(); -const createNaiveDateWithOffset = (timestamp?: number, offset = '+00:00') => { +const createNaiveDateWithOffset = ( + timestamp?: AmbiguousTimestamp, + offset = '+00:00', +) => { const date = dateFromTimestamp(timestamp); const dateString = `${date.toISOString()}`.replace(/\.[\d]+Z/, offset); return parseISO(dateString); @@ -28,12 +39,14 @@ const createNaiveDateWithOffset = (timestamp?: number, offset = '+00:00') => { // Run on the server, when there are date/timestamp/offset inputs export const convertTimestampWithOffsetToPostgresString = ( - timestamp?: number, + timestamp?: AmbiguousTimestamp, offset?: string, ) => formatDateForPostgres(createNaiveDateWithOffset(timestamp, offset)); -export const convertTimestampToNaivePostgresString = (timestamp?: number) => +export const convertTimestampToNaivePostgresString = ( + timestamp?: AmbiguousTimestamp, +) => dateFromTimestamp(timestamp) .toISOString().replace( /(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2})(.[\d]+Z)*/,