A Go library for parsing cron expressions and computing the next matching time(s). Fork of gorhill/cronexpr with the following changes:
- Modernize
- Fix panic on wrap-around ranges (e.g.
14-3for hours) by correctly handling ranges where start > end - Replace all regex-based parsing with
strings.Cut/map lookups, eliminating theregexpandsyncdependencies (~3x faster parsing, 44% less memory)
Requires Go 1.25+.
go get github.com/toba/cronexpr
import (
"fmt"
"time"
"github.com/toba/cronexpr"
)Parse an expression and get the next matching time:
expr := cronexpr.MustParse("0 0 29 2 *")
next := expr.Next(time.Now())
fmt.Println(next)Parse returns an error instead of panicking:
expr, err := cronexpr.Parse("0 0 29 2 *")
if err != nil {
log.Fatal(err)
}Get the next n matching times with NextN:
nextTimes := cronexpr.MustParse("0 0 29 2 *").NextN(time.Now(), 5)
for _, t := range nextTimes {
fmt.Println(t)
}A zero time is returned when no future match exists. Use IsZero to check:
next := cronexpr.MustParse("* * * * * 1980").Next(time.Now())
if next.IsZero() {
fmt.Println("no matching time")
}The time zone of returned times always matches the time zone of the input.
| Format | Fields |
|---|---|
| 5 fields | minute, hour, day-of-month, month, day-of-week |
| 6 fields | minute, hour, day-of-month, month, day-of-week, year |
| 7 fields | second, minute, hour, day-of-month, month, day-of-week, year |
When 5 fields are given, seconds default to 0 and year defaults to *. When 6 fields are given, seconds default to 0.
These are widely supported across cron implementations (Vixie cron, busybox, etc.):
- Month names --
JAN-DEC(case-insensitive); full names also accepted - Day-of-week names --
SUN-SAT(case-insensitive); full names also accepted;7is accepted as Sunday - Wrap-around ranges -- ranges where start > end wrap through the field boundary (e.g.
22-3for hours means 22, 23, 0, 1, 2, 3)
When both day-of-month and day-of-week are restricted (not *), a day matches if either field matches (union semantics, per the crontab spec).
These originate from Quartz Scheduler and are not part of standard cron:
Lin day-of-month -- last day of the monthLin day-of-week -- last occurrence of that weekday in the month (e.g.5L= last Friday)Win day-of-month -- nearest weekday to the given day (e.g.15W); single days onlyLWin day-of-month -- last weekday (Mon-Fri) of the month#in day-of-week -- nth occurrence of a weekday (e.g.5#3= third Friday)
| Alias | Equivalent |
|---|---|
@yearly, @annually |
0 0 0 1 1 * * |
@monthly |
0 0 0 1 * * * |
@weekly |
0 0 0 * * 0 * |
@daily |
0 0 0 * * * * |
@hourly |
0 0 * * * * * |
- Year range is 1970-2099
@rebootis not supportedW(nearest weekday) only accepts a single day value, not a range or list, and does not cross month boundaries