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

pierrejeambrun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new b3cfad92f3d Fix Gantt tooltip showing wrong end date on 
queued/scheduled bars (#68570)
b3cfad92f3d is described below

commit b3cfad92f3df5d36690cf66a9da2aa31590111b5
Author: Aaryan123456679 <[email protected]>
AuthorDate: Thu Jul 2 21:15:20 2026 +0530

    Fix Gantt tooltip showing wrong end date on queued/scheduled bars (#68570)
    
    * UI: Fix Gantt tooltip showing wrong end date on queued/scheduled bars
    
    The tooltip on the queued and scheduled segments of a Gantt bar reported
    the end of that segment (e.g. the moment queueing ended) as the task's
    End Date, instead of the task's actual end. This was a regression from
    the start-date fix in #68176, which made the Start Date consistent across
    segments but left the End Date per-segment.
    
    Carry the task's effective end (end_date, or "now" while running) on every
    segment of a try as `end_when`, mirroring `start_when`, and use it for the
    tooltip's End Date so all segments of a try report the same start and end.
    
    closes: #68174
    
    * UI: Make end_when symmetric with start_when in Gantt tooltip data
    
    Address review: derive a single effectiveEndDate (the raw end_date, or
    "now" while a started task is still running) and gate end_when on it the
    same way start_when is gated on start_date. Finished tasks now keep the
    API's raw end_date string instead of a re-serialized dayjs timestamp, and
    the tryWhenForTooltip block no longer depends on endMs ordering. Pin the
    running-task test to a fixed "now" via fake timers.
    
    * UI: Remove noisy comments from Gantt tooltip data transform
    
    Address review: drop the verbose inline comments around effectiveEndDate
    and tryWhenForTooltip; the code is self-explanatory.
---
 .../ui/src/layouts/Details/Gantt/GanttTimeline.tsx |  2 +-
 .../ui/src/layouts/Details/Gantt/utils.test.ts     | 66 +++++++++++++++++++++-
 .../airflow/ui/src/layouts/Details/Gantt/utils.ts  |  7 ++-
 3 files changed, 72 insertions(+), 3 deletions(-)

diff --git 
a/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/GanttTimeline.tsx 
b/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/GanttTimeline.tsx
index 43c28dba56b..940a80854e2 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/GanttTimeline.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/GanttTimeline.tsx
@@ -85,7 +85,7 @@ const toTooltipSummary = (
 
   return {
     child_states: null,
-    max_end_date: dayjs(segment.x[1]).toISOString(),
+    max_end_date: segment.end_when ?? dayjs(segment.x[1]).toISOString(),
     min_start_date: segment.start_when ?? dayjs(segment.x[0]).toISOString(),
     state: segment.state ?? null,
     task_display_name: segment.y,
diff --git 
a/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/utils.test.ts 
b/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/utils.test.ts
index d725dfc8be6..f39c751d50d 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/utils.test.ts
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/utils.test.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 import dayjs from "dayjs";
-import { describe, expect, it } from "vitest";
+import { describe, expect, it, vi } from "vitest";
 
 import type { LightGridTaskInstanceSummary } from "openapi/requests/types.gen";
 import type { GridTask } from "src/layouts/Details/Grid/utils";
@@ -237,6 +237,70 @@ describe("transformGanttData", () => {
     expect(result[2]?.state).toBe("success");
   });
 
+  it("carries the task's actual start and end on every segment of the try", () 
=> {
+    const result = transformGanttData({
+      allTries: [
+        {
+          end_date: "2024-03-14T10:05:00+00:00",
+          is_mapped: false,
+          queued_dttm: "2024-03-14T09:59:00+00:00",
+          scheduled_dttm: "2024-03-14T09:58:00+00:00",
+          start_date: "2024-03-14T10:00:00+00:00",
+          state: "success",
+          task_display_name: "task_1",
+          task_id: "task_1",
+          try_number: 1,
+        },
+      ],
+      flatNodes: [{ depth: 0, id: "task_1", is_mapped: false, label: "task_1" 
}],
+      gridSummaries: [],
+    });
+
+    expect(result).toHaveLength(3);
+    // The scheduled, queued, and execution bars all report the task's real 
start_date/end_date
+    // (the raw API strings) so the tooltip is consistent no matter which 
segment is hovered
+    // (regression from #68174).
+    for (const segment of result) {
+      expect(segment.start_when).toBe("2024-03-14T10:00:00+00:00");
+      expect(segment.end_when).toBe("2024-03-14T10:05:00+00:00");
+    }
+  });
+
+  it("uses the current time as end_when on every segment while the task is 
still running", () => {
+    const now = new Date("2024-03-14T10:30:00.000Z");
+
+    vi.useFakeTimers();
+    vi.setSystemTime(now);
+
+    try {
+      const result = transformGanttData({
+        allTries: [
+          {
+            end_date: null,
+            is_mapped: false,
+            queued_dttm: "2024-03-14T09:59:00+00:00",
+            scheduled_dttm: null,
+            start_date: "2024-03-14T10:00:00+00:00",
+            state: "running",
+            task_display_name: "task_1",
+            task_id: "task_1",
+            try_number: 1,
+          },
+        ],
+        flatNodes: [{ depth: 0, id: "task_1", is_mapped: false, label: 
"task_1" }],
+        gridSummaries: [],
+      });
+
+      // Queued + execution bars, both reporting the same "now" end so the 
tooltip is consistent.
+      expect(result.length).toBeGreaterThan(0);
+      for (const segment of result) {
+        expect(segment.end_when).toBe(now.toISOString());
+      }
+    } finally {
+      vi.useRealTimers();
+    }
+  });
+
   it("produces 2 segments when only queued_dttm is present", () => {
     const result = transformGanttData({
       allTries: [
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/utils.ts 
b/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/utils.ts
index 9502cd31e78..e8d2ba18b0b 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/utils.ts
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/utils.ts
@@ -28,6 +28,8 @@ import { renderDuration } from "src/utils/datetimeUtils";
 import { buildTaskInstanceUrl } from "src/utils/links";
 
 export type GanttDataItem = {
+  /** Effective task end (end_date, or "now" while running) — consistent 
across all segments of the same try. */
+  end_when?: string | null;
   isGroup?: boolean | null;
   isMapped?: boolean | null;
   /** Source try times for tooltips (matches TaskInstance `*_when` fields). */
@@ -135,11 +137,14 @@ export const transformGanttData = ({
             const queuedMs = queuedDttm === null ? undefined : 
dayjs(queuedDttm).valueOf();
             const scheduledMs = scheduledDttm === null ? undefined : 
dayjs(scheduledDttm).valueOf();
 
-            // Include scheduled/queued/start times in tooltip data whenever 
the timestamps exist.
+            const effectiveEndDate =
+              endDate ?? (hasTaskRunning && startDate !== null ? new 
Date().toISOString() : null);
+
             const tryWhenForTooltip = {
               ...(scheduledMs === undefined ? {} : { scheduled_when: 
scheduledDttm }),
               ...(queuedMs === undefined ? {} : { queued_when: queuedDttm }),
               ...(startDate === null ? {} : { start_when: startDate }),
+              ...(effectiveEndDate === null ? {} : { end_when: 
effectiveEndDate }),
             };
 
             let endMs: number;

Reply via email to