Merging values
A merge strategy combines values from all matching layers. Use it to add
staffing counts, select minimum or maximum values, or combine lists. The
default override strategy uses the last matching layer’s value.
Use a tally for counts. Use merged when you need direct
access to the low-level strategies.
Count with a tally
Section titled “Count with a tally”A tally adds numeric values that cover the same time:
import { tally, weekdays, weekends } from "@kensio/quando";
const staff = tally() .plus(weekdays(), 3, { label: "Usual crew" }) .plus(weekends(), 1) .plus("2026-03-11", 2, { label: "Delivery cover" });
const wednesday = Temporal.ZonedDateTime.from( "2026-03-11T11:00[Europe/London]",);
console.log(staff.countAt(wednesday));5The tally supplies several common operations:
| Method | Meaning |
|---|---|
plus(scope, amount, options?) |
Add an amount |
setCount(scope, amount, options?) |
Replace lower values within the scope |
countAt(instant) |
Read the amount at one instant |
explain(instant) |
Explain how the matching lines add up |
minimumCount(from, to) |
Find the lowest amount in a window |
totalBetween(from, to, unit) |
Total the amount over elapsed time |
countIntervals(from, to) |
Resolve counts, including zero gaps |
validate(from, to) |
Find inactive and shadowed lines |
countAt, minimumCount, and countIntervals treat unassigned time as zero.
See explanations for the trace returned by
explain. See accumulation for totals such as staff-hours.
Use a merge strategy directly
Section titled “Use a merge strategy directly”merged stores the strategy in a cascade:
| Strategy | Accepted values | Overlap result |
|---|---|---|
override |
Any JSON-compatible value | The later value |
sum |
Numbers | The total |
max |
Numbers | The largest value |
min |
Numbers | The smallest value |
concat |
Arrays | One array in layer order |
This cascade adds two extra staff members on Wednesday:
import { dates, weekdays } from "@kensio/quando";import { layer, merged, resolve } from "@kensio/quando/core";
const staff = merged( "sum", layer(weekdays(), 3), layer(dates("2026-03-11"), 2),);
const week = { from: Temporal.ZonedDateTime.from("2026-03-09T00:00[Europe/London]"), to: Temporal.ZonedDateTime.from("2026-03-16T00:00[Europe/London]"),};
for (const { start, end, value } of resolve(staff, week)) { console.log(`${start?.toPlainDate()} → ${end?.toPlainDate()}: ${value}`);}2026-03-09 → 2026-03-11: 32026-03-11 → 2026-03-12: 52026-03-12 → 2026-03-14: 3Layer order controls the order used by concat:
const onCall = merged( "concat", layer(weekdays(), ["alice"]), layer(dates("2026-03-11"), ["bob"]),);The value on Wednesday is ["alice", "bob"].
Replacement and merging
Section titled “Replacement and merging”A replacement layer removes lower layers from its selected period. The replacement’s result still merges with values from higher layers.
A nested replacement cascade uses its own strategy. This lets an outer
sum cascade contain a replacement that uses max, for example.
Validation
Section titled “Validation”Each strategy requires a compatible value type. TypeScript checks that
sum, max, and min receive numbers and that concat receives arrays.
Runtime validation applies the same rules to raw layers and parsed documents. Invalid values fail when the cascade is constructed or parsed:
import { parseString, parseCascade } from "@kensio/quando/parsing";
parseCascade( { type: "cascade", merge: "sum", layers: [{ scope: { type: "always" }, value: "alice" }], }, parseString,);TypeError: cascade.layers[0].value: sum needs numbers.Strategy names are part of the stored JSON document. parseCascade rejects
unknown names.