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:
@@ -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.
|
||||
Reference in New Issue
Block a user