deniz.in

Markets

Weather

Loading weather

· via dev.to (home feed)

Kairos 1.0 ships a schedule DSL where '3 business days before month-end' is one expression

Kairos 1.0 freezes a small DSL whose expressions compose business-calendar rules that cron, Quartz and RRULE cannot state, and ships a TypeScript reference CLI that enumerates upcoming and missed schedule points.

Kairos 1.0 ships a schedule DSL where '3 business days before month-end' is one expression

The developer behind Kairos has tagged version 1.0 of the language: a small DSL for writing schedules in which rules such as "3 business days before month-end", or payday on the 25th moved back when it lands on a weekend or holiday, are single expressions rather than scripts wrapped around cron entries. According to the announcement on dev.to, 1.0 freezes the language's semantics, operator family, EBNF grammar and vocabulary, so definitions that parse today keep their meaning.

Composable streams, not more features

The author's diagnosis is that existing schedulers lack composability rather than features. Cron can name the 25th but cannot roll it onto business days; Quartz offers L and LW tokens, but they do not combine and know nothing of holidays; RRULE was designed as an interchange format, not an expression language. Kairos is built on closure: every expression maps a stream of instants to another stream of instants, so derived streams — observed holidays, fiscal periods, lunisolar months — feed further definitions.

Calendars are user-defined. The announcement notes that the Gregorian calendar is itself a stdlib definition written in Kairos, and that 4-4-5 fiscal calendars, trading calendars and the Japanese lunisolar calendar are ordinary definitions rather than built-ins. Derivation rules, such as Japan's substitute-holiday law, live in the definition instead of pre-expanded tables: because July 4, 2026 falls on a Saturday, the expression derives the July 3 observance with no lookup of observed dates.

Five crontab entries, translated

A companion migration post on dev.to is blunt about fit: plain fixed-time repeats should stay on cron. The language earns its keep when "end of month" or "business day" will not fit, when a job drifts an hour after a server move, or when nobody can reconstruct what was skipped during an outage. Working from a declared premise of 2026 US federal holidays, it translates five common entries:

  • Weekdays at 9 becomes bizDay |> at(T09:00) — the timezone lives in the definition, and Labor Day is skipped.
  • Month-end at 23:55 becomes monthEnd |> at(T23:55), replacing the cron workaround of waking on the 28th through 31st and asking a script whether tomorrow is the first.
  • Payday on the 25th, backed onto business days, becomes everyDay |> within(month) |> nth(25) |> roll(Preceding, on: bizDay). In 2026, October 25 is a Sunday, so the expression yields the 23rd; December 25 is Christmas, so it yields the 24th.
  • Last business day of the month is bizDay |> within(month) |> last.
  • Three business days before month-end is:

monthEnd |> roll(Preceding, on: bizDay) |> shift(-3, unit: bizDay)

In November 2026 the count skips Thanksgiving and lands on the 24th. The post distinguishes roll, a conditional move that acts only when a point is off the target axis, from shift, which counts along an axis.

The CLI stops at deciding when

Kairos deliberately answers only the "when"; firing, retrying and logging stay with the runner, and that split is part of the spec. The CLI covers the questions cron cannot: kairos next lists upcoming points, and kairos list --from … --to … enumerates a half-open window, so a machine that was down October 20–31 reports the missed October 23 payroll run as a plain list. Two wiring patterns are suggested: keep one crontab line that checks whether a point falls today and runs the job if so, or fetch the next point as JSON and hand it to systemd-run, at(1) or Windows schtasks, letting the job re-register each following point.

Evaluation is a pure function over a set of instants, making missed fires enumerable and audits reproducible. The announcement says the semantics survived a summer of production use by an independent implementation, with predictions stable across 10 hostnames and a local-to-VPS migration. When calendar data runs out, results carry machine-readable covering and freshness annotations instead of failing quietly, and mistakes such as mixing civil-day and elapsed-hour widths or leaving a timezone undeclared surface as static errors with fix-it guidance.

Status and caveats

The reference implementation is described as a prototype: TypeScript, zero runtime dependencies, 638 tests including doctests, with every documentation example executed in CI. Documentation is canonical in Japanese, with a full English mirror of the spec, operator reference and stdlib guides; evaluator error messages are currently Japanese, while the CLI's -- output is language-neutral. The project is Apache-2.0 and installs via npm (kairos-lang), with a browser playground that runs entirely client-side.

Why it matters

Business-calendar logic is where scheduling escapes the standard toolkit, and it usually ends up as wrapper scripts and holiday tables bolted onto cron — legible to whoever wrote them and nobody else. A frozen, composable specification turns those rules into reviewable expressions with reproducible evaluation, which is what payroll, settlement and batch pipelines need when someone asks what ran, what was missed, and why. The scoping is honest — cron stays the clock, Kairos only decides — and the caveats (a prototype implementation, Japanese-first docs, a young ecosystem) are plainly stated. For teams whose crontabs have accumulated calendar hacks, that makes it a low-risk thing to try.

  • #scheduling
  • #dsl
  • #cron
  • #open-source
  • #typescript

Related posts