first commit
This commit is contained in:
84
docs/foundation/timing/datetime/src/relative/relative.md
Normal file
84
docs/foundation/timing/datetime/src/relative/relative.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# relative
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## 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.
|
||||
Reference in New Issue
Block a user