Skip to content

Explain a result

An explanation shows the result at one instant and how the rules produced it. It includes contributing layers and layers that were skipped.

Read summary for a single result sentence or details for the full text. Use steps and skipped when building your own display from structured data. Schedule, rota, and tally explanations throw BeyondHorizonError if the value is unknown.

Call explain with the same instant you would pass to isOpen:

import { schedule, weekdays } from "@kensio/quando";
const openingHours = schedule()
.open(weekdays(), "09:00-17:00")
.closed("2026-12-25");
const christmasMorning = Temporal.ZonedDateTime.from(
"2026-12-25T10:00[Europe/London]",
);
const explanation = openingHours.explain(christmasMorning);
console.log(explanation.details);
The schedule is closed on 2026-12-25 at 10:00 in Europe/London. Every condition matches. Friday is a weekday. 10:00 falls within the 09:00-17:00 window. This layer makes the schedule open. The date is 2026-12-25. This higher-priority layer changes the schedule from open to closed.

Quando produces this text from the rules, their priority, and the instant being explained. It describes day-of-week, date, time-of-day, zone, calendar, set operations, replacement, and merge behaviour without caller-written text.

Explanations use the rule’s effective zone and calendar. For example, daysOfMonth(1) inside inCalendar("hebrew", ...) describes the Hebrew day of the month and identifies the calendar.

For a schedule, explanation.value matches isOpen and is true or false. If no layer matches, the schedule is closed. The details explain why each candidate layer was skipped.

Quando can explain that a date matched. Your application knows that the date is Christmas Day. Add a label, a comment, or both when you add the layer:

const openingHours = schedule()
.open(weekdays(), "09:00-17:00", {
label: "Regular office hours",
})
.closed("2026-12-25", {
label: "Christmas Day",
comment: "The office is closed for the public holiday.",
});

The label and comment appear in summary and on the corresponding step. They are stored in the schedule document and survive JSON.stringify, parsing, and later explanation.

The same final options object works with rota and tally methods:

import { rota, tally, weekdays } from "@kensio/quando";
const onCall = rota().assign(weekdays(), "alice", {
label: "Primary possibleValues",
comment: "Alice handles weekday incidents.",
});
const staffing = tally().plus("2026-03-11", 2, {
label: "Delivery cover",
});

For an all-day opening, the options object can take the place of the hours:

schedule().open(weekdays(), { label: "Twenty-four-hour weekday service" });

Each matching step has its readable description and a structured match:

const final = explanation.steps.at(-1);
console.log(final?.description);
// The date is 2026-12-25. This higher-priority layer changes the schedule from open to closed.
console.log(final?.match);
// {
// status: "matched",
// description: "The date is 2026-12-25.",
// conditions: [],
// rule: { type: "dates", dates: ["2026-12-25"] }
// }

Compound rules have one nested condition for each part. An any explanation shows which alternatives matched. A not explanation shows whether the excluded condition matched. Applications can render these fields in another language or in a different layout.

Assignment steps also contain value and the running result. Tally steps say how much each layer adds and show the running total. Replacement steps contain the nested explanation and say that lower-priority layers were removed.

explanation.skipped accounts for layers that did not contribute. Its reason is "did-not-match" when the rule failed or "replaced" when a matching higher-priority replacement removed it. Each skipped layer has the same automatic rule explanation, label, comment, and diagnostic path as a matching step.

The path field remains available for diagnostics and source mapping. It is not the user-facing explanation.

The core entry point exposes both levels directly:

import { cascade, explain, explainRule, layer } from "@kensio/quando/core";
const weekdayMatch = explainRule(weekdays(), christmasMorning);
const onCall = cascade(
layer(weekdays(), "alice", { label: "Primary possibleValues" }),
);
const assignment = explain(onCall, christmasMorning);

Standalone cascade explanations use undefined when no value is assigned. They include the effective merge strategy and describe how each matching value changes the running result.

An explanation evaluates one instant. Use resolve for every value in a window and validation for inactive or shadowed layers.