daymath 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # daymath
2
2
 
3
- Calendar date math for **ISO 8601** `YYYY-MM-DD` strings. **date-fns-shaped** names. **Temporal.PlainDate** under the hood.
3
+ Calendar date math for **ISO 8601** day strings. **date-fns-shaped** names. **Temporal.PlainDate** under the hood.
4
4
 
5
5
  No `Date`. No time zones. No silent “local now.”
6
6
 
@@ -31,23 +31,30 @@ differenceInDays('2026-08-06', '2026-08-01') // 5
31
31
  isBefore('2026-08-05', '2026-08-06') // true
32
32
  isSameDay('2026-08-06', '2026-08-06') // true (alias of isEqual)
33
33
  startOfMonth('2026-08-06') // '2026-08-01'
34
+ addDays('9999-12-31', 1) // '+010000-01-01' (expanded year)
34
35
  eachDayOfInterval({
35
36
  start: '2026-08-05',
36
37
  end: '2026-08-07',
37
38
  }) // ['2026-08-05', '2026-08-06', '2026-08-07']
38
39
  ```
39
40
 
40
- **Inputs:** ISO 8601 `YYYY-MM-DD` or `Temporal.PlainDate`.
41
- **Outputs:** always `YYYY-MM-DD` string (for math helpers).
41
+ **Inputs:** ISO 8601 day string or `Temporal.PlainDate`.
42
+ - `YYYY-MM-DD` (years 0000–9999)
43
+ - expanded `±YYYYYY-MM-DD` (e.g. `+010000-01-01`)
42
44
 
43
- `Date` throws. Time-bearing strings throw. Sloppy forms like `2026-8-6` throw.
45
+ **Outputs:** Temporal’s ISO day string (same forms).
46
+
47
+ `Date` throws (including `isValid(date)`). Bad strings: math helpers throw; `isValid('asdf')` → `false`. Time-bearing / sloppy forms throw.
48
+
49
+ See [FUTURE.md](./FUTURE.md) for backlog (bundle size, business days, …).
44
50
 
45
51
  ## date-fns parity notes
46
52
 
47
53
  | Topic | daymath |
48
54
  |-------|---------|
49
- | Value type | `YYYY-MM-DD` string (not `Date`) |
55
+ | Value type | ISO day string (not `Date`) |
50
56
  | `isSameDay` | Alias of `isEqual` (same calendar day) |
57
+ | `isValid` | Our predicate: valid day string / PlainDate. `Date` **throws** (not date-fns’s Date check) |
51
58
  | `getMonth` / `setMonth` | **0–11** like Date/date-fns (0 = January) |
52
59
  | `getDay` | **0–6** like Date/date-fns (0 = Sunday) |
53
60
  | `weekStartsOn` | `0` = Sunday … `6` = Saturday (default `0`) |
package/index.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import type { Temporal } from 'temporal-polyfill'
2
2
 
3
3
  /**
4
- * Calendar day input: ISO 8601 `YYYY-MM-DD` string, or a Temporal.PlainDate.
5
- * `Date` is rejected at runtime.
4
+ * Calendar day input: ISO 8601 day string, or a Temporal.PlainDate.
5
+ * - `YYYY-MM-DD` (years 0000–9999)
6
+ * - expanded `±YYYYYY-MM-DD` (e.g. `+010000-01-01`)
7
+ * `Date` is rejected at runtime (TypeError).
6
8
  */
7
9
  export type DayInput = string | Temporal.PlainDate
8
10
 
@@ -19,12 +21,16 @@ export type WeekOptions = {
19
21
  weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6
20
22
  }
21
23
 
24
+ /**
25
+ * True for a valid daymath day string / PlainDate.
26
+ * Invalid strings → false. `Date` → throws TypeError (not a quiet false).
27
+ */
22
28
  export function isValid(value: unknown): boolean
23
29
 
24
- /** Validate / normalize to ISO 8601 `YYYY-MM-DD`. */
30
+ /** Validate / normalize to ISO 8601 day string (Temporal `toString` form). */
25
31
  export function parse(date: DayInput): string
26
32
 
27
- /** Format as `YYYY-MM-DD` (only pattern supported). */
33
+ /** Format as ISO day (only `yyyy-MM-dd` / `YYYY-MM-DD` patterns supported). */
28
34
  export function format(date: DayInput, pattern?: 'yyyy-MM-dd' | 'YYYY-MM-DD'): string
29
35
 
30
36
  export function addDays(date: DayInput, amount: number): string
package/index.js CHANGED
@@ -1,8 +1,16 @@
1
- /** daymath — calendar date math (YYYY-MM-DD). date-fns-shaped. No Date / time zones. */
1
+ /** daymath — calendar date math (ISO 8601 day). date-fns-shaped. No Date / time zones. */
2
2
  import { Temporal as TemporalPolyfill } from 'temporal-polyfill'
3
3
 
4
4
  const Temporal = globalThis.Temporal ?? TemporalPolyfill
5
5
 
6
+ /**
7
+ * ISO 8601 calendar day string:
8
+ * - `YYYY-MM-DD` (years 0000–9999)
9
+ * - expanded `±YYYYYY-MM-DD` (Temporal form, e.g. `+010000-01-01`)
10
+ */
11
+ const ISO_DAY =
12
+ /^(?:[+-]\d{6}|\d{4})-\d{2}-\d{2}$/
13
+
6
14
  /** @typedef {string | Temporal.PlainDate} DayInput */
7
15
  /**
8
16
  * @typedef {object} Interval
@@ -17,7 +25,7 @@ const Temporal = globalThis.Temporal ?? TemporalPolyfill
17
25
  // ─── core conversion ───────────────────────────────────────────────
18
26
 
19
27
  /**
20
- * Reject Date and non-calendar values. Accept YYYY-MM-DD string or PlainDate.
28
+ * Reject Date and non-calendar values. Accept ISO day string or PlainDate.
21
29
  * @param {unknown} value
22
30
  * @param {string} label
23
31
  * @returns {Temporal.PlainDate}
@@ -25,14 +33,13 @@ const Temporal = globalThis.Temporal ?? TemporalPolyfill
25
33
  function toPlainDate(value, label = 'date') {
26
34
  if (value instanceof Date) {
27
35
  throw new TypeError(
28
- `daymath: Date is not allowed for ${label} (pass YYYY-MM-DD string)`,
36
+ `daymath: Date is not allowed for ${label} (pass ISO 8601 day string)`,
29
37
  )
30
38
  }
31
39
  if (typeof value === 'string') {
32
- // Strict ISO 8601 calendar date: YYYY-MM-DD only.
33
- if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
40
+ if (!ISO_DAY.test(value)) {
34
41
  throw new RangeError(
35
- `daymath: ${label} must be YYYY-MM-DD (got ${JSON.stringify(value)})`,
42
+ `daymath: ${label} must be ISO 8601 day YYYY-MM-DD or ±YYYYYY-MM-DD (got ${JSON.stringify(value)})`,
36
43
  )
37
44
  }
38
45
  try {
@@ -47,7 +54,7 @@ function toPlainDate(value, label = 'date') {
47
54
  return value
48
55
  }
49
56
  throw new TypeError(
50
- `daymath: ${label} must be YYYY-MM-DD string or Temporal.PlainDate`,
57
+ `daymath: ${label} must be ISO 8601 day string or Temporal.PlainDate`,
51
58
  )
52
59
  }
53
60
 
@@ -105,8 +112,17 @@ function toInterval(interval) {
105
112
 
106
113
  // ─── parse / format / valid ────────────────────────────────────────
107
114
 
108
- /** @param {unknown} value */
115
+ /**
116
+ * True if value is a valid daymath day (ISO day string or PlainDate).
117
+ * Invalid strings → false. `Date` → throws (not a quiet false — swap trap).
118
+ * @param {unknown} value
119
+ */
109
120
  export function isValid(value) {
121
+ if (value instanceof Date) {
122
+ throw new TypeError(
123
+ 'daymath: Date is not allowed for isValid (pass ISO 8601 day string)',
124
+ )
125
+ }
110
126
  try {
111
127
  toPlainDate(value)
112
128
  return true
@@ -116,7 +132,7 @@ export function isValid(value) {
116
132
  }
117
133
 
118
134
  /**
119
- * Validate / normalize a calendar day string (ISO 8601 YYYY-MM-DD).
135
+ * Validate / normalize an ISO 8601 calendar day string.
120
136
  * @param {DayInput} date
121
137
  * @returns {string}
122
138
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "daymath",
3
- "version": "0.2.0",
4
- "description": "Calendar date math (YYYY-MM-DD / PlainDate). date-fns-shaped. No time zones.",
3
+ "version": "0.2.1",
4
+ "description": "Calendar date math (ISO 8601 day / PlainDate). date-fns-shaped. No time zones.",
5
5
  "type": "module",
6
6
  "main": "./index.js",
7
7
  "types": "./index.d.ts",