@absoluteui/helpers/date
Date-only values, calendar arithmetic, fiscal periods, weeks, intervals, formatting, and parsing.
import { ... } from '@absoluteui/helpers/date';How to use
Section titled “How to use”Date-only arithmetic that cannot shift by a day: PlainDate values (or 'YYYY-MM-DD' strings) with month-end-aware adding, differences, weeks under any locale’s rules, fiscal years, intervals, calendar grids, and strict parsing and formatting. Reach for it for joining dates, probation periods, leave ranges, and reporting periods. Everything here counts calendar days; for weekends, holidays, and business days, combine it with @absoluteui/helpers/business-calendar as shown below. The Dates and time zones guide explains the value types, and the Business calendars guide the business-day rules.
How do I compute probation end, tenure, and anniversaries?
Section titled “How do I compute probation end, tenure, and anniversaries?”addMonths clamps to the month end (31 January + 1 month is 28 or 29 February), and dateDiff gives years, months, and days for a tenure label. When the result must be a working day, roll it with adjustBusinessDay.
import { addMonths, dateDiff, differenceInYears, formatPlainDate } from '@absoluteui/helpers/date';import { adjustBusinessDay, createBusinessCalendar, fixedHoliday } from '@absoluteui/helpers/business-calendar';
const joined = '2026-01-31';const probationEnds = addMonths(joined, 6);formatPlainDate(probationEnds); // => '2026-07-31'formatPlainDate(addMonths(joined, 1)); // => '2026-02-28' // clamped to the month end
const cal = createBusinessCalendar({ holidays: [fixedHoliday('Independence Day', 8, 15)] });formatPlainDate(adjustBusinessDay(addMonths('2025-02-15', 6), 'following', cal)); // => '2025-08-18' // 15 Aug 2025 is a Friday holiday, then the weekend
dateDiff('2021-04-12', '2026-10-05'); // => { years: 5, months: 5, days: 23 }differenceInYears('2026-04-11', '2021-04-12'); // => 4 // the fifth anniversary is tomorrowHow do I label and bound fiscal periods?
Section titled “How do I label and bound fiscal periods?”Pass the fiscal start month; years are named by the calendar year in which they end (India’s April 2026 - March 2027 is FY2027) unless you choose naming: 'start'.
import { endOfFiscalYear, fiscalQuarterLabel, fiscalYearLabel, formatPlainDate, startOfFiscalQuarter } from '@absoluteui/helpers/date';
const india = { startMonth: 4 };fiscalYearLabel('2026-10-05', india); // => 'FY2027'fiscalYearLabel('2026-10-05', { ...india, style: 'span' }); // => 'FY2026-27'fiscalQuarterLabel('2026-10-05', india); // => 'Q3'formatPlainDate(startOfFiscalQuarter('2026-10-05', india)); // => '2026-10-01'formatPlainDate(endOfFiscalYear('2026-10-05', { startMonth: 10 })); // => '2027-09-30' // US federalHow do I build a month view that marks weekends and holidays?
Section titled “How do I build a month view that marks weekends and holidays?”calendarMonthGrid gives the rows of a month (with neighbouring days to fill them) starting on the locale’s first weekday. Ask the business calendar about each cell for the weekend, holiday, or half-day marker and an accessible reason.
import { calendarMonthGrid, formatPlainDate, getWeekInfo } from '@absoluteui/helpers/date';import { createBusinessCalendar, describeBusinessDay, fixedHoliday, getBusinessDay } from '@absoluteui/helpers/business-calendar';
const week = getWeekInfo('en-IN');const cal = createBusinessCalendar({ weekend: week.weekend, holidays: [fixedHoliday('Gandhi Jayanti', 10, 2)] });const grid = calendarMonthGrid(2026, 10, { firstDayOfWeek: week.firstDay, fixedWeeks: false });const cells = grid.flat().filter((d) => d.inMonth).map((d) => { const info = getBusinessDay(d.date, cal); return { date: formatPlainDate(d.date), working: info.working, label: describeBusinessDay(info) };});week.weekend; // => [7] // Sunday only in Indiacells[1]; // => { date: '2026-10-02', working: false, label: 'Holiday: Gandhi Jayanti' }cells[2]; // => { date: '2026-10-03', working: true, label: '' } // Saturday is a working day herecells.filter((c) => !c.working).length; // => 5 // four Sundays and one holidayHow do I combine overlapping leave and count the days off?
Section titled “How do I combine overlapping leave and count the days off?”mergeIntervals joins overlapping and back-to-back ranges; intervalLengthDays counts calendar days, while countBusinessDays counts only the working days the employee is actually away.
import { formatPlainDate, intervalGaps, intervalLengthDays, mergeIntervals } from '@absoluteui/helpers/date';import { countBusinessDays, createBusinessCalendar, fixedHoliday } from '@absoluteui/helpers/business-calendar';
const cal = createBusinessCalendar({ holidays: [fixedHoliday('Christmas Day', 12, 25)] });const requests = [ { start: '2026-12-21', end: '2026-12-24' }, { start: '2026-12-24', end: '2026-12-28' }, { start: '2026-12-29', end: '2026-12-31' },];const [away] = mergeIntervals(requests);away === undefined ? undefined : [away.start, away.end].map(formatPlainDate); // => ['2026-12-21', '2026-12-31']away === undefined ? 0 : intervalLengthDays(away); // => 11 // calendar daysaway === undefined ? 0 : countBusinessDays(away.start, away.end, { inclusive: 'both', calendar: cal }); // => 8 // without the weekend and ChristmasintervalGaps(requests, { start: '2026-12-01', end: '2026-12-31' }).length; // => 1 // 1-20 December is still freeHow do I parse and display dates the user types?
Section titled “How do I parse and display dates the user types?”parseDate is strict: the text must match the pattern exactly and be a real date, so 31/02/2026 is rejected instead of rolling into March. Display with formatDateIntl or formatDateRange, which never shift the date by a time zone.
import { formatDateIntl, formatDateRange, formatPlainDate, parseDate } from '@absoluteui/helpers/date';
const typed = parseDate('05/10/2026', 'dd/MM/yyyy');typed === undefined ? undefined : formatPlainDate(typed); // => '2026-10-05'parseDate('31/02/2026', 'dd/MM/yyyy'); // => undefinedformatDateIntl('2026-10-05', { locale: 'de', dateStyle: 'long' }); // => '5. Oktober 2026'formatDateRange('2026-12-21', '2026-12-31', { locale: 'en' }); // => 'Dec 21 – 31, 2026'What goes wrong?
Section titled “What goes wrong?”- Date strings must be
YYYY-MM-DD; anything else throwsHLP_INVALID_DATEinstead of being guessed. today()andisToday()use the runtime’s zone unless you pass one; pass the user’s zone so “today” does not flip at UTC midnight.differenceInDaysandaddDayscount calendar days. A “3-day SLA” usually means business days: usediffDaysoraddBusinessDaysfrom the business calendar.
import { addDays, differenceInDays, formatPlainDate, toPlainDate } from '@absoluteui/helpers/date';import { addBusinessDays } from '@absoluteui/helpers/business-calendar';
toPlainDate('10/05/2026'); // => throws HLP_INVALID_DATEdifferenceInDays('2026-10-12', '2026-10-09'); // => 3 // Friday to MondayformatPlainDate(addDays('2026-10-09', 3)); // => '2026-10-12' // MondayformatPlainDate(addBusinessDays('2026-10-09', 3)); // => '2026-10-14' // WednesdayAPI reference
Section titled “API reference”Calendar
Section titled “Calendar”Functions
Section titled “Functions”addMonths
Adds calendar months, clamping to the target month’s last day (Jan 31 + 1 month = Feb 28/29).
function addMonths(date: DateInput, months: number): PlainDate;Example:
addMonths('2024-01-31', 1) // => '2024-02-29'addQuarters
Adds quarters (3 months each, month-end clamped).
function addQuarters(date: DateInput, quarters: number): PlainDate;Example:
addQuarters('2026-11-30', 1) // => '2027-02-28'addWeeks
Adds weeks (negative subtracts).
function addWeeks(date: DateInput, weeks: number): PlainDate;Example:
addWeeks('2026-01-01', 2) // => '2026-01-15'addYears
Adds years; Feb 29 clamps to Feb 28 in non-leap years.
function addYears(date: DateInput, years: number): PlainDate;Example:
addYears('2024-02-29', 1) // => '2025-02-28'age
Age in whole years on asOf (default: today in the runtime zone). Throws
HLP_DATE_AGE_BEFORE_BIRTH when asOf precedes birthDate.
function age(birthDate: DateInput, asOf?: DateInput, options?: AgeOptions): number;Example:
age('2000-02-29', '2025-02-28') // => 25 // 'mar1' → 24assertIsoWeekday
Validates an ISO weekday option (1 = Monday … 7 = Sunday).
function assertIsoWeekday(value: number, property?: string): asserts value is IsoWeekday;Example:
const weekday: number = 7; // e.g. from a form fieldassertIsoWeekday(weekday, 'weekday'); // narrows to IsoWeekdayweekdayName(weekday, { locale: 'en' }); // => 'Sunday'assertIsoWeekday(0, 'weekday'); // => throws HLP_DATE_INVALID_WEEKDAYclampDate
Clamps date into the closed range [start, end].
function clampDate(date: DateInput, range: { readonly start: DateInput; readonly end: DateInput;}): PlainDate;Example:
clampDate('2026-12-25', { start: '2026-01-01', end: '2026-06-30' }) // => '2026-06-30'closestTo
Candidate closest to date (ties: the first such candidate), or undefined for none.
function closestTo(date: DateInput, candidates: Iterable<DateInput>): PlainDate | undefined;Example:
closestTo('2026-01-10', ['2026-01-01', '2026-01-12']) // => '2026-01-12'dateDiff
Years, months, and days from from to to such that
addDays(addMonths(from, years * 12 + months), days) equals to. When to is earlier,
every field is the negation of dateDiff(to, from).
function dateDiff(from: DateInput, to: DateInput): DateDiff;Example:
dateDiff('2024-01-31', '2024-03-01') // => { years: 0, months: 1, days: 1 }dayOfYear
Day of the year, 1-366.
function dayOfYear(date: DateInput): number;Example:
dayOfYear('2026-02-01') // => 32differenceInDays
Calendar days from earlier to later (negative when later is earlier).
function differenceInDays(later: DateInput, earlier: DateInput): number;Example:
differenceInDays('2026-03-01', '2026-02-01') // => 28differenceInMonths
Complete calendar months, month-end aware (Jan 31 -> Feb 28 is one month).
function differenceInMonths(later: DateInput, earlier: DateInput): number;Example:
differenceInMonths('2026-02-28', '2026-01-31') // => 1differenceInQuarters
Complete quarters (3 months).
function differenceInQuarters(later: DateInput, earlier: DateInput): number;Example:
differenceInQuarters('2026-12-31', '2026-01-01') // => 3differenceInWeeks
Complete weeks (truncated toward zero).
function differenceInWeeks(later: DateInput, earlier: DateInput): number;Example:
differenceInWeeks('2026-01-15', '2026-01-01') // => 2differenceInYears
Complete years (Feb 29 -> Feb 28 of a common year counts as a year, like addYears).
function differenceInYears(later: DateInput, earlier: DateInput): number;Example:
differenceInYears('2026-09-28', '2000-09-29') // => 25endOfFiscalQuarter
Last day of the fiscal quarter containing date.
function endOfFiscalQuarter(date: DateInput, options?: FiscalYearOptions): PlainDate;Example:
endOfFiscalQuarter('2026-09-29', { startMonth: 4 }) // => '2026-09-30'endOfFiscalYear
Last day of the fiscal year containing date.
function endOfFiscalYear(date: DateInput, options?: FiscalYearOptions): PlainDate;Example:
endOfFiscalYear('2026-09-29', { startMonth: 4 }) // => '2027-03-31'endOfMonth
Last day of the month.
function endOfMonth(date: DateInput): PlainDate;Example:
endOfMonth('2024-02-10') // => '2024-02-29'endOfQuarter
Last day of the calendar quarter.
function endOfQuarter(date: DateInput): PlainDate;Example:
endOfQuarter('2026-09-29') // => '2026-09-30'endOfWeek
Last day of the week containing date.
function endOfWeek(date: DateInput, options?: WeekOptions): PlainDate;Example:
endOfWeek('2026-09-30') // => '2026-10-04' // SundayendOfYear
December 31 of the year.
function endOfYear(date: DateInput): PlainDate;Example:
endOfYear('2026-09-29') // => '2026-12-31'fiscalQuarterLabel
Fiscal-quarter label Q1-Q4.
function fiscalQuarterLabel(date: DateInput, options?: FiscalYearOptions): string;Example:
fiscalQuarterLabel('2026-09-29', { startMonth: 4 }) // => 'Q2'fiscalQuarterOf
Fiscal quarter 1-4, counted from the fiscal start month.
function fiscalQuarterOf(date: DateInput, options?: FiscalYearOptions): 1 | 2 | 3 | 4;Example:
fiscalQuarterOf('2026-09-29', { startMonth: 4 }) // => 2fiscalYearLabel
Fiscal-year label such as FY2027 or FY2026-27.
function fiscalYearLabel(date: DateInput, options?: FiscalLabelOptions): string;Example:
fiscalYearLabel('2026-09-29', { startMonth: 4 }) // => 'FY2027'fiscalYearOf
Fiscal year number (see FiscalYearOptions for the naming convention).
function fiscalYearOf(date: DateInput, options?: FiscalYearOptions): number;Example:
fiscalYearOf('2026-09-29', { startMonth: 4 }) // => 2027isAfter
a is strictly after b.
function isAfter(a: DateInput, b: DateInput): boolean;Example:
isAfter('2026-01-02', '2026-01-01') // => trueisBefore
a is strictly before b.
function isBefore(a: DateInput, b: DateInput): boolean;Example:
isBefore('2026-01-01', '2026-01-02') // => trueisBetween
start <= date <= end by default; inclusive controls the endpoints. Throws
HLP_DATE_INVALID_INTERVAL when end is before start.
function isBetween(date: DateInput, start: DateInput, end: DateInput, options?: { readonly inclusive?: Inclusivity;}): boolean;Example:
isBetween('2026-01-31', '2026-01-01', '2026-01-31', { inclusive: 'start' }) // => falseisFuture
date is after today in timeZone.
function isFuture(date: DateInput, timeZone?: string): boolean;Example:
isFuture('2999-01-01') // => trueisoWeek
ISO 8601 week number (1-53).
function isoWeek(date: DateInput): number;Example:
isoWeek('2026-09-29') // => 40isoWeeksInYear
ISO weeks in a year (53 when January 1 is a Thursday, or a Wednesday in a leap year).
function isoWeeksInYear(year: number): number;Example:
isoWeeksInYear(2026) // => 53isoWeekYear
ISO 8601 week-numbering year (differs from the calendar year around January 1).
function isoWeekYear(date: DateInput): number;Example:
isoWeekYear('2027-01-01') // => 2026isPast
date is before today in timeZone.
function isPast(date: DateInput, timeZone?: string): boolean;Example:
isPast('2000-01-01') // => trueisSameDay
Same calendar day.
function isSameDay(a: DateInput, b: DateInput): boolean;Example:
isSameDay('2026-01-01', plainDate(2026, 1, 1)) // => trueisSameMonth
Same month of the same year.
function isSameMonth(a: DateInput, b: DateInput): boolean;Example:
isSameMonth('2026-09-01', '2026-09-30') // => trueisSameQuarter
Same calendar quarter of the same year.
function isSameQuarter(a: DateInput, b: DateInput): boolean;Example:
isSameQuarter('2026-07-01', '2026-09-30') // => trueisSameWeek
Same week (weeks start on firstDayOfWeek, default Monday).
function isSameWeek(a: DateInput, b: DateInput, options?: WeekOptions): boolean;Example:
isSameWeek('2026-09-28', '2026-10-04') // => trueisSameYear
Same calendar year.
function isSameYear(a: DateInput, b: DateInput): boolean;Example:
isSameYear('2026-01-01', '2026-12-31') // => trueisToday
date is today in timeZone (default: runtime zone).
function isToday(date: DateInput, timeZone?: string): boolean;Example:
isToday(today('Asia/Kolkata'), 'Asia/Kolkata') // => truemaxDate
Latest date, or undefined for none.
function maxDate(dates: Iterable<DateInput>): PlainDate | undefined;Example:
maxDate(['2026-02-01', '2026-01-01']) // => '2026-02-01'minDate
Earliest date, or undefined for none.
function minDate(dates: Iterable<DateInput>): PlainDate | undefined;Example:
minDate(['2026-02-01', '2026-01-01']) // => '2026-01-01'quarterOf
Calendar quarter (1-4; Q1 = January to March).
function quarterOf(date: DateInput): 1 | 2 | 3 | 4;Example:
quarterOf('2026-09-29') // => 3startOfFiscalQuarter
First day of the fiscal quarter containing date.
function startOfFiscalQuarter(date: DateInput, options?: FiscalYearOptions): PlainDate;Example:
startOfFiscalQuarter('2026-09-29', { startMonth: 4 }) // => '2026-07-01'startOfFiscalYear
First day of the fiscal year containing date.
function startOfFiscalYear(date: DateInput, options?: FiscalYearOptions): PlainDate;Example:
startOfFiscalYear('2026-09-29', { startMonth: 4 }) // => '2026-04-01'startOfMonth
First day of the month.
function startOfMonth(date: DateInput): PlainDate;Example:
startOfMonth('2026-09-29') // => '2026-09-01'startOfQuarter
First day of the calendar quarter.
function startOfQuarter(date: DateInput): PlainDate;Example:
startOfQuarter('2026-09-29') // => '2026-07-01'startOfWeek
First day of the week containing date.
function startOfWeek(date: DateInput, options?: WeekOptions): PlainDate;Example:
startOfWeek('2026-09-30', { firstDayOfWeek: 7 }) // => '2026-09-27' // SundaystartOfYear
January 1 of the year.
function startOfYear(date: DateInput): PlainDate;Example:
startOfYear('2026-09-29') // => '2026-01-01'subDays
Subtracts days.
function subDays(date: DateInput, days: number): PlainDate;Example:
subDays('2026-03-01', 1) // => '2026-02-28'subMonths
Subtracts months (month-end clamped).
function subMonths(date: DateInput, months: number): PlainDate;Example:
subMonths('2026-03-31', 1) // => '2026-02-28'subQuarters
Subtracts quarters.
function subQuarters(date: DateInput, quarters: number): PlainDate;Example:
subQuarters('2026-05-31', 1) // => '2026-02-28'subWeeks
Subtracts weeks.
function subWeeks(date: DateInput, weeks: number): PlainDate;Example:
subWeeks('2026-01-15', 2) // => '2026-01-01'subYears
Subtracts years.
function subYears(date: DateInput, years: number): PlainDate;Example:
subYears('2028-02-29', 1) // => '2027-02-28'weekOfMonth
1-based row of date in its month’s calendar (weeks start on firstDayOfWeek).
function weekOfMonth(date: DateInput, options?: WeekOptions): number;Example:
weekOfMonth('2026-09-29') // => 5weekOfYear
Week number and week-year under any week rule (default ISO: Monday, 4 minimal days; US: { firstDayOfWeek: 7, minimalDays: 1 }).
function weekOfYear(date: DateInput, options?: WeekNumberingOptions): { readonly week: number; readonly year: number;};Example:
weekOfYear('2027-01-01') // => { week: 53, year: 2026 }weeksInWeekYear
Number of weeks (52 or 53) in a week-year under a week rule.
function weeksInWeekYear(year: number, options?: WeekNumberingOptions): number;Example:
weeksInWeekYear(2026) // => 53Interfaces
Section titled “Interfaces”AgeOptions
interface AgeOptions { readonly leapDayBirthday?: 'feb28' | 'mar1';}DateDiff
Age-style breakdown returned by dateDiff.
interface DateDiff { readonly years: number; readonly months: number; readonly days: number;}FiscalLabelOptions
interface FiscalLabelOptions extends FiscalYearOptions { readonly prefix?: string; readonly style?: 'year' | 'span';}FiscalYearOptions
Fiscal-year options. Naming convention: a fiscal year is labelled by the calendar year in
which it ends (default naming: 'end'; US federal FY2027 = Oct 2026 - Sep 2027, India
April 2026 - March 2027 = FY2027). naming: 'start' labels by the starting year. With
startMonth: 1 both conventions give the calendar year. Quarters are numbered from the
fiscal start: Q1 is the first three months of the fiscal year.
interface FiscalYearOptions { readonly startMonth?: number; readonly naming?: 'end' | 'start';}WeekNumberingOptions
interface WeekNumberingOptions extends WeekOptions { readonly minimalDays?: number;}WeekOptions
interface WeekOptions { readonly firstDayOfWeek?: IsoWeekday;}Inclusivity
Which endpoints count as inside a range.
type Inclusivity = 'both' | 'none' | 'start' | 'end';Format
Section titled “Format”Functions
Section titled “Functions”formatDate
Formats a date or date-time with a pattern (token table at the top of this module).
function formatDate(value: DateInput | PlainDateTime, pattern: string, options?: PatternOptions): string;Example:
formatDate('2026-09-29', "EEE, d MMM yyyy 'Q'Q") // => 'Tue, 29 Sep 2026 Q3'formatDateIntl
Formats a date or wall-clock date-time with Intl.DateTimeFormat (cached). The value is
rendered in UTC internally, so the calendar date never shifts. Default { dateStyle: 'medium' }.
function formatDateIntl(value: DateInput | PlainDateTime, options?: DateIntlOptions): string;Example:
formatDateIntl('2026-09-29', { locale: 'de', dateStyle: 'long' }) // => '29. September 2026'formatDateRange
Formats a range with Intl.DateTimeFormat#formatRange (shared parts collapsed, “Jan 1 - 5, 2026”),
falling back to start - end. Throws HLP_DATE_INVALID_INTERVAL when end < start.
function formatDateRange(start: DateInput | PlainDateTime, end: DateInput | PlainDateTime, options?: DateIntlOptions): string;Example:
formatDateRange('2026-01-01', '2026-01-05', { locale: 'en' }) // => 'Jan 1 – 5, 2026'formatRelative
Describes value relative to base (default now) with the best unit, seconds to years
(months and years use average Gregorian lengths).
function formatRelative(value: InstantInput, base?: InstantInput, options?: RelativeOptions): string;Example:
formatRelative(Date.UTC(2026, 8, 29, 9), Date.UTC(2026, 8, 29, 12), { locale: 'en' }) // => '3 hours ago'Example:
formatRelative(Date.now() - 3 * 3_600_000) // e.g. '3 hours ago' (relative to now, in the runtime locale)formatRelativeDate
Describes a date relative to base (default today in the runtime zone) in calendar units:
days under a week, then weeks, complete months, and complete years.
function formatRelativeDate(date: DateInput, base?: DateInput, options?: RelativeOptions): string;Example:
formatRelativeDate('2026-09-28', '2026-09-29') // => 'yesterday'parseDate
Strictly parses a date with a pattern; time tokens are validated and discarded.
function parseDate(text: string, pattern: string, options?: ParsePatternOptions): PlainDate | undefined;Example:
parseDate('Sep 29, 2026', 'MMM d, yyyy', { locale: 'en' }) // => '2026-09-29'parseDateTime
Strictly parses a date-time with a pattern (same tokens as formatDate). Returns undefined
on any mismatch, impossible value, or inconsistent field (for example a weekday that does not
match the date). The year is required; a missing month or day defaults to 1. h/hh require a.
function parseDateTime(text: string, pattern: string, options?: ParsePatternOptions): PlainDateTime | undefined;Example:
parseDateTime('29/09/2026 14:05', 'dd/MM/yyyy HH:mm') // => '2026-09-29T14:05:00'Interfaces
Section titled “Interfaces”ParsePatternOptions
interface ParsePatternOptions extends PatternOptions { readonly referenceYear?: number;}PatternOptions
interface PatternOptions { readonly locale?: string;}RelativeOptions
interface RelativeOptions { readonly locale?: string; readonly numeric?: 'auto' | 'always'; readonly style?: 'long' | 'short' | 'narrow';}DateIntlOptions
Intl.DateTimeFormat options for wall-clock values (the zone is fixed internally).
type DateIntlOptions = Omit<Intl.DateTimeFormatOptions, 'timeZone' | 'timeZoneName'> & { readonly locale?: string;};Intervals
Section titled “Intervals”Functions
Section titled “Functions”calendarMonthGrid
Weeks (rows of 7 days) covering a month, including adjacent-month days to fill the rows.
function calendarMonthGrid(year: number, month: number, options?: CalendarGridOptions): CalendarDay[][];Example:
const grid = calendarMonthGrid(2026, 9, { firstDayOfWeek: 7 }); // Sunday-firstgrid[0]?.[0]?.date; // => '2026-08-30'grid[0]?.[0]?.inMonth; // => falsegrid.length; // => 6 // fixed height by defaultcalendarMonthGrid(2026, 9, { firstDayOfWeek: 7, fixedWeeks: false }).length; // => 5containsDate
True when date lies inside the closed interval.
function containsDate(interval: DateIntervalInput, date: DateInput): boolean;Example:
containsDate({ start: '2026-01-01', end: '2026-01-31' }, '2026-01-31') // => truedateInterval
Creates a validated, frozen closed interval; throws HLP_DATE_INVALID_INTERVAL when end < start.
function dateInterval(start: DateInput, end: DateInput): DateInterval;Example:
dateInterval('2026-01-01', '2026-01-31')eachDayOfInterval
Lazily yields every step-th day of the interval (both endpoints included).
function eachDayOfInterval(interval: DateIntervalInput, options?: IterateOptions & { readonly step?: number;}): Generator<PlainDate, void, undefined>;Example:
[...eachDayOfInterval({ start: '2026-01-30', end: '2026-02-02' })].length // => 4eachMonthOfInterval
Lazily yields the first day of every month that overlaps the interval.
function eachMonthOfInterval(interval: DateIntervalInput, options?: IterateOptions): Generator<PlainDate, void, undefined>;Example:
[...eachMonthOfInterval({ start: '2026-01-15', end: '2026-03-01' })] // => ['2026-01-01', '2026-02-01', '2026-03-01']eachWeekOfInterval
Lazily yields the first day of every week that overlaps the interval (the first item may precede start).
function eachWeekOfInterval(interval: DateIntervalInput, options?: IterateOptions & WeekOptions): Generator<PlainDate, void, undefined>;Example:
[...eachWeekOfInterval({ start: '2026-09-30', end: '2026-10-12' })] // => ['2026-09-28', '2026-10-05', '2026-10-12'] // MondayseachYearOfInterval
Lazily yields January 1 of every year that overlaps the interval.
function eachYearOfInterval(interval: DateIntervalInput, options?: IterateOptions): Generator<PlainDate, void, undefined>;Example:
[...eachYearOfInterval({ start: '2025-06-01', end: '2026-01-01' })] // => ['2025-01-01', '2026-01-01']intervalGaps
Uncovered sub-intervals of within (days not in any of intervals), ascending.
function intervalGaps(intervals: Iterable<DateIntervalInput>, within: DateIntervalInput): DateInterval[];Example:
const booked = [{ start: '2026-01-03', end: '2026-01-04' }];intervalGaps(booked, { start: '2026-01-01', end: '2026-01-06' }); // => [{ start: '2026-01-01', end: '2026-01-02' }, { start: '2026-01-05', end: '2026-01-06' }]intervalIntersection
Shared days of two intervals, or undefined when they are disjoint.
function intervalIntersection(a: DateIntervalInput, b: DateIntervalInput): DateInterval | undefined;Example:
intervalIntersection({ start: '2026-01-01', end: '2026-01-10' }, { start: '2026-01-05', end: '2026-02-01' }) // => { start: '2026-01-05', end: '2026-01-10' }Example:
intervalIntersection({ start: '2026-01-01', end: '2026-01-04' }, { start: '2026-01-05', end: '2026-01-06' }) // => undefinedintervalLengthDays
Days in the interval, counting both endpoints.
function intervalLengthDays(interval: DateIntervalInput): number;Example:
intervalLengthDays({ start: '2026-01-01', end: '2026-01-31' }) // => 31intervalsOverlap
True when two closed intervals share at least one day. With inclusive: false, intervals that
only touch at an endpoint (a.end === b.start) do not count.
function intervalsOverlap(a: DateIntervalInput, b: DateIntervalInput, options?: { readonly inclusive?: boolean;}): boolean;Example:
intervalsOverlap({ start: '2026-01-01', end: '2026-01-05' }, { start: '2026-01-05', end: '2026-01-09' }) // => truemergeIntervals
Sorts and merges overlapping intervals in O(n log n). Adjacent intervals (one ends the day
before the next starts) merge too unless mergeAdjacent: false.
function mergeIntervals(intervals: Iterable<DateIntervalInput>, options?: { readonly mergeAdjacent?: boolean;}): DateInterval[];Example:
const leave = [{ start: '2026-01-01', end: '2026-01-03' }, { start: '2026-01-04', end: '2026-01-06' }];mergeIntervals(leave); // => [{ start: '2026-01-01', end: '2026-01-06' }]mergeIntervals(leave, { mergeAdjacent: false }).length; // => 2Interfaces
Section titled “Interfaces”CalendarDay
One cell of calendarMonthGrid.
interface CalendarDay { readonly date: PlainDate; readonly inMonth: boolean; readonly isToday: boolean; readonly weekday: IsoWeekday;}CalendarGridOptions
interface CalendarGridOptions extends WeekOptions { readonly fixedWeeks?: boolean; readonly today?: DateInput;}DateInterval
Closed date interval: start and end are both included.
interface DateInterval { readonly start: PlainDate; readonly end: PlainDate;}DateIntervalInput
Interval input: dates as PlainDate or 'YYYY-MM-DD'.
interface DateIntervalInput { readonly start: DateInput; readonly end: DateInput;}IterateOptions
interface IterateOptions { readonly limit?: number;}Locale
Section titled “Locale”Functions
Section titled “Functions”getWeekInfo
Week conventions for a locale (default: the runtime locale): first day, weekend days, and
minimal days of week 1. Uses Intl.Locale#getWeekInfo() (or the older weekInfo accessor)
when present, otherwise an embedded CLDR table. Locales without a region are maximized
(ar -> ar-EG).
function getWeekInfo(locale?: string, options?: GetWeekInfoOptions): WeekInfo;Example:
getWeekInfo('ar-SA') // => { firstDay: 7, weekend: [5, 6], minimalDays: 1 }monthName
Localized month name for month 1-12.
function monthName(month: number, options?: MonthNameOptions): string;Example:
monthName(9, { locale: 'ru', context: 'format' }) // => 'сентября'monthNames
Twelve localized month names (January first).
function monthNames(locale?: string, style?: NameStyle, context?: 'standalone' | 'format'): string[];Example:
monthNames('de', 'short').slice(0, 3) // => ['Jan', 'Feb', 'Mär']Example:
monthNames('en').length // => 12weekdayName
Localized weekday name.
function weekdayName(weekday: IsoWeekday, options?: NameOptions): string;Example:
weekdayName(1, { locale: 'fr', style: 'long' }) // => 'lundi'weekdayNames
Seven localized weekday names starting at firstDay (default 1, Monday).
function weekdayNames(locale?: string, firstDay?: IsoWeekday, style?: NameStyle): string[];Example:
weekdayNames('en-US', 7, 'short') // => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']Interfaces
Section titled “Interfaces”GetWeekInfoOptions
interface GetWeekInfoOptions { readonly useNative?: boolean;}MonthNameOptions
interface MonthNameOptions extends NameOptions { readonly context?: 'standalone' | 'format';}NameOptions
interface NameOptions { readonly locale?: string; readonly style?: NameStyle;}WeekInfo
Locale week conventions (CLDR weekData).
interface WeekInfo { readonly firstDay: IsoWeekday; readonly weekend: readonly IsoWeekday[]; readonly minimalDays: number;}NameStyle
Width of a localized name.
type NameStyle = 'long' | 'short' | 'narrow';Plain date
Section titled “Plain date”Functions
Section titled “Functions”addDays
Adds (or subtracts) days.
function addDays(date: DateInput, days: number): PlainDate;Example:
addDays('2026-12-31', 1) // => '2027-01-01'comparePlainDates
Orders two dates: -1, 0, or 1.
function comparePlainDates(a: DateInput, b: DateInput): -1 | 0 | 1;Example:
['2026-02-01', '2026-01-01'].sort(comparePlainDates)daysBetween
Calendar days from a to b (negative when b is earlier), endpoints exclusive of a.
function daysBetween(a: DateInput, b: DateInput): number;Example:
daysBetween('2026-01-01', '2026-01-31') // => 30daysInMonth
Days in month (1-12) of year; 0 for an invalid month.
function daysInMonth(year: number, month: number): number;Example:
daysInMonth(2024, 2) // => 29daysInYear
365 or 366.
function daysInYear(year: number): number;Example:
daysInYear(2024) // => 366formatPlainDate
ISO 8601 YYYY-MM-DD (extended years as +/-YYYYYY).
function formatPlainDate(date: PlainDate): string;Example:
formatPlainDate(plainDate(2026, 9, 29)) // => '2026-09-29'fromEpochDay
Date for a count of days since 1970-01-01.
function fromEpochDay(epochDay: number): PlainDate;Example:
fromEpochDay(0) // => '1970-01-01'isLeapYear
True for Gregorian leap years.
function isLeapYear(year: number): boolean;Example:
isLeapYear(2024) // => trueisoWeekday
ISO weekday (1 = Monday … 7 = Sunday).
function isoWeekday(date: DateInput): IsoWeekday;Example:
isoWeekday('2026-09-29') // => 2 // TuesdayisPlainDate
Type guard for PlainDate-shaped values (including core’s).
function isPlainDate(value: unknown): value is PlainDate;Example:
isPlainDate(plainDate(2026, 1, 1)) // => trueparsePlainDate
Parses YYYY-MM-DD strictly. Returns undefined for anything else, including
impossible dates (2026-02-30) and date-times.
function parsePlainDate(text: string): PlainDate | undefined;Example:
parsePlainDate('2026-09-29')plainDate
Creates a validated, frozen date-only value.
function plainDate(year: number, month: number, day: number): PlainDate;Example:
plainDate(2026, 2, 28)plainDateFromInstant
The calendar date of an instant (epoch ms or Date) as observed in timeZone.
Throws HLP_TZ_INVALID_ZONE for an unknown zone.
function plainDateFromInstant(instant: number | Date, timeZone?: string): PlainDate;Example:
plainDateFromInstant(Date.UTC(2026, 0, 1, 20), 'Asia/Kolkata') // => '2026-01-02'today
Today’s date in timeZone (default: the runtime’s zone).
function today(timeZone?: string): PlainDate;Example:
today('Asia/Kolkata') // e.g. '2026-09-30' (the current date in Kolkata)toEpochDay
Days since 1970-01-01 for a date.
function toEpochDay(date: PlainDate): number;Example:
toEpochDay(plainDate(1970, 1, 2)) // => 1toPlainDate
Normalizes PlainDate | 'YYYY-MM-DD'; throws HLP_INVALID_DATE for bad strings.
function toPlainDate(input: DateInput): PlainDate;Example:
toPlainDate('2026-09-29')Constants
Section titled “Constants”MAX_YEAR
Supported year range (matches @absoluteui/core).
const MAX_YEAR = 275759;Example:
plainDate(MAX_YEAR, 1, 1)Interfaces
Section titled “Interfaces”PlainDate
interface PlainDate { readonly kind: 'PlainDate'; readonly year: number; readonly month: number; readonly day: number;}DateInput
Anything accepted where a date-only value is expected. Strings must be YYYY-MM-DD.
type DateInput = PlainDate | string;Example:
const d: DateInput = '2026-09-29';IsoWeekday
ISO weekday: 1 = Monday … 7 = Sunday.
type IsoWeekday = 1 | 2 | 3 | 4 | 5 | 6 | 7;Example:
const saturday: IsoWeekday = 6;Plain time
Section titled “Plain time”Functions
Section titled “Functions”comparePlainDateTimes
Orders two date-times: -1, 0, or 1.
function comparePlainDateTimes(a: PlainDateTime, b: PlainDateTime): -1 | 0 | 1;Example:
const a = plainDateTime('2026-10-05', plainTime(9, 0));const b = plainDateTime('2026-10-05', plainTime(17, 30));comparePlainDateTimes(a, b); // => -1comparePlainDateTimes(b, a); // => 1[b, a].sort(comparePlainDateTimes); // => [a, b]comparePlainTimes
Orders two times: -1, 0, or 1.
function comparePlainTimes(a: PlainTime, b: PlainTime): -1 | 0 | 1;Example:
comparePlainTimes(plainTime(9), plainTime(10)) // => -1formatInstant
ISO 8601 UTC string for an instant (Date#toISOString).
function formatInstant(instant: number | Date): string;Example:
formatInstant(0) // => '1970-01-01T00:00:00.000Z'formatPlainDateTime
ISO 8601 YYYY-MM-DDTHH:mm:ss[.SSS].
function formatPlainDateTime(value: PlainDateTime): string;Example:
formatPlainDateTime(plainDateTime('2026-03-08', plainTime(2, 30))) // => '2026-03-08T02:30:00'formatPlainTime
ISO 8601 HH:mm:ss plus .SSS when milliseconds are non-zero.
function formatPlainTime(time: PlainTime): string;Example:
formatPlainTime(plainTime(9, 5)) // => '09:05:00'isPlainDateTime
Type guard for PlainDateTime-shaped values (including core’s).
function isPlainDateTime(value: unknown): value is PlainDateTime;Example:
isPlainDateTime(plainDateTime('2026-01-01')) // => trueisPlainTime
Type guard for PlainTime-shaped values (including core’s).
function isPlainTime(value: unknown): value is PlainTime;Example:
isPlainTime(plainTime(12)) // => trueparseInstant
Parses an ISO 8601 instant (date-time with a mandatory Z or +/-HH:MM) to epoch milliseconds.
Returns undefined for text without an offset or outside the Date range.
function parseInstant(text: string): number | undefined;Example:
parseInstant('2026-03-08T07:00:00Z') // => 1772953200000parseIsoDateTime
Parses YYYY-MM-DDTHH:mm[:ss[.SSS]] with an optional Z or +/-HH:MM suffix. Strict:
uppercase T, no spaces, no impossible dates or times. Returns undefined otherwise.
function parseIsoDateTime(text: string): IsoDateTimeParts | undefined;Example:
parseIsoDateTime('2026-03-08T02:30:00-05:00'); // => { dateTime: '2026-03-08T02:30:00', offset: -18000000 }parseIsoDateTime('2026-03-08T02:30')?.offset; // => undefined // no offset in the textparseIsoDateTime('2026-02-30T10:00Z'); // => undefined // impossible dateparsePlainDateTime
Parses a date-time without an offset (YYYY-MM-DDTHH:mm[:ss[.SSS]]). Text with Z or an
offset is rejected because it denotes an instant, not a wall-clock time.
function parsePlainDateTime(text: string): PlainDateTime | undefined;Example:
parsePlainDateTime('2026-03-08T02:30') // => '2026-03-08T02:30:00'parsePlainTime
Parses HH:mm[:ss[.SSS]] strictly (1-3 fraction digits). Returns undefined otherwise,
including 24:00 and leap seconds.
function parsePlainTime(text: string): PlainTime | undefined;Example:
parsePlainTime('09:30') // => '09:30:00'plainDateTime
Creates a frozen date-time from a date and a time (default midnight).
function plainDateTime(date: DateInput, time?: PlainTime): PlainDateTime;Example:
plainDateTime('2026-03-08', plainTime(2, 30))plainDateTimeToEpochMilliseconds
Wall-clock milliseconds as if the date-time were UTC (“local epoch”). Internal building block for zone conversion; exported for business-calendar arithmetic.
function plainDateTimeToEpochMilliseconds(value: PlainDateTime): number;Example:
plainDateTimeToEpochMilliseconds(plainDateTime('1970-01-02')) // => 86400000plainTime
Creates a validated, frozen wall-clock time.
function plainTime(hour: number, minute?: number, second?: number, millisecond?: number): PlainTime;Example:
plainTime(9, 30) // => '09:30:00'timeFromMilliseconds
Time for milliseconds since midnight (wraps modulo one day, negative values wrap backwards).
function timeFromMilliseconds(ms: number): PlainTime;Example:
timeFromMilliseconds(5_400_000) // => '01:30:00'timeToMilliseconds
Milliseconds since midnight.
function timeToMilliseconds(time: PlainTime): number;Example:
timeToMilliseconds(plainTime(1)) // => 3600000Interfaces
Section titled “Interfaces”IsoDateTimeParts
Parsed ISO 8601 date-time: wall-clock fields plus the offset when one was written.
interface IsoDateTimeParts { readonly dateTime: PlainDateTime; readonly offset: number | undefined;}PlainDateTime
interface PlainDateTime { readonly kind: 'PlainDateTime'; readonly date: PlainDate; readonly time: PlainTime;}PlainTime
interface PlainTime { readonly kind: 'PlainTime'; readonly hour: number; readonly minute: number; readonly second: number; readonly millisecond: number;}Functions
Section titled “Functions”endOfDayInstant
Last millisecond of a calendar day in a zone (one before the next day’s startOfDayInstant).
function endOfDayInstant(date: DateInput, timeZone?: string): number;Example:
endOfDayInstant('2026-11-01', 'America/New_York'); // => Date.UTC(2026, 10, 2, 5) - 1 // 23:59:59.999 ESTendOfDayInstant('2026-11-01', 'America/New_York') - startOfDayInstant('2026-11-01', 'America/New_York'); // => 25 * 3_600_000 - 1 // a 25-hour daystartOfDayInstant
First instant (epoch ms) of a calendar day in a zone. DST-safe: when midnight is skipped
(for example America/Santiago or Asia/Beirut at their spring transitions) it returns the
first instant that exists on that date, as Temporal’s startOfDay does.
function startOfDayInstant(date: DateInput, timeZone?: string): number;Example:
startOfDayInstant('2026-10-05', 'Asia/Kolkata'); // => Date.UTC(2026, 9, 4, 18, 30) // 00:00 ISTstartOfDayInstant('2026-03-29', 'Asia/Beirut'); // => Date.UTC(2026, 2, 28, 22) // midnight is skipped: 01:00 local (+03:00)toPlainDateTime
Wall-clock date-time of an instant in timeZone (alias of the timezone module’s
instantToZoned, named for the date module’s instant conventions).
function toPlainDateTime(instant: InstantInput, timeZone?: string): PlainDateTime;Example:
toPlainDateTime(Date.UTC(2026, 6, 1, 12), 'Europe/London'); // => '2026-07-01T13:00:00' // BSTtoPlainDateTime(Date.UTC(2026, 11, 1, 12), 'Europe/London'); // => '2026-12-01T12:00:00' // GMTInstantInput
An exact instant: epoch milliseconds or a valid Date.
type InstantInput = number | Date;Example:
const i: InstantInput = Date.now();