From d22406b3cafbb647fbe1f61208bf3e941ca2b2ba Mon Sep 17 00:00:00 2001 From: openhands Date: Thu, 14 May 2026 19:28:38 +0000 Subject: [PATCH] Fix activity log timezone formatting Co-authored-by: openhands --- .../detail/activity-log-item.test.tsx | 11 +++++ .../automations/detail/activity-log-item.tsx | 49 +++++++++++++------ .../detail/activity-log-section.tsx | 8 ++- .../detail/configuration-section.tsx | 5 +- frontend/src/routes/automation-detail.tsx | 7 ++- frontend/src/types/automation.ts | 1 + 6 files changed, 62 insertions(+), 19 deletions(-) diff --git a/frontend/src/__tests__/components/automations/detail/activity-log-item.test.tsx b/frontend/src/__tests__/components/automations/detail/activity-log-item.test.tsx index a11fd46..9809fd5 100644 --- a/frontend/src/__tests__/components/automations/detail/activity-log-item.test.tsx +++ b/frontend/src/__tests__/components/automations/detail/activity-log-item.test.tsx @@ -37,6 +37,17 @@ describe("ActivityLogItem", () => { ).toBeInTheDocument(); }); + it("formats the run timestamp in the automation timezone", () => { + const run = createRun(); + render(); + + expect( + screen.getByText( + (content) => content.includes("2:00 AM") && content.includes("PDT"), + ), + ).toBeInTheDocument(); + }); + it("renders as a link when conversation_id exists", () => { const run = createRun({ conversation_id: "conv-abc123" }); render(); diff --git a/frontend/src/components/automations/detail/activity-log-item.tsx b/frontend/src/components/automations/detail/activity-log-item.tsx index 5a92259..e30d68a 100644 --- a/frontend/src/components/automations/detail/activity-log-item.tsx +++ b/frontend/src/components/automations/detail/activity-log-item.tsx @@ -5,18 +5,36 @@ import { RunStatusBadge } from "./run-status-badge"; interface ActivityLogItemProps { run: AutomationRun; + timeZone?: string; } -function formatRunTimestamp(dateStr: string, locale: string): string { +const RUN_TIMESTAMP_OPTIONS: Intl.DateTimeFormatOptions = { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric", + hour: "numeric", + minute: "2-digit", +}; + +function formatRunTimestamp( + dateStr: string, + locale: string, + timeZone?: string, +): string { const date = new Date(dateStr); - return date.toLocaleDateString(locale, { - weekday: "long", - year: "numeric", - month: "long", - day: "numeric", - hour: "numeric", - minute: "2-digit", - }); + const options: Intl.DateTimeFormatOptions = { ...RUN_TIMESTAMP_OPTIONS }; + + if (timeZone) { + options.timeZone = timeZone; + options.timeZoneName = "short"; + } + + try { + return date.toLocaleDateString(locale, options); + } catch { + return date.toLocaleDateString(locale, RUN_TIMESTAMP_OPTIONS); + } } function getConversationUrl(conversationId: string): string { @@ -27,16 +45,19 @@ function getConversationUrl(conversationId: string): string { return `https://${baseHost}/conversations/${conversationId}`; } -export function ActivityLogItem({ run }: ActivityLogItemProps) { +export function ActivityLogItem({ run, timeZone }: ActivityLogItemProps) { const { t, i18n } = useTranslation(); const hasConversation = !!run.conversation_id; + const runTimestamp = formatRunTimestamp( + run.started_at, + i18n.language, + timeZone, + ); const content = ( <>
- - {formatRunTimestamp(run.started_at, i18n.language)} - + {runTimestamp} {!hasConversation && ( ({t(I18nKey.AUTOMATIONS$DETAIL$NO_CONVERSATION)}) @@ -54,7 +75,7 @@ export function ActivityLogItem({ run }: ActivityLogItemProps) { target="_blank" rel="noopener noreferrer" className="flex items-center justify-between px-5 py-3 transition-colors cursor-pointer hover:bg-surface-elevated focus:bg-surface-elevated focus:outline-none" - aria-label={`View conversation for run at ${formatRunTimestamp(run.started_at, i18n.language)}`} + aria-label={`View conversation for run at ${runTimestamp}`} > {content} diff --git a/frontend/src/components/automations/detail/activity-log-section.tsx b/frontend/src/components/automations/detail/activity-log-section.tsx index d2cc801..6b092e2 100644 --- a/frontend/src/components/automations/detail/activity-log-section.tsx +++ b/frontend/src/components/automations/detail/activity-log-section.tsx @@ -7,11 +7,15 @@ import { ActivityLogItem } from "./activity-log-item"; interface ActivityLogSectionProps { automationId: string; + timeZone?: string; } const PAGE_SIZE = 20; -export function ActivityLogSection({ automationId }: ActivityLogSectionProps) { +export function ActivityLogSection({ + automationId, + timeZone, +}: ActivityLogSectionProps) { const { t } = useTranslation(); const [limit, setLimit] = useState(PAGE_SIZE); const { data, isLoading } = useAutomationRuns(automationId, limit, 0); @@ -56,7 +60,7 @@ export function ActivityLogSection({ automationId }: ActivityLogSectionProps) { key={run.id} className={index > 0 ? "border-t border-border" : ""} > - +
))} diff --git a/frontend/src/components/automations/detail/configuration-section.tsx b/frontend/src/components/automations/detail/configuration-section.tsx index 01081ad..cdd4c76 100644 --- a/frontend/src/components/automations/detail/configuration-section.tsx +++ b/frontend/src/components/automations/detail/configuration-section.tsx @@ -20,10 +20,11 @@ export function ConfigurationSection({ }: ConfigurationSectionProps) { const { t } = useTranslation(); + const automationTimeZone = automation.trigger.timezone ?? automation.timezone; let scheduleDisplay = automation.trigger.schedule ?? ""; if (automation.trigger.schedule_human) { - scheduleDisplay = automation.timezone - ? `${automation.trigger.schedule_human} (${automation.timezone})` + scheduleDisplay = automationTimeZone + ? `${automation.trigger.schedule_human} (${automationTimeZone})` : automation.trigger.schedule_human; } diff --git a/frontend/src/routes/automation-detail.tsx b/frontend/src/routes/automation-detail.tsx index 6db6202..f1ad3e7 100644 --- a/frontend/src/routes/automation-detail.tsx +++ b/frontend/src/routes/automation-detail.tsx @@ -64,6 +64,8 @@ export default function AutomationDetail() { }); }; + const automationTimeZone = automation.trigger.timezone ?? automation.timezone; + return (
@@ -81,7 +83,10 @@ export default function AutomationDetail() { createdAt={automation.created_at} lastRunAt={automation.last_triggered_at} /> - +