or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration-options.mdcore-scheduler.mdindex.mdjob-management.mdjob-wrappers.mdlogging.mdschedule-parsing.mdschedule-types.md
tile.json

schedule-parsing.mddocs/

Schedule Parsing

Parse cron specification strings into Schedule objects that determine when jobs should run.

Standard Parser

// ParseStandard returns a Schedule representing the given standard cron spec.
// It requires 5 entries: minute, hour, day of month, month, and day of week.
// Also accepts descriptors like "@midnight" and "@every 1h30m".
func ParseStandard(standardSpec string) (Schedule, error)

Parameters:

  • standardSpec string - Standard cron spec or descriptor

Returns:

  • Schedule - Parsed schedule object
  • error - Error if spec is invalid

Usage:

import "github.com/robfig/cron/v3"

// Standard 5-field cron expression
sched, err := cron.ParseStandard("30 * * * *")
if err != nil {
    log.Fatal(err)
}

// Descriptor
sched, _ := cron.ParseStandard("@hourly")

// Interval
sched, _ := cron.ParseStandard("@every 5m")

// Use with Schedule method
c := cron.New()
job := MyJob{}
c.Schedule(sched, job)

Custom Parser

Create a parser with custom field configurations.

// NewParser creates a Parser with custom options.
// Panics if more than one Optional field is configured.
func NewParser(options ParseOption) Parser

Type Definitions:

type Parser struct {
    // contains unexported fields
}

type ParseOption int

const (
    Second         ParseOption = 1 << iota // Seconds field, default 0
    SecondOptional                         // Optional seconds field, default 0
    Minute                                 // Minutes field, default 0
    Hour                                   // Hours field, default 0
    Dom                                    // Day of month field, default *
    Month                                  // Month field, default *
    Dow                                    // Day of week field, default *
    DowOptional                            // Optional day of week field, default *
    Descriptor                             // Allow descriptors (@monthly, etc.)
)

Usage:

// Parser with required seconds field (6 total fields)
parser := cron.NewParser(
    cron.Second | cron.Minute | cron.Hour |
    cron.Dom | cron.Month | cron.Dow | cron.Descriptor,
)

// Parse a 6-field spec
sched, err := parser.Parse("30 0 * * * *") // Every hour at 30 seconds
if err != nil {
    log.Fatal(err)
}

// Parser without time fields (3 fields: day, month, day-of-week)
dateParser := cron.NewParser(cron.Dom | cron.Month | cron.Dow)
sched, _ := dateParser.Parse("15 3 *") // 15th day, March, any weekday

Parsing with Custom Parser

// Parse returns a Schedule representing the given spec.
// Accepts crontab specs and features configured by NewParser.
func (p Parser) Parse(spec string) (Schedule, error)

Usage:

// Create parser with seconds
parser := cron.NewParser(
    cron.Second | cron.Minute | cron.Hour |
    cron.Dom | cron.Month | cron.Dow | cron.Descriptor,
)

// Parse various specs
specs := []string{
    "0 30 * * * *",           // Every hour at 30 minutes, 0 seconds
    "*/5 * * * * *",          // Every 5 seconds
    "@every 10s",             // Every 10 seconds
}

for _, spec := range specs {
    sched, err := parser.Parse(spec)
    if err != nil {
        log.Printf("Failed to parse %s: %v", spec, err)
        continue
    }
    fmt.Printf("Next run: %v\n", sched.Next(time.Now()))
}

Schedule Interface

All parsed schedules implement the Schedule interface.

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
}

type ScheduleParser interface {
    Parse(spec string) (Schedule, error)
}

Cron Expression Format

Standard Format (5 Fields)

Field name   | Mandatory? | Allowed values  | Allowed special characters
----------   | ---------- | --------------  | --------------------------
Minutes      | Yes        | 0-59            | * / , -
Hours        | Yes        | 0-23            | * / , -
Day of month | Yes        | 1-31            | * / , - ?
Month        | Yes        | 1-12 or JAN-DEC | * / , -
Day of week  | Yes        | 0-6 or SUN-SAT  | * / , - ?

Examples:

// Every hour on the half hour
"30 * * * *"

// Every day at 3:30 AM
"30 3 * * *"

// Every Monday at midnight
"0 0 * * 1"

// At 4:30 PM on the 15th of each month
"30 16 15 * *"

// Multiple times: 3-6am and 8-11pm
"30 3-6,20-23 * * *"

// Every 15 minutes
"*/15 * * * *"

// First day of every month
"0 0 1 * *"

Special Characters

Asterisk ( * )

  • Matches all values for that field
  • * * * * * = every minute

Slash ( / )

  • Step values
  • */15 * * * * = every 15 minutes
  • 10-30/5 * * * * = every 5 minutes from 10 to 30

Comma ( , )

  • List of values
  • 0 9,12,15 * * * = 9am, 12pm, 3pm
  • * * * * MON,WED,FRI = Monday, Wednesday, Friday

Hyphen ( - )

  • Range of values
  • 0 9-17 * * * = every hour from 9am to 5pm
  • * * 1-15 * * = first 15 days of month

Question mark ( ? )

  • Synonym for * (for day-of-month or day-of-week)
  • 0 0 ? * MON = every Monday at midnight

Descriptors

Predefined schedule aliases:

Descriptor             | Description                                | Equivalent To
-----                  | -----------                                | -------------
@yearly or @annually   | Once a year, midnight, Jan. 1st            | 0 0 1 1 *
@monthly               | Once a month, midnight, first of month     | 0 0 1 * *
@weekly                | Once a week, midnight between Sat/Sun      | 0 0 * * 0
@daily or @midnight    | Once a day, midnight                       | 0 0 * * *
@hourly                | Once an hour, beginning of hour            | 0 * * * *

