Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

cron - cron schedules

Import with import "cron.j" as cron;. Parse and evaluate cron expressions - the five-field minute hour day-of-month month day-of-week spec. parse builds a Schedule, matches tests whether a time.Time fires it, and next finds the next fire at or after a time. A pure calculator over time - no clock, no sleeping - so it runs on both binaries; a real scheduler is your own spawn + time.sleep loop over cron.next.

import "cron.j" as cron;

def s as cron.Schedule init cron.parse("30 9 * * 1-5");   # 09:30 on weekdays
def fire as time.Time init cron.next($s, time.now());
io.printf("next run: %s\n", time.iso($fire));

Runnable: examples/modules/cron_demo.j.

Functions

CallReturnsNotes
cron.parse(expr)ScheduleParse a five-field expression.
cron.matches(schedule, t)boolDoes the schedule fire at t? (minute granularity - seconds are ignored).
cron.next(schedule, after)time.TimeThe next fire at or after after, keeping its zone offset.

Fields

Five whitespace-separated fields, each with the usual operators:

FieldRange
minute0-59
hour0-23
day of month1-31
month1-12
day of week0-70 and 7 are both Sunday

Each field accepts * (every value), a single number, an a-b range, an a,b,c list, and a /n step - on a wildcard (every nth value), a range (a-b/n), or a value (a/n, meaning a to the field maximum). Examples:

ExpressionFires
* * * * *every minute
*/15 * * * *every 15 minutes
0 9 * * 1-509:00 on weekdays (Mon-Fri)
0 0 1 * *midnight on the 1st of each month
30 3 * * 003:30 on Sundays
0 0 13 * 5midnight on Friday the 13th (see below)

The day-of-month / day-of-week rule

When both the day-of-month and day-of-week fields are restricted (neither is *), a day matching either one fires - the standard cron behavior. So 0 0 13 * 5 fires on the 13th and on every Friday. When one of the two is *, only the other constrains the day.

next

cron.next(schedule, after) returns the first matching minute at or after after (with its seconds zeroed), preserving the input’s zone offset. If after already sits exactly on a matching minute, it is returned. The search skips non-matching days whole (so a yearly schedule is found quickly) and gives up after a five-year horizon - an impossible schedule (e.g. 0 0 31 2 *, February 31st) throws a catchable Error (kind "cron") rather than looping forever.

Zones are fixed-offset (as in the time library), so next does no DST arithmetic.

Scope

  • Standard five fields. No seconds field, no @daily / @reboot macros, and no non-standard extensions (L, W, #, ?).
  • A calculator, not a runner. It never touches the clock. Drive it yourself: time.sleep(time.sub(cron.next($s, time.now()), time.now())), then run the job.

See also