humanizeDuration()
Converts a Temporal.Duration into the most significant single-unit phrase — magnitude only, with no "ago"/"in" direction.
Signature
function humanizeDuration(
duration: Temporal.Duration,
locale?: string
): stringParameters
| Parameter | Type | Default | Description |
|---|---|---|---|
duration | Temporal.Duration | required | The duration to describe. The sign is ignored — output is always positive magnitude. |
locale | string | runtime default | BCP 47 tag controlling the language and number formatting. |
Unit selection
humanizeDuration() reduces the whole duration to seconds — counting every field down to nanoseconds, with approximate month (30 days) and year (365 days) — then rounds to the largest unit whose threshold it meets:
| Total (approx) | Unit | Example |
|---|---|---|
| < 60 seconds | seconds | "45 seconds" |
| < 60 minutes | minutes | "3 minutes" |
| < 24 hours | hours | "2 hours" |
| < 7 days | days | "3 days" |
| < 30 days | weeks | "2 weeks" |
| < 365 days | months | "3 months" |
| ≥ 365 days | years | "1 year" |
Output uses Intl.RelativeTimeFormat's numeric: 'always' mode with the directional prefix stripped, so single units read as "1 hour", "1 day", "1 week" (not "an hour").
Examples
By unit
import { humanizeDuration } from 'moment-less'
humanizeDuration(Temporal.Duration.from({ seconds: 20 })) // → "20 seconds"
humanizeDuration(Temporal.Duration.from({ minutes: 1 })) // → "1 minute"
humanizeDuration(Temporal.Duration.from({ hours: 2 })) // → "2 hours"
humanizeDuration(Temporal.Duration.from({ days: 3 })) // → "3 days"
humanizeDuration(Temporal.Duration.from({ weeks: 2 })) // → "2 weeks"
humanizeDuration(Temporal.Duration.from({ months: 3 })) // → "3 months"
humanizeDuration(Temporal.Duration.from({ years: 1 })) // → "1 year"Multi-field durations
The whole duration collapses to its combined magnitude — just as you'd say it aloud.
import { humanizeDuration } from 'moment-less'
// 2h 45m → 2.75 hours → rounds to "3 hours"
humanizeDuration(Temporal.Duration.from({ hours: 2, minutes: 45 })) // → "3 hours"
// 400 days → "1 year"
humanizeDuration(Temporal.Duration.from({ days: 400 })) // → "1 year"
// sub-second components are counted, not dropped
humanizeDuration(Temporal.Duration.from({ microseconds: 999_999 })) // → "1 second"Localized
import { humanizeDuration } from 'moment-less'
const twoHours = Temporal.Duration.from({ hours: 2 })
humanizeDuration(twoHours) // → "2 hours"
humanizeDuration(twoHours, 'fr') // → "2 heures"
humanizeDuration(twoHours, 'es') // → "2 horas"
humanizeDuration(twoHours, 'de') // → "2 Stunden"Negative durations
The sign is dropped — humanizeDuration() reports how long a duration is, not its direction.
import { humanizeDuration } from 'moment-less'
humanizeDuration(Temporal.Duration.from({ hours: -3 })) // → "3 hours"For past/future framing (
"3 hours ago","in 3 hours"), usefromNow()instead.
Approximate months and years
A Temporal.Duration has no anchor date, so calendar-variable units are approximated:
- 1 month ≈ 30 days
- 1 year ≈ 365 days
This is intentional — humanizeDuration() is for display, not precise calendar arithmetic. For exact spans, use Temporal.PlainDate.until() with a reference date and read the .months / .years fields directly.