ChronousDocumentation

API

buildCalendar

The engine’s front door: a range and events in, one plain JSON layout out.

Signature

buildCalendar(range, events) is the engine’s front door: a range and a list of events in, one plain object out. It is exported from both packages and needs no framework.

typescript
import { buildCalendar } from '@midstem/chronous'
import type { CalendarRange, EventInput } from '@midstem/chronous'

const range: CalendarRange = {
  view: 'week',
  currentDate: '2026-03-18',
  timeZone: 'Europe/Kyiv',
}

const events: EventInput[] = [
  { id: 'standup', start: '2026-03-18T09:00', duration: 'PT30M' },
]

const calendar = buildCalendar(range, events)

console.log(calendar.view) // 'week'
console.log(calendar.days.length) // 7
console.log(calendar.rows.length) // 1
console.log(calendar.days[2].boxes[0].startMinute) // 540

What comes back

Type
type CalendarLayout<TData = unknown> = {
  view: ViewKind
  start: IsoDateTime
  end: IsoDateTime
  days: CalendarDay<TData>[]
  rows: CalendarRow<TData>[]
}

type CalendarDay<TData = unknown> = {
  date: IsoDate
  start: IsoDateTime
  end: IsoDateTime
  minutes: number
  inCurrentPeriod: boolean
  slots: CalendarSlot[]
  boxes: CalendarBox<TData>[]
}

type CalendarRow<TData = unknown> = {
  start: IsoDate
  end: IsoDate
  dayCount: number
  lanes: number
  bars: CalendarBar<TData>[]
}
  • days are the days the grid draws, rows the bands of bars above it.
  • Every box and bar carries the normalized event as event, discriminated by allDay: a timed entry holds date-times, an all-day entry plain dates.
  • The range’s timeZone and disambiguation are the ones the events are read with, so a calendar is built from one consistent point of view.

Layout & lanes has the full shape of a box and of a bar.

It is plain JSON

Everything crossing the boundary is a string or a number. Temporal types stay inside the engine, so a calendar survives JSON.stringify, a server-to-client payload and a React state update unchanged.

typescript
import { buildCalendar } from '@midstem/chronous'

const calendar = buildCalendar(
  { view: 'day', currentDate: '2026-03-18', timeZone: 'Europe/Kyiv' },
  [{ id: 'standup', start: '2026-03-18T09:00', duration: 'PT30M' }],
)

// It survives a round trip: a server payload, a state update, a cache
console.log(JSON.parse(JSON.stringify(calendar)).days[0].date)
// '2026-03-18'

// A moment carries its offset, so this is exact without knowing the zone
console.log(new Date(calendar.days[0].boxes[0].start).toISOString())
// '2026-03-18T07:00:00.000Z'

// A date has no time and is never moved into a zone
console.log(calendar.days[0].date)
// '2026-03-18'