# relative _(no submodules)_ ## Purpose The `relative` module provides human-readable relative-time formatting for `@suaveplan/datetime`. It converts the distance between two moments into concise English phrases like `"3 days ago"`, `"in 2 hours"`, or `"just now"`. The `clock` injection point makes all functions deterministic in tests, while the unified `formatRelative` entry point subsumes the common `timeAgo` / `timeUntil` aliases. The module also exposes `diffInUnits`, a lower-level utility that returns the signed numeric difference between two `DateInput` values in any `DurationUnit`. This is useful when callers need the raw integer delta rather than a formatted string. ## Features - `diffInUnits` — signed numeric difference in any `DurationUnit` - `formatRelative` — threshold-based English string (past and future) - `timeAgo` — convenience alias for past-relative formatting - `timeUntil` — convenience alias for future-relative formatting - Pluggable `Clock` for deterministic rendering in tests - `"just now"` threshold for differences under 45 seconds ## Usage: Basic Example ```ts import { timeAgo, timeUntil } from "@suaveplan/datetime/relative"; // Using wall clock console.log(timeAgo(new Date(Date.now() - 3 * 60_000))); // "3 minutes ago" console.log(timeUntil(new Date(Date.now() + 2 * 3600_000))); // "in 2 hours" ``` ## Usage: Advanced Example ```ts import { diffInUnits, formatRelative } from "@suaveplan/datetime/relative"; // Deterministic with a fake clock const clock = () => new Date("2024-06-01T12:00:00Z").getTime(); const pastDate = "2024-05-28T12:00:00Z"; console.log(formatRelative(pastDate, { clock })); // "4 days ago" console.log(diffInUnits(pastDate, "2024-06-01T12:00:00Z", "days")); // 4 // Sub-unit precision const oneHourAgo = new Date(clock() - 3723_000); // 1h 2m 3s ago console.log(formatRelative(oneHourAgo, { clock })); // "1 hour ago" console.log(diffInUnits(oneHourAgo, new Date(clock()), "seconds")); // 3723 ``` ## API Reference ### `diffInUnits(a: DateInput, b: DateInput, unit: DurationUnit): number` Returns `floor((b − a) / unitMs)` truncated toward zero. Positive when `b > a`. - `a` — start date (any `DateInput`) - `b` — end date (any `DateInput`) - `unit` — one of the `DurationUnit` values ### `formatRelative(date: DateInput, opts?: RelativeTimeOptions): string` Format the difference between `date` and `opts.clock()` (default: `Date.now()`). - Distances under 45 seconds → `"just now"` - Distances under 45 minutes → `"N seconds ago"` / `"in N seconds"` - Distances under 22 hours → `"N minutes ago"` / `"in N minutes"` - Distances under 26 days → `"N hours ago"` / `"in N hours"` - Distances under 11 months → `"N days ago"` / `"in N days"` - Otherwise → `"N months ago"` / `"in N months"` or `"N years ago"` / `"in N years"` ### `timeAgo(date: DateInput, opts?: RelativeTimeOptions): string` Alias for `formatRelative`. Intended for past dates, but works symmetrically. ### `timeUntil(date: DateInput, opts?: RelativeTimeOptions): string` Alias for `formatRelative`. Intended for future dates, but works symmetrically. ## Implementation Notes Average month (30.4375 days) and average year (365.25 days) constants are used for the coarse `months` / `years` thresholds. These are approximations intentionally — precise calendar arithmetic for display strings would add complexity without improving user experience. Use `diffInUnits("months")` together with calendar arithmetic when you need exact month counts. The threshold table mirrors the conventions used by popular libraries like `date-fns` and `moment.js` so that migrating code produces the same user-visible strings. The `precision` field in `RelativeTimeOptions` is reserved for future use to cap the label at a specific unit (e.g. only show up to "days", never "hours"). Currently it is accepted but not applied in the implementation.