cron

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 8, 2026 License: MIT Imports: 5 Imported by: 0

README

Cron

Don't waste another hour configuring crontabs. Spin up, manage, and debug scheduled tasks in seconds with Crystade - the next-level SaaS for modern cron management.

banner

Cron is a fork of robfig/cron. Compared to the original project:

  • The scheduler is removed
  • The syntax is modified:
    • Only CRON_TZ= is permitted to represent timezone (the original permits CRON_TZ= and TZ=)
    • Only * is permitted to represent "every-step" (the original permits both * and ?)
    • Descriptors are unsupported
    • Seconds are unsupported
  • The default timezone is UTC (the original sets default to local)
  • Customizable year limit for the scheduling algorithm

Installation

go get github.com/crystade/cron

Usage

Basic Usage
package main

import (
    "fmt"
    "log"
    "time"
    
    "github.com/crystade/cron"
)

func main() {
    // Parse a cron expression
    schedule, err := cron.Parse("0 9 * * mon-fri")
    if err != nil {
        log.Fatal(err)
    }
    
    // Get the next execution time
    next := schedule.Next(time.Now())
    fmt.Println("Next run:", next)
}
Custom Parser Configuration
// Create a parser with custom year limit
parser := cron.NewParser(10) // Search up to 10 years ahead
schedule, err := parser.Parse("0 0 29 2 *") // Feb 29 (leap years only)
if err != nil {
    log.Fatal(err)
}

next := schedule.Next(time.Now())
fmt.Println("Next leap year Feb 29:", next)
WASM/TinyGo Support

This library is fully compatible with TinyGo and WASM. Build for WASM:

tinygo build -target wasm -o cron.wasm ./cmd/wasm

Example WASM usage in JavaScript:

// Load the WASM module
const go = new Go();
WebAssembly.instantiateStreaming(fetch("cron.wasm"), go.importObject)
    .then((result) => {
        go.run(result.instance);

        // Reusable schedule: parse once, call next() many times
        const spec = cronParse("0 9 * * mon-fri");
        if (spec.error) {
            console.error(spec.error);
        } else {
            const now = Temporal.Now.instant();
            const r1 = spec.next(now.epochMilliseconds);
            console.log("Next run:", Temporal.Instant.fromEpochMilliseconds(r1.unixMillis));
            const r2 = spec.next(r1.unixMillis);
            console.log("Run after:", Temporal.Instant.fromEpochMilliseconds(r2.unixMillis));
            spec.free(); // release Go handles when done
        }

        // One-shot helper
        const r = cronNext("0 9 * * mon-fri", Temporal.Now.instant().epochMilliseconds);
        if (r.error) {
            console.error(r.error);
        } else {
            console.log("Next run:", Temporal.Instant.fromEpochMilliseconds(r.unixMillis));
        }
    });

Both functions accept and return Unix timestamps in milliseconds (number / integer). Use Temporal.Now.instant().epochMilliseconds to get the current time and Temporal.Instant.fromEpochMilliseconds(result.unixMillis) to convert the result back to a JS Temporal.Instant.

Syntax

[CRON_TZ=<timezone>] <min> <hour> <dom> <month> <dow>
  • Explanation to each field (from left to right):
Field Range
Minute 0–59
Hour 0–23
Day of month 1–31
Month 1–12 or jan–dec
Day of week 0–6 or sun–sat
  • Special presentation (any, every, range and list)
* = any   */n = every n   a-b = range   a,b = list (OR)
  • When both day-of-month and day-of-week coexist, they act as OR condition
Example
Cron Expression Meaning
0 * * * * Every hour
0 9 * * mon-fri 9 AM on weekdays
*/15 * * * * Every 15 minutes
CRON_TZ=America/New_York 0 8 * * * 8 AM New York time daily
EBNF
Expression      = [ TimeZone, Space ], UnixExpr
TimeZone        = TimeZonePrefix, "=", IANATimeZone
TimeZonePrefix  = "CRON_TZ"
IANATimeZone    = (* IANA timezone identifier, e.g., "America/New_York", "UTC" *)
Space           = " " { " " } (* one or more U+0020 spaces *)

