Scheduling
Cron expressions
A schedule is six fields separated by whitespace. A job runs at every moment where all six match.
On this page
The expression above means 09:30:00, Monday to Friday.
Field syntax
Every field accepts the same syntax, and each one can combine forms in a comma-separated list. The examples below are for the seconds field.
| Form | Example | Matches |
|---|---|---|
| Any value | * |
Every value in the field |
| Value | 5 |
5 |
| Range | 10-13 |
10, 11, 12, 13 |
| List | 1,15,30 |
1, 15, 30 |
| Step | */15 |
0, 15, 30, 45 |
| Range with step | 10-30/5 |
10, 15, 20, 25, 30 |
| Start with step | 5/10 |
5, 15, 25, 35, 45, 55 |
| Mixed list | 1-5/2,10,20/20 |
1, 3, 5, 10, 20, 40 |
Try one
This uses the same parser as the package, so what it accepts is what baker.add() accepts. Click a field to select it in the input.
- Second
- 0at :00
- Minute
- 0at :00
- Hour
- 1212:xx
- Day
- *every day
- Month
- *every month
- Weekday
- 1-5Mon to Fri
Differences from crontab
If you’re used to a system crontab, four things will catch you out.
Six fields, not five
The seconds field comes first and isn’t optional. A five-field expression is rejected. To convert one, put a 0 in front of it, so */5 * * * * becomes 0 */5 * * * *.
Numbers only
Names such as MON and JAN aren’t supported, and neither are ?, L, W or #. Sunday is 0, and 7 is out of range.
Day of month and day of week must both match
Most cron implementations run when either day field matches. Cronbake requires both, so 0 0 0 13 * 5 only fires on Friday the 13th. On a standard crontab, the same two day fields would fire on every Friday and on every 13th.
Steps restart at each field
A step counts through the values of its field and doesn’t carry over into the next one. */7 in the seconds field matches 0, 7, 14 and so on up to 56, and then the next match is 0 in the following minute, only four seconds later.
A step larger than the field collapses to one value. */90 in the seconds field matches only 0, so it runs once a minute and not every 90 seconds. If you need an exact interval, keep your own timer.
Validation
An invalid expression fails early and loudly.
Cron.isValid("60 * * * * *"); // false
// Throws: Cron field out of range: 60
Cron.parse("60 * * * * *");
// Throws: Invalid cron expression: 60 * * * * *
baker.add({ name: "bad", cron: "60 * * * * *", callback });TypeScript catches mistakes in presets, but it can’t check six-field strings, so those fail when the job is created.
baker.add({ name: "a", cron: "@every_5_minutes", callback });
// @ts-expect-error "mins" is not a unit, so this doesn't compile
baker.add({ name: "b", cron: "@every_5_mins", callback });Dates that never come
Validation checks syntax and bounds, not the calendar. 0 0 0 31 2 * asks for the 31st of February, so it passes Cron.isValid() and baker.add(), then throws when the job starts because no matching date exists.
Using expressions without a job
The static methods on Cron don’t need a running job.
Cron.parse("0 */15 9-17 * * 1-5");
// { second: [0], minute: [0, 15, 30, 45], hour: [9, 10, ..., 17], ... }
Cron.getNext("@daily"); // the next midnight
Cron.getPrevious("@daily"); // the last midnightReference dates
getNext() and getPrevious() take an optional Date to search from. The search doesn’t include that moment itself, so you can step through a schedule by feeding each result back in.
let run = new Date();
const upcoming: Date[] = [];
for (let i = 0; i < 5; i++) {
run = Cron.getNext("0 30 9 * * 1-5", run);
upcoming.push(run);
}Both methods search up to 400 years in either direction, then throw if nothing matches. They also throw if the reference date is invalid. In 0.4.0 the second argument is ignored.
All of this uses the time zone of the process. See time zones for what that means in practice.