/** * What a human types into a date field, turned into a calendar date * — or into a stated reason it was not one. * * ### The calendar grid is the easy half * * Seven columns and some modular arithmetic is a weekend. The half * that decides whether a date field is pleasant or infuriating is * what happens when somebody types. Every hand-rolled date field in * this fleet either accepts nothing but the format it prints, or * hands the string to `new Date(…)` — which accepts `"Tuesday"` as * an `Invalid Date`, accepts `"2026-08-19"` as *UTC midnight* while * accepting `"08/19/2026"` as *local* midnight, and turns `"5"` into * the year 2001. That last one is a real, specified behaviour and it * is why `Date.parse` appears nowhere in this file. * * ### The rule that governs all of it: never guess in silence * * Three mechanisms, and they are the whole design: * * 1. **The grammar is ordered and total.** Each rule below is * anchored on the *whole* input. The first rule that matches * wins, and if none does the result is `unparsed` with a reason * — never a nearest-guess, never a partial read. `8/` is not * the first of August. * 2. **Ambiguity is a failure with a name, not a coin flip.** A * month or weekday prefix that matches more than one name comes * back as `unparsed` saying which ones: `ju` is not June, it is * "June or July — type more of it". This is why the matcher * tests uniqueness instead of taking the first hit, which is * what every library that "just works" does and is exactly how * a July task lands in June. * 3. **Nothing commits without an echo.** `DatePicker` renders the * resolved date in full underneath the input, in a live region, * before and after commit. The one genuinely irreducible * ambiguity in date entry — `3/4` — is resolved by the locale's * own field order and then *shown*, so a wrong reading is * visible rather than discovered in three weeks. * * ### The one place a reading is swapped, and why it is not a guess * * `19/8` in an en-US locale has no valid month-first reading — there * is no nineteenth month. Rather than rejecting it, the swapped * reading is taken, because it is the **only** valid one. When both * readings are valid (`3/4`) the locale's order wins and nothing is * swapped. So the rule is: *swap only when exactly one arrangement * is a real date*, which is deduction rather than preference. * * ### Everything locale-dependent is read from `Intl` * * Field order, month names, weekday names. There is no English word * list in this file except the relative keywords (`today`, * `tomorrow`, `next week`), which are the one thing `Intl` does not * publish. Those are the documented, explicitly-English part of the * surface, and a consumer that needs others supplies `keywords`. */ import type { PlainDate } from "./plainDate.ts"; export type DateInputResult = { date: PlainDate; kind: "date"; } | { kind: "empty"; } | { kind: "unparsed"; reason: string; }; export type DateInputKeywords = { /** `last`, as in `last friday`. */ last: readonly string[]; /** `next`, as in `next friday` and `next week`. */ next: readonly string[]; today: readonly string[]; tomorrow: readonly string[]; /** Unit suffixes for `+3d` and `in 2 weeks`, longest-first. */ units: { day: readonly string[]; month: readonly string[]; week: readonly string[]; year: readonly string[]; }; yesterday: readonly string[]; }; /** * English, and stated rather than assumed. * * `Intl` publishes month and weekday names for every locale a * browser knows, so those are never written down. It publishes * nothing for "tomorrow" — `Intl.RelativeTimeFormat` *renders* one * but cannot parse one — so this table is the honest boundary of * what the component understands out of the box, and it is a prop so * a consumer is not stuck with it. */ export declare const DEFAULT_DATE_INPUT_KEYWORDS: DateInputKeywords; export type ParseDateInputOptions = { keywords?: DateInputKeywords; locale?: string; /** * The clock, injected. Every relative form resolves against this * and nothing reads the system clock, which is what lets the whole * grammar be tested with a fixed date instead of tests that pass * on the day they were written. */ today: PlainDate; }; export declare const parseDateInput: (rawText: string, { keywords, locale, today, }: ParseDateInputOptions) => DateInputResult; //# sourceMappingURL=parseDateInput.d.ts.map