(* 5-field standard Unix cron: minute hour day-of-month month day-of-week *)
UnixExpr        = MinuteField, Space, HourField, Space, DayOfMonthField, Space, MonthField, Space, DayOfWeekField

(* Comma-separated field lists *)
MinuteField     = NumericFieldPart { ",", NumericFieldPart } (* 0-59 *)
HourField       = NumericFieldPart { ",", NumericFieldPart } (* 0-23 *)
DayOfMonthField = NumericFieldPart   { ",", NumericFieldPart }  (* 1-31 *)
MonthField      = MonthFieldPart   { ",", MonthFieldPart }  (* 1-12 *)
DayOfWeekField  = DOWFieldPart     { ",", DOWFieldPart }  (* 0-6 *)

(* Core field components: *, */N, N, N-N, N/N, N-N/N *)
NumericFieldPart = EveryStep [ "/" number ] | number [ "-" number ] [ "/" number ]
MonthFieldPart   = EveryStep [ "/" number ] | MonthValue [ "-" MonthValue ] [ "/" number ]
DOWFieldPart     = EveryStep [ "/" number ] | DOWValue [ "-" DOWValue ] [ "/" number ]

EveryStep       = "*"
number          = digit { digit }
digit           = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"

(* Named values for months and days of week (case-insensitive in practice) *)
MonthValue      = number | "jan" | "feb" | "mar" | "apr" | "may" | "jun" | "jul" | "aug" | "sep" | "oct" | "nov" | "dec"
DOWValue        = number | "sun" | "mon" | "tue" | "wed" | "thu" | "fri" | "sat"

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Parser

type Parser struct {
	// YearLimit is the maximum number of years to search for the next schedule.
	// Default is 5 years if not set or set to 0.
	YearLimit int
}

Parser is a cron expression parser with configurable options. Parser is safe for concurrent use and can be reused across multiple Parse calls. It is compatible with TinyGo and WASM environments.

func NewParser

func NewParser(yearLimit int) *Parser

NewParser creates a new Parser with the specified year limit. If yearLimit is 0 or negative, the default of 5 years will be used.

Example:

parser := cron.NewParser(10) // Search up to 10 years ahead
schedule, err := parser.Parse("0 9 * * mon-fri")

func (*Parser) Parse

func (p *Parser) Parse(spec string) (Schedule, error)

Parse parses a 5-field cron expression and returns a schedule that can compute the next matching activation time using the parser's configuration.

type Schedule

type Schedule interface {
	// Next returns the next activation time, later than the given time.
	// Next is invoked initially, and then each time the job is run.
	Next(time.Time) time.Time
}

Schedule describes a job's duty cycle.

func Parse

func Parse(spec string) (Schedule, error)

Parse parses a 5-field cron expression and returns a schedule that can compute the next matching activation time. This is a convenience function that uses the default Parser configuration (5 year limit).

For custom configuration, create a Parser with NewParser() or use a Parser literal.

Example:

schedule, err := cron.Parse("0 9 * * mon-fri")
if err != nil {
    log.Fatal(err)
}
next := schedule.Next(time.Now())

type SpecSchedule

type SpecSchedule struct {
	Second, Minute, Hour, Dom, Month, Dow uint64

	// Override location for this schedule.
	Location  *time.Location
	YearLimit int
}

SpecSchedule specifies a duty cycle (to the second granularity), based on a traditional crontab specification. It is computed initially and stored as bit sets.

func (*SpecSchedule) Next

func (s *SpecSchedule) Next(t time.Time) time.Time

Next returns the next time this schedule is activated, greater than the given time. If no time can be found to satisfy the schedule, return the zero time.

Directories

Path Synopsis
cmd
wasm command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL