Move docs/ package mirror into document-navigator plugin

Relocate the ~6k-file docs tree from the repo root into
plugins/document-navigator/docs/ so the plugin is self-contained and
portable. docs/superpowers/ (specs and plans) stays at the repo root.

Moved via cp+rm rather than git mv because directory rename() fails with
EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the
identical-content files at commit time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-30 01:10:00 +01:00
parent fe3fedf42e
commit 9bf56a806b
6022 changed files with 12437 additions and 0 deletions

View File

@@ -0,0 +1,100 @@
# business-days
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The `business-days` module provides a complete suite of working-day arithmetic functions for `@suaveplan/datetime`. It models "business day" as any day that is neither in the caller-supplied `weekendDays` set (default: Sunday and Saturday) nor in the caller-supplied `holidays` list. This allows the same API to serve different jurisdictions and corporate calendars without baking in any cultural assumptions.
All functions accept the polymorphic `DateInput` type and delegate to `toDate` for coercion, so callers can pass ISO strings, epoch milliseconds, or `Date` objects interchangeably.
## Features
- `isBusinessDay` — predicate for whether a given date is a working day
- `nextBusinessDay` / `previousBusinessDay` — scan forward/backward for the nearest working day
- `addBusinessDays` / `subtractBusinessDays` — advance or retreat by an exact count of working days
- `businessDaysBetween` — count working days in a half-open interval `[start, end)`
- Configurable `weekendDays` (any subset of 0–6)
- Holiday exclusion via an `Date[]` array
- Cooperative cancellation via `AbortSignal` (checked every 256 iterations in `businessDaysBetween`)
- OTel counter via `recordBusinessDaysBetween`
## Usage: Basic Example
```ts
import { isBusinessDay, addBusinessDays } from "@suaveplan/datetime/business-days";
// Simple weekday check
console.log(isBusinessDay("2024-03-18")); // true (Monday)
console.log(isBusinessDay("2024-03-16")); // false (Saturday)
// Advance 5 business days
const result = addBusinessDays("2024-03-18", 5);
console.log(result.toDateString()); // Fri Mar 29 2024 (skips weekend)
```
## Usage: Advanced Example
```ts
import {
businessDaysBetween,
nextBusinessDay,
} from "@suaveplan/datetime/business-days";
const nyseHolidays = [
new Date("2024-01-01"), // New Year's Day
new Date("2024-07-04"), // Independence Day
];
// How many NYSE trading days in Q1 2024?
const q1 = businessDaysBetween("2024-01-01", "2024-04-01", {
holidays: nyseHolidays,
});
console.log(q1); // ~62 trading days
// Cooperative cancellation
const controller = new AbortController();
setTimeout(() => controller.abort(), 10);
const days = businessDaysBetween("2020-01-01", "2030-01-01", {
signal: controller.signal,
});
console.log(days); // partial count, aborted after ~256 iterations
```
## API Reference
### `isBusinessDay(date: DateInput, opts?: BusinessDayOptions): boolean`
Returns `true` when `date` is not a weekend day and not a holiday.
- `opts.weekendDays` — default `[0, 6]` (Sun, Sat)
- `opts.holidays` — list of `Date` objects to exclude
### `nextBusinessDay(date: DateInput, opts?: BusinessDayOptions): Date`
Returns the first business day strictly after `date`.
### `previousBusinessDay(date: DateInput, opts?: BusinessDayOptions): Date`
Returns the first business day strictly before `date`.
### `addBusinessDays(date: DateInput, n: number, opts?: BusinessDayOptions): Date`
Advance `date` by exactly `|n|` business days. Negative `n` moves backward.
### `subtractBusinessDays(date: DateInput, n: number, opts?: BusinessDayOptions): Date`
Move `date` backward by exactly `n` business days. Delegates to `addBusinessDays(date, -n)`.
### `businessDaysBetween(start: DateInput, end: DateInput, opts?: BusinessDayOptions): number`
Count business days in `[start, end)`. Returns a negative number when `end < start`. Checks `opts.signal` every 256 iterations.
## Implementation Notes
Holiday lookup uses a `Set<string>` keyed by `"YYYY-M-D"` (local time) built once per call. This is O(1) per day check and avoids allocating `Date` objects for the comparison.
All date arithmetic is performed at local-midnight epoch millis to prevent time-of-day accumulation errors across DST transitions. The `midnight()` helper normalises any input to 00:00:00.000 local time before iteration begins.
`addBusinessDays(d, 0)` returns the local midnight of `d` regardless of whether `d` itself is a business day, because zero steps means no movement.