diff --git a/skyvern-frontend/src/routes/tasks/detail/StepArtifacts.tsx b/skyvern-frontend/src/routes/tasks/detail/StepArtifacts.tsx index 5a972f947f..8151540131 100644 --- a/skyvern-frontend/src/routes/tasks/detail/StepArtifacts.tsx +++ b/skyvern-frontend/src/routes/tasks/detail/StepArtifacts.tsx @@ -13,7 +13,7 @@ import { ZoomableImage } from "@/components/ZoomableImage"; import { Skeleton } from "@/components/ui/skeleton"; import { getImageURL } from "./artifactUtils"; import { Input } from "@/components/ui/input"; -import { basicTimeFormat } from "@/util/timeFormat"; +import { basicLocalTimeFormat, basicTimeFormat } from "@/util/timeFormat"; import { useCredentialGetter } from "@/hooks/useCredentialGetter"; import { Artifact } from "./Artifact"; @@ -132,7 +132,11 @@ function StepArtifacts({ id, stepProps }: Props) { {isFetching ? ( ) : stepProps ? ( - + ) : null} diff --git a/skyvern-frontend/src/routes/tasks/detail/StepInfo.tsx b/skyvern-frontend/src/routes/tasks/detail/StepInfo.tsx index 5a1e5fcc91..7bc7b27f29 100644 --- a/skyvern-frontend/src/routes/tasks/detail/StepInfo.tsx +++ b/skyvern-frontend/src/routes/tasks/detail/StepInfo.tsx @@ -2,7 +2,7 @@ import { StepApiResponse } from "@/api/types"; import { StatusBadge } from "@/components/StatusBadge"; import { Label } from "@/components/ui/label"; import { Skeleton } from "@/components/ui/skeleton"; -import { basicTimeFormat } from "@/util/timeFormat"; +import { basicLocalTimeFormat, basicTimeFormat } from "@/util/timeFormat"; type Props = { isFetching: boolean; @@ -33,7 +33,9 @@ function StepInfo({ isFetching, stepProps }: Props) { {isFetching ? ( ) : stepProps ? ( - {basicTimeFormat(stepProps.created_at)} + + {basicLocalTimeFormat(stepProps.created_at)} + ) : null} diff --git a/skyvern-frontend/src/routes/tasks/list/TaskHistory.tsx b/skyvern-frontend/src/routes/tasks/list/TaskHistory.tsx index 6dfe734b8c..2039123040 100644 --- a/skyvern-frontend/src/routes/tasks/list/TaskHistory.tsx +++ b/skyvern-frontend/src/routes/tasks/list/TaskHistory.tsx @@ -22,7 +22,7 @@ import { PaginationPrevious, } from "@/components/ui/pagination"; import { StatusBadge } from "@/components/StatusBadge"; -import { basicTimeFormat } from "@/util/timeFormat"; +import { basicLocalTimeFormat, basicTimeFormat } from "@/util/timeFormat"; import { cn } from "@/util/utils"; import { TaskActions } from "./TaskActions"; @@ -120,8 +120,9 @@ function TaskHistory() { handleNavigate(event, task.task_id)} + title={basicTimeFormat(task.created_at)} > - {basicTimeFormat(task.created_at)} + {basicLocalTimeFormat(task.created_at)} diff --git a/skyvern-frontend/src/routes/tasks/running/QueuedTasks.tsx b/skyvern-frontend/src/routes/tasks/running/QueuedTasks.tsx index 736a170d6f..8f5555e2f4 100644 --- a/skyvern-frontend/src/routes/tasks/running/QueuedTasks.tsx +++ b/skyvern-frontend/src/routes/tasks/running/QueuedTasks.tsx @@ -1,7 +1,7 @@ import { getClient } from "@/api/AxiosClient"; import { TaskApiResponse } from "@/api/types"; import { useQuery } from "@tanstack/react-query"; -import { basicTimeFormat } from "@/util/timeFormat"; +import { basicLocalTimeFormat, basicTimeFormat } from "@/util/timeFormat"; import { Table, TableBody, @@ -77,8 +77,11 @@ function QueuedTasks() { - - {basicTimeFormat(task.created_at)} + + {basicLocalTimeFormat(task.created_at)} ); diff --git a/skyvern-frontend/src/routes/tasks/running/RunningTasks.tsx b/skyvern-frontend/src/routes/tasks/running/RunningTasks.tsx index b677cf06e1..d59fa8c59e 100644 --- a/skyvern-frontend/src/routes/tasks/running/RunningTasks.tsx +++ b/skyvern-frontend/src/routes/tasks/running/RunningTasks.tsx @@ -10,7 +10,7 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; -import { basicTimeFormat } from "@/util/timeFormat"; +import { basicLocalTimeFormat, basicTimeFormat } from "@/util/timeFormat"; import { LatestScreenshot } from "./LatestScreenshot"; import { useCredentialGetter } from "@/hooks/useCredentialGetter"; @@ -70,7 +70,9 @@ function RunningTasks() { - Created: {basicTimeFormat(task.created_at)} + + Created: {basicLocalTimeFormat(task.created_at)} + ); }); diff --git a/skyvern-frontend/src/routes/workflows/WorkflowPage.tsx b/skyvern-frontend/src/routes/workflows/WorkflowPage.tsx index 84a9d38821..d31cd0dab3 100644 --- a/skyvern-frontend/src/routes/workflows/WorkflowPage.tsx +++ b/skyvern-frontend/src/routes/workflows/WorkflowPage.tsx @@ -20,7 +20,7 @@ import { TableRow, } from "@/components/ui/table"; import { useCredentialGetter } from "@/hooks/useCredentialGetter"; -import { basicTimeFormat } from "@/util/timeFormat"; +import { basicLocalTimeFormat, basicTimeFormat } from "@/util/timeFormat"; import { cn } from "@/util/utils"; import { Pencil2Icon, PlayIcon } from "@radix-ui/react-icons"; import { useQuery } from "@tanstack/react-query"; @@ -147,8 +147,8 @@ function WorkflowPage() { - - {basicTimeFormat(workflowRun.created_at)} + + {basicLocalTimeFormat(workflowRun.created_at)} )) diff --git a/skyvern-frontend/src/routes/workflows/WorkflowRun.tsx b/skyvern-frontend/src/routes/workflows/WorkflowRun.tsx index 39a3b88fc2..41ad821bde 100644 --- a/skyvern-frontend/src/routes/workflows/WorkflowRun.tsx +++ b/skyvern-frontend/src/routes/workflows/WorkflowRun.tsx @@ -32,7 +32,11 @@ import { useApiCredential } from "@/hooks/useApiCredential"; import { useCredentialGetter } from "@/hooks/useCredentialGetter"; import { copyText } from "@/util/copyText"; import { apiBaseUrl, envCredential } from "@/util/env"; -import { basicTimeFormat, timeFormatWithShortDate } from "@/util/timeFormat"; +import { + basicLocalTimeFormat, + basicTimeFormat, + timeFormatWithShortDate, +} from "@/util/timeFormat"; import { cn } from "@/util/utils"; import { CopyIcon, @@ -468,8 +472,9 @@ function WorkflowRun() { onClick={(event) => handleNavigate(event, task.task_id) } + title={basicTimeFormat(task.created_at)} > - {basicTimeFormat(task.created_at)} + {basicLocalTimeFormat(task.created_at)} diff --git a/skyvern-frontend/src/routes/workflows/Workflows.tsx b/skyvern-frontend/src/routes/workflows/Workflows.tsx index cc058766ef..6e10d43fa1 100644 --- a/skyvern-frontend/src/routes/workflows/Workflows.tsx +++ b/skyvern-frontend/src/routes/workflows/Workflows.tsx @@ -26,7 +26,7 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import { useCredentialGetter } from "@/hooks/useCredentialGetter"; -import { basicTimeFormat } from "@/util/timeFormat"; +import { basicLocalTimeFormat, basicTimeFormat } from "@/util/timeFormat"; import { cn } from "@/util/utils"; import { ExclamationTriangleIcon, @@ -241,8 +241,9 @@ function Workflows() { onClick={(event) => { handleRowClick(event, workflow.workflow_permanent_id); }} + title={basicTimeFormat(workflow.created_at)} > - {basicTimeFormat(workflow.created_at)} + {basicLocalTimeFormat(workflow.created_at)}
@@ -384,8 +385,11 @@ function Workflows() { - - {basicTimeFormat(workflowRun.created_at)} + + {basicLocalTimeFormat(workflowRun.created_at)} ); diff --git a/skyvern-frontend/src/routes/workflows/components/LastRunAtTime.tsx b/skyvern-frontend/src/routes/workflows/components/LastRunAtTime.tsx index af5c781f10..58a96434dd 100644 --- a/skyvern-frontend/src/routes/workflows/components/LastRunAtTime.tsx +++ b/skyvern-frontend/src/routes/workflows/components/LastRunAtTime.tsx @@ -1,6 +1,6 @@ import { Skeleton } from "@/components/ui/skeleton"; import { useWorkflowLastRunQuery } from "../hooks/useWorkflowLastRunQuery"; -import { basicTimeFormat } from "@/util/timeFormat"; +import { basicLocalTimeFormat, basicTimeFormat } from "@/util/timeFormat"; type Props = { workflowId: string; @@ -21,7 +21,11 @@ function LastRunAtTime({ workflowId }: Props) { return N/A; } - return {basicTimeFormat(data.time)}; + return ( + + {basicLocalTimeFormat(data.time)} + + ); } export { LastRunAtTime }; diff --git a/skyvern-frontend/src/util/timeFormat.ts b/skyvern-frontend/src/util/timeFormat.ts index 57ad206bb3..dcb7d2753a 100644 --- a/skyvern-frontend/src/util/timeFormat.ts +++ b/skyvern-frontend/src/util/timeFormat.ts @@ -1,3 +1,30 @@ +function basicLocalTimeFormat(time: string): string { + // Adjust the fractional seconds to milliseconds (3 digits) + time = time.replace(/\.(\d{3})\d*/, ".$1"); + + // Append 'Z' to indicate UTC time if not already present + if (!time.endsWith("Z")) { + time += "Z"; + } + + const date = new Date(time); + const localTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; + + // Format the date and time in the local time zone + const dateString = date.toLocaleDateString("en-US", { + weekday: "short", + year: "numeric", + month: "short", + day: "numeric", + timeZone: localTimezone, + }); + const timeString = date.toLocaleTimeString("en-US", { + timeZone: localTimezone, + }); + + return `${dateString} at ${timeString}`; +} + function basicTimeFormat(time: string): string { const date = new Date(time); const dateString = date.toLocaleDateString("en-US", { @@ -7,7 +34,7 @@ function basicTimeFormat(time: string): string { day: "numeric", }); const timeString = date.toLocaleTimeString("en-US"); - return `${dateString} at ${timeString}`; + return `${dateString} at ${timeString} UTC`; } function timeFormatWithShortDate(time: string): string { @@ -18,4 +45,4 @@ function timeFormatWithShortDate(time: string): string { return `${dateString} at ${timeString}`; } -export { basicTimeFormat, timeFormatWithShortDate }; +export { basicLocalTimeFormat, basicTimeFormat, timeFormatWithShortDate };