Quando is a TypeScript library for schedules, rotas, time-based values, and recurring time rules.
It answers questions such as these:
- Is this shop open now?
- When does it open next?
- When will three working hours have elapsed?
- Where is the first two-hour gap in several people's availability?
- Which half-hour booking slots are available?
- Which opening times changed in a revised schedule?
- Does a rota leave any time unassigned?
- Why is this instant open, closed, assigned, or counted this way?
- Who is on call at a given time?
- How many people are working during a period?
Quando uses the standard Temporal API for dates, times, durations, and time
zones.
npm install @kensio/quandoQuando uses a global Temporal. If your runtime does not provide it, install
temporal-polyfill and add import "temporal-polyfill/global" to your entry
point before evaluating rules. See getting started.
TypeScript projects must include ESNext in compilerOptions.lib.
import { schedule, weekdays } from "@kensio/quando";
const openingHours = schedule({ zone: "Europe/London" })
.open(weekdays(), "09:00-17:00")
.closed("2026-12-25")
.setHours("2026-12-24", "09:00-15:00");
const placed = Temporal.ZonedDateTime.from("2026-03-13T16:55[Europe/London]");
openingHours.isOpen(placed);
// true
openingHours.nextOpenInterval(placed.add({ hours: 2 }))?.start?.toString();
// 2026-03-16T09:00:00+00:00[Europe/London]
openingHours
.firstOpenSlot(placed, { minutes: 30 }, { within: { days: 7 } })
?.start?.toString();
// 2026-03-16T09:00:00+00:00[Europe/London]
openingHours.addOpenTime(placed, { hours: 3 })?.toString();
// 2026-03-16T11:55:00+00:00[Europe/London]The first call to open sets the usual hours. Later calls add exceptions.
closed closes Christmas Day, and setHours gives Christmas Eve its own hours.
| API | Use it for |
|---|---|
schedule |
Opening hours and other open or closed periods |
rota |
Assigning names or application values over time |
tally |
Adding numeric values where periods overlap |
| Rules | Composing custom definitions of when |
Schedules, rotas, and tallies provide methods named for their domains. Rules provide the common time model underneath them.
import { rota, tally, weekdays, weekends } from "@kensio/quando";
const onCall = rota()
.assign(weekdays(), "alice")
.assign(weekends(), "bob")
.assign("2026-03-11", "carol");
const staffing = tally().plus(weekdays(), 3).plus("2026-03-11", 2);Quando definitions are JSON-compatible data. Parsers validate stored data and restore the API methods.
import { parseSchedule } from "@kensio/quando";
const stored = JSON.stringify(openingHours);
const restored = parseSchedule(JSON.parse(stored));
restored.isOpen(placed);The installed quando command reads stored definitions. It can return a
timeline, explain one instant, or validate a finite window.
npx quando timeline opening-hours.json \
--from '2026-03-09T00:00[Europe/London]' \
--to '2026-03-10T00:00[Europe/London]'Commands return JSON by default. Pass --format text for terminal output. The
command-line guide lists the accepted documents and options.
Start with the getting started guide. The remaining guides cover:
- Schedules and rotas
- Rules, terms and constraints
- Horizons
- Queries
- Validation
- Explanations
- Command line
- Time zones
- Serialisation
- Cascades and merging
- Comparison and the API reference
- Performance
The same documentation is published at quandojs.dev.
Quando calculates times and intervals. It leaves job execution, persistence, and holiday data to the application.
Constraints that depend on previous occurrences are in scope. atMostOccurrences caps how
many things may happen in a window, minimumGap sets the least time between
them, and atMostOccupiedTime caps the total time they take. All three read against
a history the query carries, and firstBreach checks a whole proposed plan
against them, feeding each occurrence into the history before asking about the
next. See constraints.
How far a rule can be trusted is in scope too. knownThrough declares the last
day a subtree counts as evidence, and a query whose answer would rest on
anything past it refuses. A cascade carries the same thing per value, so
unknownValueIntervals says which stretches its layers cannot settle. See
horizons.
So is an answer with several possible outcomes. "One to three working days" goes into a query as an estimate and comes back as dates, with the weekends and the holidays already applied. Read the possible dates off it for a customer, a quantile for a contract, or the chance of beating a date. See uncertainty.