Schedule occurrences: `dre schedule ls`
dre schedule ls works out when a project’s schedules fire within a time window. Each firing is
an occurrence, and it comes with the exact command and environment that runs it, so an
orchestrator, a scheduler table or a cron job needs to know nothing about cron, rrule or DST.
The command is pure: it reads the project and nothing else. It needs no profiles, plugins or
network, and writes nothing, so it runs in CI right after dre validate. Keeping the calendar,
noticing changes, dispatching runs and tracking them is up to you (see the
orchestration recipe).
dre schedule ls # the next 5 firings of each scheduledre schedule ls --schedule close_monthly --limit 12dre schedule ls -s sales_summary # schedules that run this reportdre schedule ls --from 2026-09-01 --to 2026-10-01 --output jsonTIME SCHEDULE REPORTS2026-10-01 06:00 AEST close_monthly sales_summary/client_a, sales_summary/client_b2026-10-01 07:00 AEST flash_daily sales_summary/client_a| Option | |
|---|---|
--schedule <name> |
Only this schedule; repeat it for several. |
-s <selector> |
Only schedules that run one of the selected reports (report names, tag:, folders, as on dre run). |
--from <when> |
Start of the window: a date (2026-09-01, meaning 00:00 UTC) or an RFC 3339 date-time. Default: now, to the minute. It may be in the past, to plan a backfill. |
--to <when> |
End of the window, exclusive. Default: 35 days after --from, so a weekly refresh overlaps the last one. At most 366 days after --from. |
--limit <n> |
At most this many firings per schedule. Text output defaults to 5; JSON lists every firing in the window. |
--split |
One occurrence per report and Set instead of one per firing (see below). |
--output text|json |
A table for people (problems go to stderr), or the JSON document below. |
The project has to load without errors (other than missing profiles): a schedule with an error
would be missing from the list, which a consumer would read as removed. Run dre validate first.
When a schedule fires
Section titled “When a schedule fires”- Timezone. A schedule fires in its own
timezone:(or its timing’s), else the project’stimezone:, else UTC. A report’s timezone and--timezone/DRE_TIMEZONEnever move a firing.dre validatewarns when a schedule fires in one timezone and a report it runs renders in another. - DST. A time that doesn’t exist on the day the clocks go forward fires at the first instant after the gap (02:30 becomes 03:00). A time that happens twice when they go back fires once, the first time. Two times that land on the same instant are one firing.
- Run date. Each occurrence’s
run_dateis the firing’s date in its timezone: “06:00 on the 1st, Sydney” is the run for the 1st, though it’s still the 31st in UTC.
The schedules guide’s What’s supported lists every cron field, rule part and option.
The JSON document
Section titled “The JSON document”{ "dre_schedule_version": 1, "dre_version": "0.1.2", "project": "acme_reports", "window": {"from": "2026-09-30T00:00:00Z", "to": "2026-10-01T00:00:00Z"}, "split": false, "complete": true, "project_hash": "4f0c…", "schedules": { "close_monthly": { "definition_hash": "9a1e…", "enabled": true, "timing": null, "timezone": "Australia/Sydney", "vars": {"period": "month"}, "bindings": [ {"report": "sales_summary", "set": "client_a", "binding": "client_a"}, {"report": "sales_summary", "set": "client_b", "binding": "client_b"} ] } }, "occurrences": [ { "key": "close_monthly/2026-09-30T20:00:00Z", "schedule": "close_monthly", "timing": null, "fires_at": "2026-09-30T20:00:00Z", "fires_at_local": "2026-10-01T06:00:00+10:00", "timezone": "Australia/Sydney", "run_date": "2026-10-01", "bindings": [ {"report": "sales_summary", "set": "client_a", "binding": "client_a"}, {"report": "sales_summary", "set": "client_b", "binding": "client_b"} ], "vars": {"period": "month"}, "invocation": { "argv": ["dre", "run", "--schedule", "close_monthly"], "env": {"DRE_RUN_AT": "2026-09-30T20:00:00Z", "DRE_RUN_DATE": "2026-10-01"} } } ], "problems": []}| Field | |
|---|---|
dre_schedule_version |
The format’s version, 1. |
window |
The firings listed: from from (inclusive) to to (exclusive), in UTC. |
split |
Whether occurrences are per firing (false) or per report and Set (--split). |
complete |
true when every schedule is listed (no --schedule or -s). Only then does a missing schedule mean it was removed. |
project_hash |
One hash over every schedule in the project, whatever you asked for. Unchanged means no schedule changed: a refresh can stop there. |
schedules |
The schedules you asked for, by name, including paused ones and ones under problems. |
schedules.*.definition_hash |
Over the schedule’s resolved timing (named or inline), firing timezone, vars, enabled and the Bindings it runs. When it changes, replace that schedule’s future occurrences. |
schedules.*.enabled |
false for a paused schedule (enabled: false), which has no occurrences. |
schedules.*.timing |
The shared timing it uses, or null. |
occurrences |
In firing order, then by key. |
occurrences.*.key |
The natural key: <schedule>/<fires_at>, plus /<report>/<binding> with --split. Upsert on it. |
occurrences.*.fires_at, fires_at_local |
The instant in UTC, and in the firing timezone with its offset. |
occurrences.*.run_date |
The date the run renders as run.date. |
occurrences.*.invocation |
The command that runs it, from the project directory: argv (no shell, no quoting) and env. DRE_RUN_AT pins run.now and run.scheduled_at; DRE_RUN_DATE pins run.date. It never names a target, profile or credentials: your deployment adds those (--target prod, its own environment). |
problems |
Schedules that can’t be expanded, each with schedule, code and message. They have no occurrences. |
Identity and change
Section titled “Identity and change”- A schedule is identified by its name, the same name
dre run --scheduletakes. A rename is a removal plus an addition. - A schedule missing from a
completelisting has been removed or renamed: retire its future occurrences. - Hashes are opaque. The only promise is that the same definition gives the same hash within a format version.
Split occurrences
Section titled “Split occurrences”With --split, each firing gives one occurrence per report and Set it runs, so each can be run,
retried and tracked on its own. The key adds /<report>/<binding>, bindings holds one entry, and
the invocation narrows the run to it:
"argv": ["dre", "run", "--schedule", "close_monthly", "-s", "sales_summary", "--set", "client_a"]That run keeps the schedule’s vars and timezone, and renders exactly what the same Binding renders in the full scheduled run.
Problems
Section titled “Problems”On 0.1.x these are warnings in dre validate and problems here; from 0.2.0 they’re errors:
| Code | |
|---|---|
schedule-needs-anchor |
every, or a rule with INTERVAL above 1, a COUNT, or a day it takes from its start, has no starting. Its occurrences would depend on when you look. |
schedule-too-frequent |
FREQ=SECONDLY or FREQ=MINUTELY. A minute is the finest grain. |
schedule-seconds |
BYSECOND. Schedules fire on whole minutes. |
schedule-not-expandable |
The rule can’t be expanded (e.g. a rule part that doesn’t fit its FREQ). |
dre run --schedule still runs such a schedule.
Compatibility
Section titled “Compatibility”The document is described by a JSON Schema,
schedule-ls.schema.json.
Within format 1, fields may be added in any release, so ignore fields you don’t know. Removing or
renaming a field, or changing what a hash covers, needs a new dre_schedule_version, and only
happens in a minor release.