Examples:

c := cron.New()
c.AddFunc("@yearly", func() { fmt.Println("Happy New Year!") })
c.AddFunc("@monthly", func() { fmt.Println("Start of month") })
c.AddFunc("@weekly", func() { fmt.Println("Weekly report") })
c.AddFunc("@daily", func() { fmt.Println("Daily backup") })
c.AddFunc("@hourly", func() { fmt.Println("Hourly check") })

Intervals

Fixed duration intervals starting from when added or cron starts.

Format: @every <duration>

Examples:

c := cron.New()

// Every 5 minutes
c.AddFunc("@every 5m", func() { fmt.Println("5 min") })

// Every 1 hour 30 minutes 10 seconds
c.AddFunc("@every 1h30m10s", func() { fmt.Println("1.5 hours") })

// Every 30 seconds
c.AddFunc("@every 30s", func() { fmt.Println("30 sec") })

// Every 24 hours
c.AddFunc("@every 24h", func() { fmt.Println("Daily") })

Note: The interval does not account for job runtime. If a job takes 3 minutes and runs every 5 minutes, there will be only 2 minutes between executions.

Timezones

Default Timezone

By default, all schedules use the machine's local timezone (or the timezone set with WithLocation).

// All specs use time.Local
c := cron.New()
c.AddFunc("0 6 * * *", func() { fmt.Println("6am local time") })

// All specs use America/New_York
nyc, _ := time.LoadLocation("America/New_York")
c := cron.New(cron.WithLocation(nyc))
c.AddFunc("0 6 * * *", func() { fmt.Println("6am New York time") })

Per-Schedule Timezone

Override timezone for individual schedules using CRON_TZ= or TZ= prefix:

c := cron.New() // Uses time.Local

// This job runs at 6am Tokyo time
c.AddFunc("CRON_TZ=Asia/Tokyo 0 6 * * ?", func() {
    fmt.Println("6am Tokyo time")
})

// This job runs at 6am New York time
c.AddFunc("CRON_TZ=America/New_York 0 6 * * ?", func() {
    fmt.Println("6am New York time")
})

// This job runs at 6am local time
c.AddFunc("0 6 * * ?", func() {
    fmt.Println("6am local time")
})

Daylight Saving Time Warning: Jobs scheduled during DST leap-ahead transitions will not run!

Month and Weekday Names

Month and day-of-week values are case insensitive:

Months: JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC Weekdays: SUN, MON, TUE, WED, THU, FRI, SAT

// These are all equivalent
"0 0 1 JAN *"
"0 0 1 jan *"
"0 0 1 1 *"

// These are all equivalent
"0 0 * * MON"
"0 0 * * mon"
"0 0 * * 1"

Parser Configuration Examples

Seconds Field (Required)

// Quartz-compatible format with 6 fields
parser := cron.NewParser(
    cron.Second | cron.Minute | cron.Hour |
    cron.Dom | cron.Month | cron.Dow | cron.Descriptor,
)

// Must provide seconds field
sched, _ := parser.Parse("0 30 * * * *") // Every hour at 30 minutes, 0 seconds
sched, _ := parser.Parse("*/5 * * * * *") // Every 5 seconds

Seconds Field (Optional)

// 5 or 6 fields, seconds defaults to 0 if omitted
parser := cron.NewParser(
    cron.SecondOptional | cron.Minute | cron.Hour |
    cron.Dom | cron.Month | cron.Dow | cron.Descriptor,
)

// With seconds (6 fields)
sched, _ := parser.Parse("30 0 * * * *")

// Without seconds (5 fields, defaults to 0)
sched, _ := parser.Parse("0 * * * *") // Same as "0 0 * * * *"

Minimal Fields

// Only day, month, weekday
parser := cron.NewParser(cron.Dom | cron.Month | cron.Dow)
sched, _ := parser.Parse("15 * *") // 15th of every month

Without Descriptors

// No @hourly, @daily, etc.
parser := cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow)

// This works
sched, _ := parser.Parse("0 * * * *")

// This fails (descriptors not enabled)
sched, err := parser.Parse("@hourly") // Returns error

Complete Parsing Example

package main

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

func main() {
    // Standard parser
    standardSpecs := []string{
        "30 * * * *",         // Every hour at 30 minutes
        "0 0 * * *",          // Daily at midnight
        "@hourly",            // Every hour
        "@every 30m",         // Every 30 minutes
    }

    for _, spec := range standardSpecs {
        sched, err := cron.ParseStandard(spec)
        if err != nil {
            log.Printf("Failed to parse %s: %v", spec, err)
            continue
        }
        next := sched.Next(time.Now())
        fmt.Printf("Spec: %-20s Next: %v\n", spec, next)
    }

    // Custom parser with seconds
    parser := cron.NewParser(
        cron.Second | cron.Minute | cron.Hour |
        cron.Dom | cron.Month | cron.Dow | cron.Descriptor,
    )

    secondSpecs := []string{
        "0 30 * * * *",       // Every hour at 30 minutes, 0 seconds
        "*/10 * * * * *",     // Every 10 seconds
        "@every 5s",          // Every 5 seconds
    }

    for _, spec := range secondSpecs {
        sched, err := parser.Parse(spec)
        if err != nil {
            log.Printf("Failed to parse %s: %v", spec, err)
            continue
        }
        next := sched.Next(time.Now())
        fmt.Printf("Spec: %-20s Next: %v\n", spec, next)
    }

    // Test timezone handling
    spec := "CRON_TZ=Asia/Tokyo 0 6 * * ?"
    sched, _ := cron.ParseStandard(spec)
    next := sched.Next(time.Now())
    fmt.Printf("Spec: %s\nNext: %v\n", spec, next)
}