From cdb6130345a52797c36eb3008e2bff968a538359 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Thu, 12 Mar 2026 09:08:10 -0500 Subject: [PATCH] Extract date utility logic --- src/components/DateTimePicker.tsx | 19 +++++++++---------- src/utility/date.ts | 9 ++++++++- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/components/DateTimePicker.tsx b/src/components/DateTimePicker.tsx index 0fc0357a..a5080092 100644 --- a/src/components/DateTimePicker.tsx +++ b/src/components/DateTimePicker.tsx @@ -24,17 +24,16 @@ import { clsx } from 'clsx/lite'; import useClickInsideOutside from '@/utility/useClickInsideOutside'; import { useFormStatus } from 'react-dom'; import { TbCalendar, TbChevronLeft, TbChevronRight } from 'react-icons/tb'; +import { + DATE_FORMAT_POSTGRES, + DAY_NAMES, + getLocalTimeZoneLabel, +} from '@/utility/date'; -const FORMAT_NAIVE = 'yyyy-MM-dd HH:mm:ss'; -const DAY_NAMES = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']; - -export type DateTimePickerType = 'utc' | 'naive'; +type DateTimePickerType = 'utc' | 'naive'; type DisplayMode = 'utc' | 'local'; -// Get short local timezone label, e.g. "EST", "PST", "UTC+5" -const LOCAL_TZ_LABEL = - new Date().toLocaleTimeString('en-US', { timeZoneName: 'short' }) - .split(' ').at(-1) ?? 'LOCAL'; +const LOCAL_TZ_LABEL = getLocalTimeZoneLabel() ?? 'LOCAL'; function parseValue( value: string, @@ -60,7 +59,7 @@ function parseValue( utcDate.getUTCSeconds(), ); } else { - const d = parse(value, FORMAT_NAIVE, new Date()); + const d = parse(value, DATE_FORMAT_POSTGRES, new Date()); return isValid(d) ? d : null; } } catch { @@ -95,7 +94,7 @@ function formatValue( displayDate.getSeconds(), )).toISOString(); } - return format(displayDate, FORMAT_NAIVE); + return format(displayDate, DATE_FORMAT_POSTGRES); } export default function DateTimePicker({ diff --git a/src/utility/date.ts b/src/utility/date.ts index b8b9294a..96c14049 100644 --- a/src/utility/date.ts +++ b/src/utility/date.ts @@ -20,11 +20,13 @@ const DATE_FORMAT_LONG_PLACEHOLDER = '00 000 0000 00:0000'; const DATE_FORMAT_RSS = 'EEE, dd LLL yyyy HH:mm:ss xx'; const DATE_FORMAT_RSS_PLACEHOLDER = '000, 00 000 0000 00:00:00 00'; -const DATE_FORMAT_POSTGRES = 'yyyy-MM-dd HH:mm:ss'; +export const DATE_FORMAT_POSTGRES = 'yyyy-MM-dd HH:mm:ss'; export const VALIDATION_EXAMPLE_POSTGRES = '2025-01-03T21:00:44.000Z'; export const VALIDATION_EXAMPLE_POSTGRES_NAIVE = '2025-01-03 16:00:44'; +export const DAY_NAMES = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']; + type AmbiguousTimestamp = number | string; type Length = 'tiny' | 'short' | 'medium' | 'long' | 'rss'; @@ -158,3 +160,8 @@ export const validationMessageNaivePostgresDateString = (date = '') => validateNaivePostgresDateString(date) ? undefined : `Invalid format (${VALIDATION_EXAMPLE_POSTGRES_NAIVE})`; + +// Short local timezone label, e.g. "EST", "PST", "UTC+5" +export const getLocalTimeZoneLabel = () => + new Date().toLocaleTimeString('en-US', { timeZoneName: 'short' }) + .split(' ').at(-1);