Guides
Events
An event is ISO-8601 strings: what start, end, duration and allDay mean, and how a time zone is read.
The shape
An event is described with ISO-8601 strings and nothing else — no Date objects, no library types. Your own payload rides along under data, typed, and comes back untouched on every box and bar.
Type
type EventInput<TData = unknown> = { id: string start: string end?: string duration?: string allDay?: boolean timeZone?: string recurrence?: RecurrenceInput<TData> data?: TData }
Events
import { buildCalendar } from '@midstem/chronous' import type { CalendarRange, EventInput } from '@midstem/chronous' type EventData = { title: string } const range: CalendarRange = { view: 'day', currentDate: '2026-03-18', timeZone: 'Europe/Kyiv', } const events: EventInput<EventData>[] = [ { id: 'standup', start: '2026-03-18T09:00', duration: 'PT30M', data: { title: 'Standup' }, }, { id: 'review', start: '2026-03-18T14:00', end: '2026-03-18T15:30', data: { title: 'Design review' }, }, ] const calendar = buildCalendar(range, events) console.log(calendar.days[0].boxes.map(box => box.event.data?.title)) // [ 'Standup', 'Design review' ]
How long an event is
endwins overduration; with neither, a timed event has zero length.durationis an ISO-8601 duration. Date units are calendar arithmetic —P1Dkeeps the wall clock across a DST transition — while clock units are exact, soPT4His always four real hours.- An event that ends before it starts throws
InvalidEventError, carrying the offendingeventId.
typescript
import { buildCalendar } from '@midstem/chronous' import type { EventInput } from '@midstem/chronous' const events: EventInput[] = [ // clock units are exact — always four real hours { id: 'workshop', start: '2026-03-29T00:30', duration: 'PT4H' }, // date units are calendar arithmetic — the wall clock survives the change { id: 'sprint', start: '2026-03-29T00:30', duration: 'P1D' }, ] // 29 March 2026 is the day Kyiv loses an hour const calendar = buildCalendar( { view: 'day', currentDate: '2026-03-29', timeZone: 'Europe/Kyiv' }, events, ) console.log(calendar.days[0].boxes.map(box => [box.event.id, box.end])) // [ [ 'workshop', '2026-03-29T05:30:00+03:00' ] ] // 00:30 + four real hours is 05:30 on the wall clock, because 03:00 never // happened. 'sprint' keeps its 00:30 and became a 23-hour bar instead.
All-day events
An event is all-day when allDay says so, or when its dates carry no time. All-day events are plain dates with no zone attached, and their end is exclusive.
typescript
import { buildCalendar } from '@midstem/chronous' import type { EventInput } from '@midstem/chronous' const events: EventInput[] = [ // three days: 16, 17 and 18 March — end is exclusive { id: 'offsite', start: '2026-03-16', end: '2026-03-19' }, // a single day { id: 'holiday', start: '2026-03-20', end: '2026-03-20' }, // a timed event pushed into the all-day lanes anyway { id: 'launch', start: '2026-03-17T09:00', allDay: true }, ] const calendar = buildCalendar( { view: 'week', currentDate: '2026-03-18', timeZone: 'Europe/Kyiv' }, events, ) console.log(calendar.rows[0].bars.map(bar => [bar.event.id, bar.dayCount])) // [ [ 'offsite', 3 ], [ 'launch', 1 ], [ 'holiday', 1 ] ] // Bars read across the row longest first, which is why 'launch' on the 17th // comes before 'holiday' on the 20th.
Every all-day event is drawn as a bar above the grid rather than inside it — Layout & lanes covers how those bars are packed.
Time zones
Times are read in the calendar’s time zone unless the event carries its own — that is how a Kyiv office schedule stays correct when it is opened from Berlin.
typescript
import { buildCalendar } from '@midstem/chronous' import type { CalendarRange, EventInput } from '@midstem/chronous' const range: CalendarRange = { view: 'day', currentDate: '2026-03-18', timeZone: 'Europe/Kyiv', } const events: EventInput[] = [ // read in Europe/Kyiv, the calendar's own zone { id: 'standup', start: '2026-03-18T09:00', duration: 'PT30M' }, // read in Tokyo, and drawn where that lands in Kyiv { id: 'sync', start: '2026-03-18T09:00', duration: 'PT30M', timeZone: 'Asia/Tokyo', }, ] const calendar = buildCalendar(range, events) console.log(calendar.days[0].boxes.map(box => [box.event.id, box.startMinute])) // [ [ 'sync', 120 ], [ 'standup', 540 ] ] — 02:00 and 09:00 Kyiv time