Skip to content

feature: calendar arithmetic builtins#495

Merged
danieljohnmorris merged 7 commits into
mainfrom
feature/calendar-arithmetic
May 21, 2026
Merged

feature: calendar arithmetic builtins#495
danieljohnmorris merged 7 commits into
mainfrom
feature/calendar-arithmetic

Conversation

@danieljohnmorris
Copy link
Copy Markdown
Collaborator

Summary

  • Adds four tree-bridge-eligible date arithmetic builtins: add-mo, last-dom, next-business-day, day-of-week
  • All take Unix epoch seconds, return epoch seconds at 00:00 UTC
  • Pure (no FnRef, no I/O): VM and Cranelift inherit via tree-bridge without new opcodes

Builtins

builtin signature behaviour
add-mo dt n n n > n add N calendar months, snapping end-of-month (Jan 31 + 1 = Feb 28/29)
last-dom dt n > n epoch of last day of month containing dt at 00:00 UTC
next-business-day dt n > n next weekday: Fri/Sat/Sun all advance to Mon
day-of-week dt n > n 0=Sun, 1=Mon, 2=Tue, 3=Wed, 4=Thu, 5=Fri, 6=Sat

What's in the diff

  1. src/builtins.rs - four new variants in Builtin enum, from_name, name(), ALL
  2. src/interpreter/mod.rs - implementation of all four builtins
  3. src/verify.rs - arity/type signatures in the BUILTINS table
  4. src/vm/mod.rs - is_tree_bridge_eligible entries (all four bridge through)
  5. tests/regression_calendar_arithmetic.rs - 19 cross-engine tests
  6. examples/calendar-arithmetic.ilo - runnable example with -- out: assertion
  7. SPEC.md, ai.txt, skills/ilo/SKILL.md - doc sync

Test plan

  • cargo check clean
  • cargo fmt applied
  • 19 regression tests all pass (VM + JIT): leap year snap, non-leap snap, negative months, last-dom Dec, next-business-day Fri/Sat/Sun/Mon, day-of-week all days + epoch zero
  • Full suite: cargo test --release --features cranelift

Repro before/after

Before: no calendar month arithmetic builtins - agents had to hand-roll month addition using dtparse/dtfmt roundtrips.

After:

add-mo 1706659200 1        -- 1709164800 (Jan 31 2024 -> Feb 29 2024, leap snap)
last-dom 1707955200        -- 1709164800 (Feb 15 2024 -> Feb 29 2024)
next-business-day 1705622400  -- 1705881600 (Fri Jan 19 -> Mon Jan 22)
day-of-week 1705276800     -- 1 (Mon Jan 15 2024)

@codecov
Copy link
Copy Markdown

codecov Bot commented May 20, 2026

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
4922 1 4921 0
View the top 1 failed test(s) by shortest run time
ilo::regression_calendar_arithmetic::add_mo_result_out_of_calendar_range
Stack Traces | 0.012s run time
thread 'main' (43488) panicked at src/interpreter/mod.rs:1891:21:
attempt to add with overflow
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@danieljohnmorris danieljohnmorris force-pushed the feature/calendar-arithmetic branch 9 times, most recently from f1d3112 to 8c3616b Compare May 21, 2026 13:46
…, day-of-week

Four new tree-bridge-eligible builtins for month-level and business-day
date arithmetic. All take Unix epoch seconds and return epoch seconds at
00:00 UTC. End-of-month snap in add-mo (Jan 31 + 1 = Feb 28/29 depending
on leap year). day-of-week uses 0=Sun..6=Sat (JS/POSIX convention).
19 cross-engine tests covering: leap year snap (Jan 31 + 1 = Feb 29 2024,
non-leap Feb 28 2025), negative months, last-dom for Feb leap/non-leap and
Dec, next-business-day for Fri/Sat/Sun/Mon, day-of-week for all edge cases.
Add table rows and narrative section to SPEC.md, inline entries to ai.txt,
and update skills/ilo/SKILL.md to list the new builtins under the datetime
section.
Without this, Cranelift swallowed the ILO-R009 errors raised by add-mo /
last-dom / next-business-day / day-of-week on out-of-range epochs as
nil, diverging from tree/VM which both raise. Adds the four to
tree_bridge_propagates_error so all three engines render the same
runtime error.
Adds cross-engine regression coverage for the previously-uncovered
branches in the calendar arithmetic builtins:

- day-of-week: Tue/Wed/Thu match arms
- next-business-day: midweek (Tue->Wed, Thu->Fri) via the catch-all
- add-mo: m==12 / y+1 in add_months_snap (Nov->Dec, Dec->Jan next year),
  negative cross-year (Jan->Dec prev year), -12 stride, +13 with snap
  (Jan 29 2024 + 13 mo = Feb 28 2025)
- error paths for all four builtins:
  - add-mo: epoch-out-of-range and result-out-of-calendar-range
  - last-dom / next-business-day / day-of-week: epoch out of range

Out-of-range epochs are reached via numeric literals beyond f64-as-i64
saturation so chrono's timestamp_opt returns None. The matching
tree_bridge_propagates_error entry (preceding commit) keeps Cranelift
in lockstep with tree/VM error rendering for these tests.
Same pattern as the recent lstsq (#515) and URL-encoding extractions:
inlining four chrono-heavy builtin arms into call_function bloated the
debug-build dispatch frame past the default 2 MiB Linux pthread stack,
tripping interpret_braceless_guard_fibonacci in CI on nextest.

Move add-mo, last-dom, next-business-day, day-of-week into their own
lines (return helper(...)). Behaviour unchanged; the regression suite
still exercises every branch on both --vm and --jit.
@danieljohnmorris danieljohnmorris force-pushed the feature/calendar-arithmetic branch from 8c3616b to 6b7a23b Compare May 21, 2026 13:54
@danieljohnmorris danieljohnmorris merged commit bc34cdb into main May 21, 2026
1 of 4 checks passed
@danieljohnmorris danieljohnmorris deleted the feature/calendar-arithmetic branch May 21, 2026 14:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant