Schedules and calendars
Every model has one master timeline; every stream occurrence lands on it.
The timeline
time calendar monthly from 2026-01 for 72Add a projection tail when a valuation needs periods beyond the hold (e.g. exit on forward NOI):
time calendar monthly from 2026-01 for 120 project 12Phases name sub-ranges for organization and scoping:
phase lease_up from 2026-01 to 2027-06The calendar is an assumption when events are involved
An event fires in the first period where its condition holds, and never between periods. So the calendar sets how precisely a condition can be met — which matters as soon as an event decides who gets paid.
An annual grid asks a covenant test, a flip test or a cash-trap test twelve times less often than a monthly one, and each miss delays the consequence to the next year end. On the tax-equity flip the same deal flips ten months later on an annual calendar than a monthly one, moving about $3.5m.
Choose the grid the test is actually performed on, not the grid the reporting happens to use.
Schedule patterns
One-time:
schedule on 2026-06Recurring:
schedule every month from 2026-01 to 2026-12Day rules:
schedule every month on day 15 from 2026-01 to 2026-12
schedule every month on eom from 2026-01 to 2026-12YYYY-MM dates normalize to first-of-month; from must be ≤ to, and
occurrences must land inside the model timeline (E2103). A schedule finer
than the timeline, such as every week on a monthly calendar, is refused
(E2108): model at the finest grain at which anything varies.
Placement in the period — start, mid or end — says where a payment
sits and so how far it is discounted. A recurrence defaults to end, a
one-time date to start, and a schedule states at most one (E2109):
schedule every year start from 2026-01 to 2030-01
schedule every year mid from 2026-01 to 2030-01
schedule on 2029-06 endBusiness-day conventions roll a date that falls on a weekend or holiday:
following, preceding, modified_following, modified_preceding and
none, against a named calendar:
schedule on 2026-07-04 convention following calendar "us"Exceptions stay in one schedule: except drops dates and also adds them:
schedule every day from 2026-07-01 to 2026-07-05 except [2026-07-03] also [2026-07-08]A partial first or last period is not inferred: state it as its own schedule, a one-time payment beside the regular pattern.
Phase-anchored, so the schedule moves when the phase does:
schedule on phase_enter("operations")
schedule every quarter from phase_start("hold") to phase_end("hold")State-anchored (the third anchor, beside dates and phases):
schedule every month from state_enter(asset.site, building) for 18 periodsEach entry of the entity into the state opens its own window of n grid periods; a re-entered state re-anchors with a fresh window. This is what "eighteen months of construction from whenever construction starts" needs — the machine enters the state whenever its edge fires, and the schedule hangs off the entry. See lifecycles and state.
A contract's timing
A pack contract states when its obligations run with term, as dates
(term 2026-01..2030-12) or anchored on a state entry
(term from state_enter(party.acme, renewed) for 60 periods), and how its
lowered lines fall in their period with payment: a lag, payment net 45
or payment net 2 months, or a placement, payment start. A clause may name
one line by its role, payment rent start, and then governs that line
alone. The pack's rule decides how often the agreement pays.
Day counts
Accrual-style math uses year_frac(d1, d2, basis) in expressions, with
"30/360" (US/bond), "act/360", "act/365", and actual/actual bases —
decimal-exact. Date helpers: eomonth, edate, parse_date,
months_between.