Skip to content

humanizeDuration()

Converts a Temporal.Duration into the most significant single-unit phrase — magnitude only, with no "ago"/"in" direction.

Signature

ts
function humanizeDuration(
  duration: Temporal.Duration,
  locale?: string
): string

Parameters

ParameterTypeDefaultDescription
durationTemporal.DurationrequiredThe duration to describe. The sign is ignored — output is always positive magnitude.
localestringruntime defaultBCP 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)UnitExample
< 60 secondsseconds"45 seconds"
< 60 minutesminutes"3 minutes"
< 24 hourshours"2 hours"
< 7 daysdays"3 days"
< 30 daysweeks"2 weeks"
< 365 daysmonths"3 months"
≥ 365 daysyears"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

ts
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.

ts
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

ts
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.

ts
import { humanizeDuration } from 'moment-less'

humanizeDuration(Temporal.Duration.from({ hours: -3 })) // → "3 hours"

For past/future framing ("3 hours ago", "in 3 hours"), use fromNow() 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.

Released under the MIT License.