/**
* A calendar date, and deliberately not an instant.
*
* ### The bug this file exists to make impossible
*
* A due date is not a moment. "This is due on the 19th" is true in
* Denver and true in Tokyo, and it does not become the 18th because
* a laptop crossed a meridian. The classic date-picker defect is
* that the value is a `Date` — which is an instant, a count of
* milliseconds — and every read of it goes through the *browser's*
* timezone. Pick the 19th at 23:30 in Denver, store
* `new Date("2026-08-19").toISOString()`, and the server, the next
* device, and the same device after a flight all disagree about
* which day you meant. It is a one-day error, it only appears near
* midnight or after travel, and it is invisible in every test
* written at noon.
*
* So the value here is a **plain date**: a year, a month, and a day,
* with no time, no offset, and no zone. It is stored as an ISO
* `YYYY-MM-DD` string — which is exactly ``'s own
* `value` format, so a consumer can move between this component and
* the native control without a migration.
*
* ### There is no `Date` arithmetic in here at all
*
* Not "carefully-written `Date` arithmetic" — none. Every operation
* below is integer maths on a **day number**: the count of days
* since 1970-01-01, computed with Howard Hinnant's `days_from_civil`
* / `civil_from_days` (public domain, the algorithm C++20's
* `` and Rust's `chrono` both use). Adding a day is `+ 1`.
* That is a proleptic Gregorian calendar with no DST, no leap
* seconds, and no zone, which is what a calendar date actually is.
*
* A `Date` appears in exactly two places, both of them named and
* both of them fenced:
*
* - `getLocalPlainDate`, which is the **one** conversion from an
* instant to a calendar date, and which requires you to say
* which zone's calendar you mean.
* - `formatPlainDate` and its callers, which hand `Intl` a date
* built at UTC midnight and format it with `timeZone: "UTC"`, so
* the formatter cannot shift the day either.
*
* `plainDate.test.ts` re-runs the whole suite under four `TZ`
* values, including UTC+14 and UTC-11, and asserts every answer is
* byte-identical.
*
* ### Why not `Temporal`
*
* `Temporal.PlainDate` is this type, done properly, and it is
* available in the Node this repo builds on. It is not available in
* every browser a published package has to run in, and the polyfill
* is ~50 KB gz — an order of magnitude more than this entire
* component. The functions below are the ~80 lines of it a date
* picker needs. When `Temporal` is baseline, this file becomes a
* thin adapter and the public API — ISO strings — does not change,
* which is the reason the API is ISO strings.
*/
/** Year, month (1-12), day (1-31). No time, no zone. */
export type PlainDate = {
day: number;
month: number;
year: number;
};
/**
* A start and an end, either of which may be absent — because a
* half-picked range is a real state a user spends time in, not an
* error. `start` with a null `end` is "I have clicked once".
*/
export type DateRange = {
end: null | string;
start: null | string;
};
export declare const getIsLeapYear: (year: number) => boolean;
export declare const getDaysInMonth: (year: number, month: number) => number;
export declare const getIsValidPlainDate: ({ day, month, year, }: PlainDate) => boolean;
/**
* Days since 1970-01-01, by Hinnant's `days_from_civil`.
*
* Every other operation in this file is expressed through this and
* its inverse, which is why "add a month, then add 40 days, then
* ask what weekday it is" cannot drift: there is one representation
* and it is an integer.
*/
export declare const getDayNumber: ({ day, month, year, }: PlainDate) => number;
/** The inverse — Hinnant's `civil_from_days`. */
export declare const getPlainDateFromDayNumber: (dayNumber: number) => PlainDate;
export declare const addDays: (date: PlainDate, days: number) => PlainDate;
/**
* Month arithmetic clamps the day rather than overflowing, which is
* the answer every calendar app gives and the one `Date` does not:
* `new Date(2026, 0, 31)` plus a month is **3 March**, because 31
* February rolls forward. One month after 31 January is 28
* February.
*/
export declare const addMonths: (date: PlainDate, months: number) => PlainDate;
export declare const comparePlainDates: (first: PlainDate, second: PlainDate) => number;
export declare const getIsSameDay: (first: PlainDate, second: PlainDate) => boolean;
/**
* Whole days from `from` to `to`. Docket's staleness threshold is
* counted in days, and this is the subtraction that counts it —
* with no hour-of-day and no DST to make "14 days" occasionally mean
* 13.958.
*/
export declare const getDaysBetween: (from: PlainDate, to: PlainDate) => number;
export declare const clampPlainDate: (date: PlainDate, minimum?: PlainDate, maximum?: PlainDate) => PlainDate;
export declare const getIsWithinRange: (date: PlainDate, minimum?: PlainDate, maximum?: PlainDate) => boolean;
/**
* 0 is Sunday, 6 is Saturday — the numbering `Intl`'s `weekday`
* option and `Date.prototype.getDay` both use, so the two never
* need reconciling at a call site.
*
* Day number 0 (1970-01-01) was a Thursday, which is the `+ 4`.
*/
export declare const getWeekday: (date: PlainDate) => number;
export declare const toIsoDate: (date: PlainDate) => string;
/**
* `null` rather than a throw, and `null` rather than a "best
* effort" date. A string that is not a calendar date has no nearest
* calendar date, and inventing one is the silent-guess failure this
* component is built to avoid.
*/
export declare const parseIsoDate: (isoDate: null | string | undefined) => null | PlainDate;
export type PlainDateFormatOptions = Intl.DateTimeFormatOptions & {
locale?: string;
};
/**
* `timeZone: "UTC"` is forced, and it is not configurable.
*
* The instant above is UTC midnight, so formatting it in any other
* zone renders the previous or the next day — which is the same
* one-day error as storing an instant, arriving through the display
* layer instead. There is no legitimate reason to format a calendar
* date in a zone: the date *is* the answer.
*/
export declare const formatPlainDate: (date: PlainDate, { locale, ...options }?: PlainDateFormatOptions) => string;
/**
* The one instant-to-calendar-date conversion in the library, and it
* makes you name the zone.
*
* Defaulting `timeZone` to the device's own resolved zone is right
* for "what is today on this screen"; passing one explicitly is
* right for "what is today for this household". Either way the
* question is asked out loud, which is the difference between this
* and `new Date().toISOString().slice(0, 10)` — a line that appears
* in three of the fleet's repos and is wrong for everybody west of
* Greenwich after 17:00.
*/
export declare const getLocalPlainDate: (now: Date, timeZone?: string) => PlainDate;
export declare const getFirstDayOfWeek: (locale?: string) => number;
/**
* The seven weekday names, rotated so index 0 is the locale's own
* first day. A fixed sample week is formatted rather than a
* hand-written table, so every locale the browser knows is covered
* and nothing here needs translating.
*/
export declare const getWeekdayNames: ({ firstDayOfWeek, locale, weekday, }: {
firstDayOfWeek: number;
locale?: string;
weekday: "long" | "narrow" | "short";
}) => string[];
/** January-first month names, for the parser and the header. */
export declare const getMonthNames: ({ locale, month, }: {
locale?: string;
month: "long" | "short";
}) => string[];
//# sourceMappingURL=plainDate.d.ts.map