UNPKG

@burglekitt/gmt

Version:

Temporal-based date and time utilities with timezone support and polyfill integration

224 lines (160 loc) 8.24 kB
--- name: format-relative-time description: > Produce human-friendly relative time strings ("yesterday", "in 2 hours", "3 days ago") for any gmt value type. Use formatRelativeDate, formatRelativeTime, formatRelativeDateTime for plain values; formatRelativeZoned for IANA zoned values; formatRelativeUnix for epoch ms/seconds; formatRelativeUtc for UTC ISO strings. All accept a reference anchor and Intl.RelativeTimeFormat options (numeric, style). sources: - 'burglekitt/gmt:packages/gmt/src/plain/format/formatRelativeDate.ts' - 'burglekitt/gmt:packages/gmt/src/plain/format/formatRelativeTime.ts' - 'burglekitt/gmt:packages/gmt/src/plain/format/formatRelativeDateTime.ts' - 'burglekitt/gmt:packages/gmt/src/zoned/format/formatRelativeZoned.ts' - 'burglekitt/gmt:packages/gmt/src/unix/format/formatRelativeUnix.ts' - 'burglekitt/gmt:packages/gmt/src/utc/format/formatRelativeUtc.ts' metadata: type: core library: '@burglekitt/gmt' library_version: '1.14.1' --- # Format Relative Time Use this skill when you need a human-friendly relative time label — "yesterday", "in 5 minutes", "2 weeks ago" — instead of an absolute timestamp. There is one relative formatter per value type; pick the one matching the input. ## Picking the right formatter | Input shape | Formatter | Module | |---|---|---| | ISO date `"2024-03-15"` | `formatRelativeDate` | `@burglekitt/gmt` | | ISO time `"14:30:45"` | `formatRelativeTime` | `@burglekitt/gmt` | | ISO datetime `"2024-03-15T14:30:45"` | `formatRelativeDateTime` | `@burglekitt/gmt` | | Zoned `"2024-03-15T14:30:45-05:00[America/New_York]"` | `formatRelativeZoned` | `@burglekitt/gmt/zoned` | | Unix epoch (ms or seconds) | `formatRelativeUnix` | `@burglekitt/gmt/unix` | | UTC ISO `"2024-03-15T14:30:45Z"` | `formatRelativeUtc` | `@burglekitt/gmt/utc` | ## Core Patterns ### Plain date relative ```ts import { formatRelativeDate } from "@burglekitt/gmt"; const ref = "2024-03-15"; formatRelativeDate("2024-03-14", "en-US", { reference: ref }); // "yesterday" formatRelativeDate("2024-03-16", "en-US", { reference: ref }); // "tomorrow" formatRelativeDate("2024-03-12", "en-US", { reference: ref }); // "3 days ago" formatRelativeDate("2024-02-23", "en-US", { reference: ref, largestUnit: "week", numeric: "always", }); // "3 weeks ago" ``` ### Plain time relative ```ts import { formatRelativeTime } from "@burglekitt/gmt"; const ref = "12:00:00"; formatRelativeTime("11:30:00", "en-US", { reference: ref }); // "30 minutes ago" formatRelativeTime("12:00:30", "en-US", { reference: ref }); // "in 30 seconds" ``` ### Plain datetime relative ```ts import { formatRelativeDateTime } from "@burglekitt/gmt"; const ref = "2024-03-15T12:00:00"; formatRelativeDateTime("2024-03-15T10:00:00", "en-US", { reference: ref }); // "2 hours ago" ``` ### Zoned relative (DST-safe) ```ts import { formatRelativeZoned } from "@burglekitt/gmt/zoned"; const ref = "2024-03-15T12:00:00-04:00[America/New_York]"; formatRelativeZoned( "2024-03-15T10:00:00-04:00[America/New_York]", "en-US", { reference: ref }, ); // "2 hours ago" ``` Zoned arithmetic uses `Temporal.ZonedDateTime`, so spans that cross DST transitions ("fall back" / "spring forward") are still wall-clock-correct. ### Unix relative ```ts import { formatRelativeUnix } from "@burglekitt/gmt/unix"; // Default epoch unit is milliseconds. formatRelativeUnix(1710507600000, "en-US", { reference: 1710511200000 }); // "1 hour ago" // Switch to seconds via epochUnit. formatRelativeUnix(1710507600, "en-US", { reference: 1710511200, epochUnit: "seconds", }); // "1 hour ago" ``` ### UTC relative ```ts import { formatRelativeUtc } from "@burglekitt/gmt/utc"; formatRelativeUtc("2024-03-15T11:00:00Z", "en-US", { reference: "2024-03-15T12:00:00Z", }); // "1 hour ago" ``` ## Shared options All relative formatters accept the same option shape (besides their type-specific `reference`): - `reference` — **required** anchor of the same input shape (e.g. an ISO date for `formatRelativeDate`). - `numeric: "auto" | "always"` — default `"auto"`. - `"auto"` produces phrases like `"yesterday"`, `"tomorrow"`, `"last week"`. - `"always"` forces numeric form: `"1 day ago"`, `"in 1 day"`. - `style: "long" | "short" | "narrow"` — default `"long"`. - `"long"`: `"3 minutes ago"` - `"short"`: `"3 min. ago"` - `"narrow"`: `"3m ago"` - `largestUnit` — pick a specific bucket (`"second" | "minute" | "hour" | "day" | "week" | "month" | "quarter" | "year"`). Useful when you want `"3 weeks ago"` even though the difference would auto-bucket to `"last month"`. - `roundingMethod: "floor" | "ceil" | "round"` — default `"round"`. Controls how the fractional distance rounds to the display unit; `"floor"`/`"ceil"` apply directly to the signed value (not its absolute value), so they respect whether the distance is past or future. ## Runtime ICU data These formatters delegate locale rendering to the host runtime's `Intl.RelativeTimeFormat`. Output therefore depends on the ICU data shipped with the running Node (or browser): - **Full ICU** runtimes (official Node binaries from nodejs.org, all modern browsers) return fully localized strings — e.g. `formatRelativeDate("2023-03-15", "sv-SE", { reference: "2024-03-15" })` returns `"i fjol"`. - **Small/partial ICU** runtimes (some Node builds compiled with `--with-intl=small-icu` or repackaged distributions) fall back to a longer/different form — the same call may return `"förra året"`. Some locales (e.g. he-IL) also tack on a fallback numeric suffix like `"לפני שעתיים (2)"` instead of the full-ICU dual form `"לפני שעתיים"`. This is a property of the runtime, not gmt. For consistent non-English output, deploy on a full-ICU Node build or polyfill `Intl` with a package that bundles locale data. ## Common Mistakes ### HIGH Forgetting `reference` Wrong: ```ts formatRelativeDate("2024-03-12"); // "" — no anchor to diff against ``` Correct: ```ts formatRelativeDate("2024-03-12", "en-US", { reference: "2024-03-15" }); // "3 days ago" ``` Source: `packages/gmt/src/plain/format/formatRelativeDate.ts` — `reference` is required. ### HIGH Mismatched input shape between value and reference Wrong: ```ts formatRelativeDate("2024-03-15T10:00:00", "en-US", { reference: "2024-03-15", // datetime as value, date as reference }); // "" ``` Correct: match shapes — use `formatRelativeDateTime` for datetimes, `formatRelativeDate` for dates, etc. Source: each relative formatter validates its input shape and returns `""` on mismatch. ### HIGH Hand-rolling diff math with `Date` Wrong: ```ts const diffMs = new Date(value).getTime() - new Date(ref).getTime(); const label = `${Math.round(diffMs / 60000)} minutes ago`; // ignores locale, DST ``` Correct: ```ts import { formatRelativeDateTime } from "@burglekitt/gmt"; formatRelativeDateTime(value, locale, { reference: ref }); ``` Source: AGENTS.md — Never use JavaScript Date APIs. ### MEDIUM Picking the wrong formatter for a Unix value Wrong: ```ts import { formatRelativeUtc } from "@burglekitt/gmt/utc"; formatRelativeUtc(1710507600000, "en-US", { reference: 1710511200000 }); // "" — formatRelativeUtc expects ISO strings, not numbers ``` Correct: ```ts import { formatRelativeUnix } from "@burglekitt/gmt/unix"; formatRelativeUnix(1710507600000, "en-US", { reference: 1710511200000 }); // "1 hour ago" ``` Source: `packages/gmt/src/unix/format/formatRelativeUnix.ts`. ### LOW Relying on `"auto"` numeric for tomorrow/yesterday wording `numeric: "auto"` produces the locale's natural words ("yesterday", "ayer", "gestern") only when the diff is exactly ±1 of the largest unit. Past that boundary it falls back to numeric ("2 days ago"). If you need a consistent word-form output, pair `numeric: "auto"` with `largestUnit` to force a specific bucket. ## References - [Intl.RelativeTimeFormat documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat) - [Format Date/Time skill](../format-date-time/SKILL.md) — for absolute formatting - [Zoned date operations skill](../zoned-date-ops/SKILL.md) — for timezone-aware operations