# datetime (adapter system)

Date/time math capability pack using JS Date + Intl only (zero dependencies): duration breakdown between two dates, add a duration, business-day counting/adding (Mon-Fri minus holidays), ISO week...


Date/time **math** pack built purely on the JS `Date` + `Intl` built-ins (both always present in Node)
— **zero dependencies**. Every tool takes ONE args object. Dates are accepted loosely (ISO string,
epoch-ms number, or anything `Date.parse` handles); calendar arithmetic runs in UTC so it's
deterministic and DST-agnostic.

## Tools (`adapters`)

| tool | args | result |
|------|------|--------|
| `durationBetween` | `{ from, to, unit? }` | `{ breakdown:{years,months,days,hours,minutes,seconds}, totals, negative }` |
| `addDuration` | `{ date, years?,months?,days?,hours?,minutes?,seconds? }` | `{ iso, epoch }` |
| `businessDays` | `{ from, to, holidays? }` | `{ businessDays, holidaysExcluded }` — Mon-Fri, inclusive of both ends |
| `addBusinessDays` | `{ date, days, holidays? }` | `{ iso, epoch, date }` |
| `weekNumber` | `{ date }` | `{ week, isoYear, label }` — ISO-8601 week |
| `dayOfYear` | `{ date }` | `{ dayOfYear, daysInYear }` |
| `timeAgo` | `{ date, now? }` | `{ phrase, unit, value, future }` — e.g. "3 days ago" / "in 2 hours" |
| `toTimezone` | `{ date, timeZone }` | `{ wallClock, parts, formatted }` via Intl in that IANA zone |
| `format` | `{ date, locale?, options? }` | `{ formatted }` via `Intl.DateTimeFormat` |
| `isWeekend` | `{ date }` | `{ isWeekend, weekday }` |
| `isLeapYear` | `{ year }` | `{ isLeapYear }` |
| `daysInMonth` | `{ year, month }` | `{ days }` (month is 1-12) |
| `parse` | `{ input }` | `{ iso, epoch, valid, year, month, day, weekday }` — tolerant |

## Usage

```js
import datetime from './index.js';
datetime.adapters.businessDays({ from: '2024-01-01', to: '2024-01-05' }); // → { businessDays: 5, ... }
datetime.adapters.timeAgo({ date: '2024-01-01', now: '2024-01-04' });     // → { phrase: '3 days ago', ... }
datetime.adapters.weekNumber({ date: '2024-01-01' });                     // → { week: 1, isoYear: 2024, ... }
datetime.adapters.toTimezone({ date: '2024-06-01T12:00:00Z', timeZone: 'America/New_York' });
```

## DRY boundary notes

- **Does NOT** parse interval strings like `"5m"` / `"2h"` into milliseconds — that belongs to the
  **`cron`** pack.
- **Does NOT** do generic ISO coercion of arbitrary/loose types — that's **a-transformation
  `timestamps.*`**. (This pack coerces inputs only as a convenience for its own math.)
- **Does NOT** render calendars (month grids, week layouts) — that's the **`calendar`** pack.
- `toTimezone` returns the wall-clock **parts** in the requested zone via `Intl` (which carries the
  real IANA tz db in Node). True raw numeric tz-offset arithmetic without a bundled tz db is out of
  scope here.

This pack = **duration / business-day / timezone / relative-time math.**


---
Source: shared/engines/adapters/domain/datetime/README.md
Canonical: https://docs.leumas.tech/p/adapters/domain/datetime
