This is an automated email from the ASF dual-hosted git repository.

kaxilnaik pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit 58735c44ba538ebe3b3e73e08a192b1b76df9905
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Fri Sep 12 08:35:33 2025 -0600

    Unify datetime format in the UI (#55572)
    
    (cherry picked from commit 318a1f162d94941cf038b20c7db19c8a3ee91558)
---
 airflow-core/src/airflow/ui/src/components/DateTimeInput.tsx   |  3 ++-
 airflow-core/src/airflow/ui/src/components/DurationChart.tsx   |  3 ++-
 airflow-core/src/airflow/ui/src/components/Time.test.tsx       |  9 +++++----
 airflow-core/src/airflow/ui/src/components/Time.tsx            |  9 +++------
 .../airflow/ui/src/components/TriggerDag/TriggerDAGForm.tsx    |  3 ++-
 .../src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx         | 10 +++++-----
 .../src/airflow/ui/src/layouts/Nav/TimezoneSelector.tsx        |  3 ++-
 .../ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx        |  3 ++-
 airflow-core/src/airflow/ui/src/utils/datetimeUtils.ts         |  5 ++++-
 9 files changed, 27 insertions(+), 21 deletions(-)

diff --git a/airflow-core/src/airflow/ui/src/components/DateTimeInput.tsx 
b/airflow-core/src/airflow/ui/src/components/DateTimeInput.tsx
index d31257a3623..3ee9dbf62c8 100644
--- a/airflow-core/src/airflow/ui/src/components/DateTimeInput.tsx
+++ b/airflow-core/src/airflow/ui/src/components/DateTimeInput.tsx
@@ -22,6 +22,7 @@ import tz from "dayjs/plugin/timezone";
 import { forwardRef } from "react";
 
 import { useTimezone } from "src/context/timezone";
+import { DEFAULT_DATETIME_FORMAT } from "src/utils/datetimeUtils";
 
 dayjs.extend(tz);
 
@@ -35,7 +36,7 @@ export const DateTimeInput = forwardRef<HTMLInputElement, 
Props>(({ onChange, va
   // Convert UTC value to local time for display
   const displayValue =
     Boolean(value) && dayjs(value).isValid()
-      ? dayjs(value).tz(selectedTimezone).format("YYYY-MM-DDTHH:mm:ss.SSS")
+      ? dayjs(value).tz(selectedTimezone).format(DEFAULT_DATETIME_FORMAT)
       : "";
 
   return (
diff --git a/airflow-core/src/airflow/ui/src/components/DurationChart.tsx 
b/airflow-core/src/airflow/ui/src/components/DurationChart.tsx
index e0a44ebe9aa..c0fd7cf796b 100644
--- a/airflow-core/src/airflow/ui/src/components/DurationChart.tsx
+++ b/airflow-core/src/airflow/ui/src/components/DurationChart.tsx
@@ -36,6 +36,7 @@ import { useNavigate } from "react-router-dom";
 
 import type { TaskInstanceResponse, GridRunsResponse } from 
"openapi/requests/types.gen";
 import { getComputedCSSVariableValue } from "src/theme";
+import { DEFAULT_DATETIME_FORMAT } from "src/utils/datetimeUtils";
 
 ChartJS.register(
   CategoryScale,
@@ -160,7 +161,7 @@ export const DurationChart = ({
               label: translate("durationChart.runDuration"),
             },
           ],
-          labels: entries.map((entry: RunResponse) => 
dayjs(entry.run_after).format("YYYY-MM-DD, hh:mm:ss")),
+          labels: entries.map((entry: RunResponse) => 
dayjs(entry.run_after).format(DEFAULT_DATETIME_FORMAT)),
         }}
         datasetIdKey="id"
         options={{
diff --git a/airflow-core/src/airflow/ui/src/components/Time.test.tsx 
b/airflow-core/src/airflow/ui/src/components/Time.test.tsx
index c2f0f53c02c..f342ad0b484 100644
--- a/airflow-core/src/airflow/ui/src/components/Time.test.tsx
+++ b/airflow-core/src/airflow/ui/src/components/Time.test.tsx
@@ -22,8 +22,9 @@ import { describe, it, expect, vi } from "vitest";
 
 import { TimezoneContext } from "src/context/timezone";
 import { Wrapper } from "src/utils/Wrapper";
+import { DEFAULT_DATETIME_FORMAT, DEFAULT_DATETIME_FORMAT_WITH_TZ } from 
"src/utils/datetimeUtils";
 
-import Time, { defaultFormat, defaultFormatWithTZ } from "./Time";
+import Time from "./Time";
 
 describe("Test Time and TimezoneProvider", () => {
   it("Displays a UTC time correctly", () => {
@@ -38,7 +39,7 @@ describe("Test Time and TimezoneProvider", () => {
       },
     );
 
-    const utcTime = screen.getByText(dayjs.utc(now).format(defaultFormat));
+    const utcTime = 
screen.getByText(dayjs.utc(now).format(DEFAULT_DATETIME_FORMAT));
 
     expect(utcTime).toBeDefined();
     expect(utcTime.title).toBeFalsy();
@@ -58,9 +59,9 @@ describe("Test Time and TimezoneProvider", () => {
     );
 
     const nowTime = dayjs(now);
-    const samoaTime = screen.getByText(nowTime.tz(tz).format(defaultFormat));
+    const samoaTime = 
screen.getByText(nowTime.tz(tz).format(DEFAULT_DATETIME_FORMAT));
 
     expect(samoaTime).toBeDefined();
-    
expect(samoaTime.title).toEqual(nowTime.tz("UTC").format(defaultFormatWithTZ));
+    
expect(samoaTime.title).toEqual(nowTime.tz("UTC").format(DEFAULT_DATETIME_FORMAT_WITH_TZ));
   });
 });
diff --git a/airflow-core/src/airflow/ui/src/components/Time.tsx 
b/airflow-core/src/airflow/ui/src/components/Time.tsx
index 8edd92d983f..99b7f029f72 100644
--- a/airflow-core/src/airflow/ui/src/components/Time.tsx
+++ b/airflow-core/src/airflow/ui/src/components/Time.tsx
@@ -23,10 +23,7 @@ import tz from "dayjs/plugin/timezone";
 import utc from "dayjs/plugin/utc";
 
 import { useTimezone } from "src/context/timezone";
-
-export const defaultFormat = "YYYY-MM-DD, HH:mm:ss";
-export const defaultFormatWithTZ = `${defaultFormat} z`;
-export const defaultTZFormat = "z (Z)";
+import { DEFAULT_DATETIME_FORMAT, DEFAULT_DATETIME_FORMAT_WITH_TZ } from 
"src/utils/datetimeUtils";
 
 dayjs.extend(utc);
 dayjs.extend(tz);
@@ -38,7 +35,7 @@ type Props = {
   readonly showTooltip?: boolean;
 } & SpanProps;
 
-const Time = ({ datetime, format = defaultFormat, showTooltip = true, ...rest 
}: Props) => {
+const Time = ({ datetime, format = DEFAULT_DATETIME_FORMAT, showTooltip = 
true, ...rest }: Props) => {
   const { selectedTimezone } = useTimezone();
   const time = dayjs(datetime);
 
@@ -47,7 +44,7 @@ const Time = ({ datetime, format = defaultFormat, showTooltip 
= true, ...rest }:
   }
 
   const formattedTime = time.tz(selectedTimezone).format(format);
-  const utcTime = time.tz("UTC").format(defaultFormatWithTZ);
+  const utcTime = time.tz("UTC").format(DEFAULT_DATETIME_FORMAT_WITH_TZ);
 
   return (
     <chakra.span dir="ltr" {...rest}>
diff --git 
a/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGForm.tsx 
b/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGForm.tsx
index 8bd3484ca33..8c92730714d 100644
--- a/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGForm.tsx
+++ b/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGForm.tsx
@@ -27,6 +27,7 @@ import { useDagParams } from "src/queries/useDagParams";
 import { useParamStore } from "src/queries/useParamStore";
 import { useTogglePause } from "src/queries/useTogglePause";
 import { useTrigger } from "src/queries/useTrigger";
+import { DEFAULT_DATETIME_FORMAT } from "src/utils/datetimeUtils";
 
 import ConfigForm from "../ConfigForm";
 import { DateTimeInput } from "../DateTimeInput";
@@ -65,7 +66,7 @@ const TriggerDAGForm = ({ dagDisplayName, dagId, isPaused, 
onClose, open }: Trig
       conf,
       dagRunId: "",
       // Default logical date to now, show it in the selected timezone
-      logicalDate: dayjs().format("YYYY-MM-DDTHH:mm:ss.SSS"),
+      logicalDate: dayjs().format(DEFAULT_DATETIME_FORMAT),
       note: "",
     },
   });
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx 
b/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx
index fe623727377..655e86e19ce 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx
@@ -48,7 +48,7 @@ import { useGridStructure } from 
"src/queries/useGridStructure";
 import { useGridTiSummaries } from "src/queries/useGridTISummaries";
 import { getComputedCSSVariableValue } from "src/theme";
 import { isStatePending, useAutoRefresh } from "src/utils";
-import { formatDate } from "src/utils/datetimeUtils";
+import { DEFAULT_DATETIME_FORMAT, formatDate } from "src/utils/datetimeUtils";
 
 import { createHandleBarClick, createChartOptions } from "./utils";
 
@@ -147,8 +147,8 @@ export const Gantt = ({ limit }: Props) => {
             state: gridSummary.state,
             taskId: gridSummary.task_id,
             x: [
-              formatDate(gridSummary.min_start_date, selectedTimezone, 
"YYYY-MM-DD HH:mm:ss.SSS"),
-              formatDate(gridSummary.max_end_date, selectedTimezone, 
"YYYY-MM-DD HH:mm:ss.SSS"),
+              formatDate(gridSummary.min_start_date, selectedTimezone, 
DEFAULT_DATETIME_FORMAT),
+              formatDate(gridSummary.max_end_date, selectedTimezone, 
DEFAULT_DATETIME_FORMAT),
             ],
             y: gridSummary.task_id,
           };
@@ -163,8 +163,8 @@ export const Gantt = ({ limit }: Props) => {
               state: taskInstance.state,
               taskId: taskInstance.task_id,
               x: [
-                formatDate(taskInstance.start_date, selectedTimezone, 
"YYYY-MM-DD HH:mm:ss.SSS"),
-                formatDate(taskInstance.end_date, selectedTimezone, 
"YYYY-MM-DD HH:mm:ss.SSS"),
+                formatDate(taskInstance.start_date, selectedTimezone, 
DEFAULT_DATETIME_FORMAT),
+                formatDate(taskInstance.end_date, selectedTimezone, 
DEFAULT_DATETIME_FORMAT),
               ],
               y: taskInstance.task_id,
             };
diff --git a/airflow-core/src/airflow/ui/src/layouts/Nav/TimezoneSelector.tsx 
b/airflow-core/src/airflow/ui/src/layouts/Nav/TimezoneSelector.tsx
index 7a458f668c3..1ab91744cb3 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Nav/TimezoneSelector.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Nav/TimezoneSelector.tsx
@@ -25,6 +25,7 @@ import React, { useEffect, useMemo, useState } from "react";
 import { useTranslation } from "react-i18next";
 
 import { useTimezone } from "src/context/timezone";
+import { DEFAULT_DATETIME_FORMAT } from "src/utils/datetimeUtils";
 import type { Option as TimezoneOption } from "src/utils/option";
 
 dayjs.extend(utc);
@@ -60,7 +61,7 @@ const TimezoneSelector: React.FC = () => {
 
   useEffect(() => {
     const updateTime = () => {
-      setCurrentTime(dayjs().tz(selectedTimezone).format("YYYY-MM-DD 
HH:mm:ss"));
+      
setCurrentTime(dayjs().tz(selectedTimezone).format(DEFAULT_DATETIME_FORMAT));
     };
 
     updateTime();
diff --git 
a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx 
b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx
index 30610756121..e703683ce9b 100644
--- 
a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx
+++ 
b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx
@@ -27,6 +27,7 @@ import { FlexibleForm } from 
"src/components/FlexibleForm/FlexibleForm";
 import Time from "src/components/Time";
 import { useParamStore } from "src/queries/useParamStore";
 import { useUpdateHITLDetail } from "src/queries/useUpdateHITLDetail";
+import { DEFAULT_DATETIME_FORMAT } from "src/utils/datetimeUtils";
 import { getHITLParamsDict, getHITLFormData, getPreloadHITLFormData } from 
"src/utils/hitl";
 
 type HITLResponseFormProps = {
@@ -97,7 +98,7 @@ export const HITLResponseForm = ({ hitlDetail }: 
HITLResponseFormProps) => {
       {hitlDetail.response_received ? (
         <Text color="fg.muted" fontSize="sm">
           {translate("response.received")}
-          <Time datetime={hitlDetail.response_at} format="YYYY-MM-DD, 
HH:mm:ss" />
+          <Time datetime={hitlDetail.response_at} 
format={DEFAULT_DATETIME_FORMAT} />
         </Text>
       ) : undefined}
       <Accordion.Root
diff --git a/airflow-core/src/airflow/ui/src/utils/datetimeUtils.ts 
b/airflow-core/src/airflow/ui/src/utils/datetimeUtils.ts
index bcc1f657316..f1871b4d4fe 100644
--- a/airflow-core/src/airflow/ui/src/utils/datetimeUtils.ts
+++ b/airflow-core/src/airflow/ui/src/utils/datetimeUtils.ts
@@ -23,6 +23,9 @@ import tz from "dayjs/plugin/timezone";
 dayjs.extend(dayjsDuration);
 dayjs.extend(tz);
 
+export const DEFAULT_DATETIME_FORMAT = "YYYY-MM-DD HH:mm:ss";
+export const DEFAULT_DATETIME_FORMAT_WITH_TZ = `${DEFAULT_DATETIME_FORMAT} z`;
+
 export const renderDuration = (durationSeconds: number | null | undefined): 
string => {
   if (
     durationSeconds === null ||
@@ -51,7 +54,7 @@ export const getDuration = (startDate?: string | null, 
endDate?: string | null)
 export const formatDate = (
   date: number | string | null | undefined,
   timezone: string,
-  format: string = "YYYY-MM-DD HH:mm:ss",
+  format: string = DEFAULT_DATETIME_FORMAT,
 ) => {
   if (date === null || date === undefined || !dayjs(date).isValid()) {
     return dayjs().tz(timezone).format(format);

Reply via email to