Skip to content

More human readable duration strings - #72350

Open
bbovenzi wants to merge 3 commits into
apache:mainfrom
astronomer:dev/timestamp-subsecond-durations-191d7e
Open

More human readable duration strings#72350
bbovenzi wants to merge 3 commits into
apache:mainfrom
astronomer:dev/timestamp-subsecond-durations-191d7e

Conversation

@bbovenzi

@bbovenzi bbovenzi commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Problem:

our duration format was dd:hh:mm:ss.ms, which for short durations had a lot of excessive zeros or with many ms had floating point overflow.

Screenshot 2026-08-31 at 3 45 37 PM

Solution:
Switch to a more human readable format string that can be set per locale

Screenshot 2026-08-31 at 3 41 33 PM Screenshot 2026-08-31 at 3 41 56 PM Screenshot 2026-08-31 at 3 41 45 PM
Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)
    Claude Opus 5

  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.

@bbovenzi bbovenzi added this to the Airflow 3.3.2 milestone Aug 31, 2026
@bbovenzi bbovenzi added the backport-to-v3-3-test Backport to v3-3-test label Aug 31, 2026
@boring-cyborg boring-cyborg Bot added the area:UI Related to UI/UX. For Frontend Developers. label Aug 31, 2026

@ryanahamilton ryanahamilton left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Read through the whole diff and ran the checks locally. The core band logic in getDurationParts is solid — I probed the boundaries hard (0.9996, 59.95, 3599.6, 86399.4, …) and the carry is correct at every one of them. I also pushed all 21 shipped locales through both Intl.NumberFormat(style: "unit") and Intl.ListFormat, narrow and long, with no throws. The move off zero-padded clock strings is a clear improvement, and driving the format from CLDR with no new translation keys is the right way to get there.

Local results, on 4b6f540:

Check Result
pnpm exec tsc --p tsconfig.app.json pass
pnpm exec eslint --quiet pass
pnpm exec vitest run 1150/1151 — the single failure, DateTimeInput.test.tsx, is an unrelated 5s timeout that passes in isolation

One thing I'd call a bug, on getRelativeTime — it prints 60 minutes ago / 24 hours ago / 12 months ago where fromNow() said an hour ago / a day ago / a year ago. Details inline. The rest are layout budgets that were tuned for the always-English "1h 2m" and are now tight in the wider locales, plus some cleanup.

Three notes that don't anchor to a changed line:

  • src/utils/index.ts:21 still re-exports getDuration / renderDuration. After this PR nothing imports them from the barrel, and useDurationFormat's own doc says components must not use the raw functions — dropping the re-export would close the escape hatch the hook exists to eliminate.
  • Backfills.tsx takes formatElapsed as a third positional argument to getColumns, where the sibling DagRuns / TaskInstances column builders take an options object. Trivial, but the three read differently now.
  • Newsfragment — there isn't one. This changes how every duration in the UI renders, so it may be worth an airflow-core/newsfragments/72350.improvement.rst; entirely your call on whether it clears the user-facing bar.

Comment thread airflow-core/src/airflow/ui/src/utils/datetimeUtils.ts
Comment thread airflow-core/src/airflow/ui/src/utils/datetimeUtils.ts Outdated
Comment thread airflow-core/src/airflow/ui/src/utils/datetimeUtils.ts
Comment thread airflow-core/src/airflow/ui/src/utils/datetimeUtils.ts Outdated
Comment thread airflow-core/src/airflow/ui/src/layouts/Details/Gantt/utils.ts
Comment thread airflow-core/src/airflow/ui/src/utils/useDurationFormat.ts
Comment thread airflow-core/src/airflow/ui/src/utils/datetimeUtils.test.ts Outdated
Comment thread airflow-core/src/airflow/ui/src/layouts/Details/Gantt/utils.test.ts
Zero-padded clock strings buried the one number that mattered: a task
that took 83ms read as 00:00:00.083, and anything under a second
rendered as 00:00:00 in chart tooltips and Gantt axis labels, which is
indistinguishable from no time at all. Someone scanning a column of
durations wants the magnitude at a glance, and roughly three
significant digits gives it to them at every scale from milliseconds
to days.

Unit names, decimal separators and plural forms now come from CLDR
through Intl rather than being hardcoded English, so durations follow
the language the rest of the UI is already using. This needs no new
translation keys, and it reproduces Intl.DurationFormat's narrow style
without requiring it, since that API is above this package's Node
floor.

Formatting goes through a hook bound to the active language because
reading the language straight off the i18next singleton put it outside
anything React tracks: a component with no reason of its own to
subscribe to a language change kept rendering the previous locale
indefinitely.
Relative times picked their unit from the unrounded gap and then rounded
inside it, so anything just short of the next unit printed that unit's
own ceiling: "60 minutes ago" where "an hour ago" was meant, and the
same at 24 hours and 12 months. Promoting after the rounding matches
what the duration path already does a few hundred lines up.

Two layout budgets were sized when every duration was the English
"1h 2m" and are too tight now that CLDR decides the width: the bar-end
labels on the slowest-task chart reserved a fixed 64px, and the Gantt
axis derived its tick spacing from an eight-character "HH:MM:SS"
estimate. German narrow needs roughly half again as much room.

The Gantt axis also placed ticks at evenly divided raw values, which
the old truncating format hid; at this precision a seven-second span
read 0s | 1.17s | 2.33s. Snapping to the same counted units the other
duration axes use keeps short spans legible.

Rounding to two units leaves "1h 2m" covering a full minute, which is
too coarse to tell two similar runs apart in a list. The exact value now
rides along in a title on the duration columns, so the compact form
stays scannable without giving up the precise number.
@bbovenzi
bbovenzi force-pushed the dev/timestamp-subsecond-durations-191d7e branch from 4b6f540 to da399ed Compare September 1, 2026 13:55

@pierrejeambrun pierrejeambrun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM thanks.

@ryanahamilton ryanahamilton left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had one more small comment and looks like you have a merge conflict. Overall looks like a nice enhancement.

import { Text, type TextProps } from "@chakra-ui/react";

import { renderDuration } from "src/utils";
import { useDurationFormat } from "src/utils/useDurationFormat";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like src/utils has a barrel export file that didn't get amended for this new util. Would probably be ideal to add it and keep this pattern consistent.

Suggested change
import { useDurationFormat } from "src/utils/useDurationFormat";
import { useDurationFormat } from "src/utils";

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:UI Related to UI/UX. For Frontend Developers. backport-to-v3-3-test Backport to v3-3-test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants