The project is in a healthy, maintained state
A zero-dependency Ruby gem for parsing 5-field cron expressions and running an in-process scheduler. Supports wildcards, ranges, steps, and lists.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies
 Project Readme

philiprehberger-cron_kit

Gem Version CI License

Cron expression parser and scheduler for Ruby. Zero dependencies.

Requirements

  • Ruby >= 3.1

Installation

Add to your Gemfile:

gem "philiprehberger-cron_kit"

Or install directly:

gem install philiprehberger-cron_kit

Usage

Parsing Expressions

require "philiprehberger/cron_kit"

expr = Philiprehberger::CronKit.parse("*/5 * * * *")

expr.match?(Time.new(2026, 3, 10, 12, 15)) # => true
expr.match?(Time.new(2026, 3, 10, 12, 13)) # => false

expr.next_at(from: Time.new(2026, 3, 10, 12, 13))
# => 2026-03-10 12:15:00

expr.to_s # => "*/5 * * * *"

Timezone Support

Evaluate cron expressions in a specific timezone. Uses only stdlib — no external gems required.

# Fixed UTC offset
expr = Philiprehberger::CronKit.parse("0 9 * * *", timezone: "+05:30")

# POSIX timezone name (resolved via ENV["TZ"])
expr = Philiprehberger::CronKit.parse("0 9 * * *", timezone: "US/Eastern")

# UTC shorthand
expr = Philiprehberger::CronKit.parse("0 9 * * *", timezone: "UTC")

expr.match?(some_time)       # evaluated in the configured timezone
expr.next_at(from: Time.now) # next match in that timezone

Next Runs Preview

Get the next N upcoming execution times from a given start:

expr = Philiprehberger::CronKit.parse("0 * * * *")

expr.next_runs(count: 5, from: Time.now)
# => [2026-03-17 14:00, 2026-03-17 15:00, 2026-03-17 16:00, ...]

Previous Run

Find the most recent past match:

expr = Philiprehberger::CronKit.parse("0 * * * *")

expr.previous_run(from: Time.now)
# => 2026-03-17 13:00:00

Non-Standard Aliases

Use convenient shorthand aliases instead of full cron expressions:

Philiprehberger::CronKit.parse("@hourly")   # => "0 * * * *"
Philiprehberger::CronKit.parse("@daily")    # => "0 0 * * *"
Philiprehberger::CronKit.parse("@weekly")   # => "0 0 * * 0"
Philiprehberger::CronKit.parse("@monthly")  # => "0 0 1 * *"
Philiprehberger::CronKit.parse("@yearly")   # => "0 0 1 1 *"
Philiprehberger::CronKit.parse("@annually") # => "0 0 1 1 *"

Scheduling Jobs

scheduler = Philiprehberger::CronKit.new

scheduler.every("0 9 * * 1-5") do |time|
  puts "Good morning! It's #{time}"
end

scheduler.every("*/10 * * * *") do
  puts "Running every 10 minutes"
end

scheduler.start   # runs in a background thread
scheduler.running? # => true
scheduler.stop

Job Timeout

Kill jobs that exceed a time limit (in seconds):

scheduler = Philiprehberger::CronKit.new

scheduler.every("*/5 * * * *", timeout: 30) do
  perform_work  # killed if it takes longer than 30 seconds
end

Named Jobs

scheduler = Philiprehberger::CronKit.new

scheduler.every("0 9 * * 1-5", name: "morning-report") do
  generate_report
end

scheduler.job_names     # => ["morning-report"]
scheduler.remove("morning-report")

Inspecting Next Runs

scheduler.next_runs(from: Time.now)
# => { "morning-report" => 2026-03-13 09:00:00 ... }

Supported Syntax

Token Example Description
* * * * * * Every possible value
Value 5 * * * * Specific value
Range 1-5 Values from 1 through 5
Step */5 Every 5th value
List 1,3,5 Values 1, 3, and 5
Alias @daily Non-standard shorthand

Fields

Position Field Range
1 Minute 0-59
2 Hour 0-23
3 Day of month 1-31
4 Month 1-12
5 Day of week 0-6

API

Method Description
Philiprehberger::CronKit.parse(expression, timezone: nil) Parse a cron expression, returns Expression
Philiprehberger::CronKit.new Create a new Scheduler
Expression#match?(time) Check if a Time matches the expression
Expression#next_at(from:) Find the next matching Time
Expression#next_runs(count: 5, from:) Return the next N matching times
Expression#previous_run(from:) Find the most recent past match
Expression#to_s Return the original expression string
Expression#timezone Return the configured timezone (or nil)
Scheduler#every(expression, name: nil, timeout: nil, &block) Register a cron job
Scheduler#job_names List registered job names
Scheduler#remove(name) Remove a job by name
Scheduler#next_runs(from:) Hash of job names to their next scheduled time
Scheduler#start Start the scheduler in a background thread
Scheduler#stop Stop the scheduler
Scheduler#running? Check if the scheduler is running

Development

bundle install
bundle exec rspec
bundle exec rubocop

License

MIT