Skip to content

Duration

  • The core functions, which every industry uses.

An amount of time has two shapes: an ISO 8601 duration string ("PT15M") and a unit object ({ minutes: 15 }). The duration/ functions take the string, except totalUnits and durationFromUnits, which take the object. The date arithmetic functions (addDate, subtractZoned and their siblings) take the object. Passing the wrong shape is the most common source of duration bugs.

HIGH

Confusing duration strings with unit objects

'P1Y2M' and { years: 1, months: 2 } are one amount in two shapes, and a function takes only one of them. The other shape returns the invalid-input sentinel. When you hold a unit object and the function takes a duration string, write the string with durationFromUnits.

Wrong

formatDuration({ years: 1, months: 2 }, "en-US"); // "" — a unit object, not a duration string
addDate("2024-01-15", "P1Y2M"); // "" — a duration string, not a unit object

Right

import { addDate, durationFromUnits, formatDuration } from "@northguild/gmt";
durationFromUnits({ years: 1, months: 2 }); // "P1Y2M"
formatDuration(durationFromUnits({ years: 1, months: 2 }), "en-US"); // "1 year and 2 months"
addDate("2024-01-15", { years: 1, months: 2 }); // "2025-03-15"
MEDIUM

Not validating before parsing

parseDuration returns an empty string for invalid input. Feeding unvalidated user input straight into duration arithmetic produces silent failures.

Wrong

const d = parseDuration(userInput); // userInput = "PX" → "" — no error thrown

Right

import { isValidDuration, parseDuration } from "@northguild/gmt";
if (isValidDuration(userInput)) {
const d = parseDuration(userInput);
}
MEDIUM

Expecting a best-effort answer for calendar units without relativeTo

Converting a calendar-unit duration (years, months) to days requires a reference date. Without relativeTo, the conversion is ambiguous — different months have different lengths.

Wrong

totalDuration("P1M", "days"); // null — 28, 29, 30 or 31 days: without relativeTo, which month?

Right

import { totalDuration } from "@northguild/gmt";
totalDuration("P1M", "days", { relativeTo: "2024-02-01" }); // 29 — February 2024 has 29 days
MEDIUM

Giving totalUnits a week

totalUnits takes exact units only: days, hours, minutes, seconds, milliseconds, microseconds and nanoseconds. A week, a month or a year returns null, as a key or as the unit to total into, because TC39 Temporal gives those units no length without a date to measure from. Write a week as seven days. For a calendar amount, use totalDuration with relativeTo.

Wrong

totalUnits({ weeks: 1 }, "days"); // null — a week is not an exact unit
totalUnits({ days: 7 }, "weeks"); // null — the same rule for the unit to total into

Right

import { totalDuration, totalUnits } from "@northguild/gmt";
totalUnits({ days: 7 }, "hours"); // 168 — a day counts as 24 hours
totalDuration("P1W", "days", { relativeTo: "2024-02-01" }); // 7 — a week measured from a date
HIGH

Reading a component with getDurationUnit when you wanted a total

getDurationUnit returns one component of a normalized duration (e.g. just the hours). If you need the total duration in a specific unit, use totalDuration instead.

Wrong

const d = normalizeDuration("P1DT2H3M");
const hours = getDurationUnit(d, "hours"); // 2 — just the hours component, not the total

Right

import { normalizeDuration, totalDuration } from "@northguild/gmt";
const d = normalizeDuration("P1DT2H3M");
const totalHours = totalDuration(d, "hours"); // 26.05 — full duration converted, 3 minutes included
MEDIUM

Expecting normalizeDuration to resolve calendar units against today

normalizeDuration has no implicit relativeTo. Whenever a calendar unit (years, months, weeks) is involved, in the duration or as largestUnit, it needs an explicit relativeTo; without one it returns the empty-string sentinel, never a conversion against today's date.

Wrong

normalizeDuration("P1M", { largestUnit: "day" }); // "" — no relativeTo, so no answer

Right

import { normalizeDuration } from "@northguild/gmt";
normalizeDuration("P1M", { largestUnit: "day", relativeTo: "2024-02-01" }); // "P29D"
MEDIUM

Expecting formatDuration to match native Intl.DurationFormat output exactly

GMT's formatDuration renders each non-zero component with Intl.NumberFormat's unit style and joins them with Intl.ListFormat. Intl.DurationFormat output varies by locale, unit set, and style options in its own way. They are not drop-in replacements for each other.

Wrong

// assuming formatDuration("PT1H30M") matches Intl.DurationFormat

Right

import { formatDuration } from "@northguild/gmt";
formatDuration("PT1H30M", "en-US"); // "1 hour and 30 minutes" — joined as a list
MEDIUM

Passing an array of units to totalDuration

totalDuration takes a single unit name, not an array. Passing a list of unit names returns null.

Wrong

totalDuration("P1DT2H3M", ["hours", "minutes"]); // null — expects a single unit string

Right

import { totalDuration } from "@northguild/gmt";
totalDuration("P1DT2H3M", "hours"); // 26.05
totalDuration("P1DT2H3M", "minutes"); // 1563 — one call per unit