From 277b92ddece61952ca30a721c23e43ed8206d457 Mon Sep 17 00:00:00 2001 From: Scott Wilson Date: Thu, 17 Jul 2025 20:16:57 -0700 Subject: [PATCH] fix: update day picker to handle utc display --- .../components/v2/DatePicker/DatePicker.tsx | 45 ++++++++++++++++--- frontend/src/helpers/datetime.ts | 24 ++++++++++ .../components/LogsDateFilter.tsx | 6 ++- .../AuditLogsPage/components/LogsSection.tsx | 4 +- .../AuditLogsPage/components/LogsTableRow.tsx | 16 +------ .../AuditLogsPage/components/types.tsx | 5 --- 6 files changed, 71 insertions(+), 29 deletions(-) create mode 100644 frontend/src/helpers/datetime.ts diff --git a/frontend/src/components/v2/DatePicker/DatePicker.tsx b/frontend/src/components/v2/DatePicker/DatePicker.tsx index 37f22f59e..a4af1afd9 100644 --- a/frontend/src/components/v2/DatePicker/DatePicker.tsx +++ b/frontend/src/components/v2/DatePicker/DatePicker.tsx @@ -1,11 +1,13 @@ import { ChangeEventHandler, useState } from "react"; -import { DayPicker, DayPickerProps, getDefaultClassNames, UI } from "react-day-picker"; +import { DayPicker, DayPickerProps, getDefaultClassNames, TZDate, UI } from "react-day-picker"; import { faCalendar } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { PopoverContentProps, PopoverProps } from "@radix-ui/react-popover"; import { format, setHours, setMinutes } from "date-fns"; import { twMerge } from "tailwind-merge"; +import { formatDateTime, Timezone } from "@app/helpers/datetime"; + import { Button } from "../Button"; import { Input } from "../Input"; import { Popover, PopoverContent, PopoverTrigger } from "../Popoverv2"; @@ -19,6 +21,31 @@ export type DatePickerProps = Omit & { popUpContentProps: PopoverContentProps; dateFormat?: "PPP" | "PP" | "P"; // extend as needed buttonClassName?: string; + timezone?: Timezone; +}; + +const localTimeToUTC = (timeString: string) => { + const today = new Date(); + const [hours, minutes] = timeString.split(":").map(Number); + + today.setHours(hours, minutes, 0, 0); + + const utcHours = today.getUTCHours().toString().padStart(2, "0"); + const utcMinutes = today.getUTCMinutes().toString().padStart(2, "0"); + + return `${utcHours}:${utcMinutes}`; +}; + +const utcTimeToLocal = (utcTimeString: string) => { + const today = new Date(); + const [hours, minutes] = utcTimeString.split(":").map(Number); + + today.setUTCHours(hours, minutes, 0, 0); + + const localHours = today.getHours().toString().padStart(2, "0"); + const localMinutes = today.getMinutes().toString().padStart(2, "0"); + + return `${localHours}:${localMinutes}`; }; // Doc: https://react-day-picker.js.org/ @@ -29,12 +56,15 @@ export const DatePicker = ({ popUpContentProps, dateFormat = "PPP", buttonClassName, + timezone, ...props }: DatePickerProps) => { const [timeValue, setTimeValue] = useState(value ? format(value, "HH:mm") : "00:00"); + const displayUtc = timezone === Timezone.UTC; const handleTimeChange: ChangeEventHandler = (e) => { - const time = e.target.value; + const time = displayUtc ? utcTimeToLocal(e.target.value) : e.target.value; + if (time) { setTimeValue(time); if (value) { @@ -64,7 +94,9 @@ export const DatePicker = ({ variant="outline_bg" leftIcon={} > - {value ? format(value, dateFormat) : "Pick a date and time"} + {value + ? formatDateTime({ timestamp: value, timezone, dateFormat }) + : "Pick a date and time"} handleDaySelect(date ? new TZDate(date, undefined) : undefined)} className="font-inter text-mineshaft-200" + timeZone={displayUtc ? "UTC" : undefined} classNames={{ today: "text-primary border-primary", selected: " text-mineshaft-100 bg-mineshaft-500", @@ -94,7 +127,7 @@ export const DatePicker = ({
diff --git a/frontend/src/helpers/datetime.ts b/frontend/src/helpers/datetime.ts new file mode 100644 index 000000000..b0b174166 --- /dev/null +++ b/frontend/src/helpers/datetime.ts @@ -0,0 +1,24 @@ +import { format } from "date-fns"; + +export enum Timezone { + Local = "local", + UTC = "UTC" +} + +export const formatDateTime = ({ + timezone, + timestamp, + dateFormat = "MMM do yyyy, hh:mm a" +}: { + timestamp: string | Date; + timezone?: Timezone; + dateFormat?: string; +}) => { + const date = new Date(timestamp); + + if (timezone === Timezone.UTC) { + const utcDate = new Date(date.getTime() + date.getTimezoneOffset() * 60000); + return `${format(utcDate, dateFormat)} UTC`; + } + return format(date, dateFormat); +}; diff --git a/frontend/src/pages/organization/AuditLogsPage/components/LogsDateFilter.tsx b/frontend/src/pages/organization/AuditLogsPage/components/LogsDateFilter.tsx index bcdb3e8b7..1249b6b8b 100644 --- a/frontend/src/pages/organization/AuditLogsPage/components/LogsDateFilter.tsx +++ b/frontend/src/pages/organization/AuditLogsPage/components/LogsDateFilter.tsx @@ -18,12 +18,12 @@ import { Select, SelectItem } from "@app/components/v2"; +import { Timezone } from "@app/helpers/datetime"; import { auditLogDateFilterFormSchema, AuditLogDateFilterType, - TAuditLogDateFilterFormData, - Timezone + TAuditLogDateFilterFormData } from "./types"; type Props = { @@ -233,6 +233,7 @@ export const LogsDateFilter = ({ setFilter, filter, timezone, setTimezone }: Pro { ); }; -const formatDateTime = (timestamp: string | Date, timezone: Timezone) => { - const date = new Date(timestamp); - - if (timezone === Timezone.UTC) { - const utcDate = new Date(date.getTime() + date.getTimezoneOffset() * 60000); - return `${format(utcDate, "MMM do yyyy, hh:mm a")} UTC`; - } - return format(date, "MMM do yyyy, hh:mm a"); -}; - export const LogsTableRow = ({ auditLog, rowNumber, timezone }: Props) => { const [isOpen, setIsOpen] = useToggle(); @@ -59,7 +47,7 @@ export const LogsTableRow = ({ auditLog, rowNumber, timezone }: Props) => { {rowNumber} - {formatDateTime(auditLog.createdAt, timezone)} + {formatDateTime({ timestamp: auditLog.createdAt, timezone })}
diff --git a/frontend/src/pages/organization/AuditLogsPage/components/types.tsx b/frontend/src/pages/organization/AuditLogsPage/components/types.tsx index d436e5564..8e0ec9ba9 100644 --- a/frontend/src/pages/organization/AuditLogsPage/components/types.tsx +++ b/frontend/src/pages/organization/AuditLogsPage/components/types.tsx @@ -8,11 +8,6 @@ export enum AuditLogDateFilterType { Absolute = "absolute" } -export enum Timezone { - Local = "local", - UTC = "UTC" -} - export const auditLogFilterFormSchema = z.object({ eventMetadata: z.object({}).optional(), project: z