# @ethisyscore/core-utils

Framework-agnostic utilities shared by the EthisysCore monolith and plugins.
Pure TypeScript — **no React, no MUI**. Utilities are grouped by domain: `date`
(date/time formatting incl. the org-format seam), `patch`, `list`, `money`
(currency display incl. compact abbreviation), and `number` (plain-number and
percentage display). Import a group via its sub-path (e.g.
`@ethisyscore/core-utils/money`, `@ethisyscore/core-utils/number`) or pull
everything from the umbrella entry.

## Install

```bash
npm install @ethisyscore/core-utils
```

`date-fns` is a runtime dependency. `dayjs` is an **optional** peer dependency —
only needed if you import the `./date/dayjs` sub-path.

## Usage

```ts
// Everything (umbrella entry)
import { formatDateSafe, dateOnlyToIsoUtc, formatDurationNumber } from "@ethisyscore/core-utils";

// Or import the date group directly
import { addDaysToIsoDate, toHHmmss } from "@ethisyscore/core-utils/date";

// dayjs-backed picker helper (requires dayjs)
import { dayjsToIsoDayBoundary } from "@ethisyscore/core-utils/date/dayjs";
```

## What's inside (`./date`)

| Area | Exports |
| --- | --- |
| **Format constants** | `DATE_FORMAT`, `DISPLAY_DATE_FORMAT`, `AUDIT_DATE_FORMAT`, `DAY_DATE_FORMAT`, `DAY_MONTH_FORMAT`, `DAY_MONTH_YEAR_FORMAT`, `TIME_FORMAT`, `SHORT_DATETIME_FORMAT` |
| **ISO / timezone-safe** | `isDateOnlyString`, `formatDate`, `getTodayIsoDate`, `getMonthStartIso`, `getMonthEndIso`, `parseIsoDateLocal`, `addDaysToIsoDate`, `toDateOnlyString` |
| **Display formatting** | `formatDateString`, `formatDateSafe`, `formatDateWithOrdinal`, `formatTimeAgo` |
| **Duration / timespan** | `timespanToMilliseconds`, `millisecondsToHours`, `hoursToMilliseconds`, `formatMillisecondsAsTimeSpent`, `formatDuration`, `formatDurationNumber`, `toHHmm`, `toHHmmss` |
| **Wire boundary (`<input>` ⇄ .NET `DateTimeOffset`)** | `isoToDateInput`, `dateInputToIso`, `dateOnlyToIsoUtc`, `nowLocalDateTimeInputValue`, `localDateTimeInputToIsoUtc`, `isoUtcToLocalDateTimeInput`, `ensureUtcIso` |
| **Ranges / timezone** | `getDateRange`, `computeDateRange`, `currentMonthRangeUtc`, `getCurrentOffsetMinutes` |
| **dayjs sub-path** | `dayjsToIsoDayBoundary` |

The wire-boundary helpers exist to bridge the naive local strings emitted by
`<input type="date">` / `<input type="datetime-local">` to full ISO-8601 UTC,
avoiding the offset/DST drift that occurs when a .NET `DateTimeOffset` endpoint
reinterprets a no-suffix timestamp as UTC.

## Scripts

```bash
npm run build   # tsup → ESM + CJS + d.ts
npm test        # vitest
npm run lint    # tsc --noEmit
```
