日本語 · English

Cron: run on the last day of the month — and the last business day

Translated from the canonical Japanese page recipes/cron-last-day-of-month.md.

“Run at the end of every month” is one of cron’s most common stumbling blocks (325k views on Stack Overflow). In Kairos, both variants are one-line expressions:

monthEnd                                  # last day of the month
bizDay |> within(month) |> last           # last business day of the month

What happens with cron

The root cause is not a missing feature but that expressions do not compose — even if you can produce “month-end”, you cannot feed the result into the next rule (“roll backward onto business days”).

Writing it in Kairos

The holiday-aware last business day of the month, under the standard @JP premise (whose bizDay is derived from weekends plus Japan’s holiday data):

# eval: 2026-01-01..2026-07-01
@JP
bizDay |> within(month) |> last
#=> 2026-01-30 2026-02-27 2026-03-31 2026-04-30 2026-05-29 2026-06-30

January (1/31 Sat) and May (5/30 Sat, 5/31 Sun) correctly retreat to Friday. “Build the stream of business days, take the last point of each month” — the structure reads exactly as stated, the holidays come from calendar data (with covering:), and when that data runs out the result carries an annotation instead of silently degrading.

Counting back from month-end composes from the same vocabulary:

monthEnd |> roll(Preceding, on: bizDay) |> shift(-3, unit: bizDay)   # 3 business days before month-end

Try it in your browser

A self-contained form (the holiday table inline) is ready to run in the Playground — move from/to around, add holidays, and watch the behavior.