RRule vs Cron
Cron has been the default scheduling primitive for 50 years. RRule (RFC 5545) is a different model — one built around expressibility, determinism, and human readability. This page outlines the practical differences.
At a glance
| Capability | Cron | RRule (RFC 5545) |
|---|---|---|
| Simple interval (every N minutes/hours) | ✅ | ✅ |
| Specific day of week | ✅ | ✅ |
| Every N weeks | ⚠️ workaround | ✅ |
| Nth weekday of month (e.g. first Monday) | ❌ | ✅ |
| Last day of month | ❌ | ✅ |
| Run exactly N times (COUNT) | ❌ | ✅ |
| Run until a date (UNTIL) | ❌ | ✅ |
| Explicit IANA timezone | ❌ | ✅ |
| Automatic DST handling | ❌ | ✅ |
| Human-readable explanation | ❌ | ✅ |
| Deterministic simulation | ❌ | ✅ |
| Standardized format (RFC) | ❌ | ✅ |
Expressiveness
Cron uses five fields (minute, hour, day-of-month, month, day-of-week) and supports basic intervals via */n syntax. This covers a narrow range of real-world schedules. Anything more complex either requires workarounds or simply cannot be expressed.
Every two weeks
Cron
# No native support.
# Common workaround: run weekly, check week number in script
0 9 * * 1
# Requires external logic to skip odd/even weeksRRule
FREQ=WEEKLY;INTERVAL=2;BYDAY=MO;BYHOUR=9;BYMINUTE=0First Monday of every month
Cron
# Not expressible in standard cron.
# Workaround: run every Monday, filter with script
0 9 * * 1
# if [ $(date +\%e) -le 7 ]; then ...RRule
FREQ=MONTHLY;BYDAY=+1MO;BYHOUR=9;BYMINUTE=0Last day of every month
Cron
# Not expressible. Common workaround uses a wrapper:
0 9 28-31 * *
# Requires: [ "$(date +%d)" = "$(cal | awk '/[0-9]/{x=$NF} END{print x}')" ]RRule
FREQ=MONTHLY;BYMONTHDAY=-1;BYHOUR=9;BYMINUTE=0BYMONTHDAY=-1 means the last day of the month, regardless of its length.
Every weekday, 10 times then stop
Cron
# COUNT not supported. Requires external counter:
0 9 * * 1-5
# Script must track executions and remove crontab after 10 runsRRule
FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;BYHOUR=9;BYMINUTE=0;COUNT=10Bounded schedules
Cron jobs run indefinitely by default. Stopping them requires external logic: a wrapper script, a database flag, or removing the crontab entry. There is no native way to say "run 10 times" or "run until March 31st".
RRule has first-class support for both:
# Run exactly 10 times, then stop
FREQ=WEEKLY;BYDAY=MO;BYHOUR=9;COUNT=10
# Run every week until March 31st
FREQ=WEEKLY;BYDAY=MO;BYHOUR=9;UNTIL=20261231T000000ZThe schedule is self-describing: its end condition is part of the rule itself, not external state.
Timezones and DST
Standard cron has no timezone concept. It runs in the system timezone of the host. When the host changes timezone, or when DST transitions occur, behavior shifts silently. A job scheduled at 0 9 * * * may fire at 8am or 10am after a DST transition depending on the implementation.
RRule is paired with an explicit IANA timezone. The question "9am means 9am local time, always" is answered unambiguously:
# Every weekday at 9am, expressed with explicit timezone
DTSTART;TZID=America/New_York:20260101T090000
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;BYHOUR=9;BYMINUTE=0
# rrule.net output in UTC (automatic DST handling):
# 2026-03-09T14:00:00Z → 9am EST (UTC-5)
# 2026-03-16T13:00:00Z → 9am EDT (UTC-4) ← DST transition handledThe UTC offsets shift automatically at DST boundaries. The local time stays constant.
Explainability
Given a cron expression, the only way to understand it is to parse it manually or use a third-party tool. There is no standard for producing a human-readable description from a cron string.
RRule expressions can be described in natural language. rrule.net goes further: validation returns an explanation alongside the rule, so both humans and AI agents can verify intent before anything is scheduled.
rrule.net — POST /v1/schedules/validate
// Input
{
"input": "Every second Tuesday of the month at 6pm",
"timezone": "Europe/Paris"
}
// Output
{
"valid": true,
"rrule": {
"dtstart": "2026-02-10T17:00:00.000Z",
"rule": "FREQ=MONTHLY;BYDAY=TU;BYSETPOS=2;BYHOUR=18;BYMINUTE=0;BYSECOND=0"
},
"explanation": {
"text": "Every second Tuesday of the month at 6:00 PM (Europe/Paris)",
"confidence": 0.97
}
}The confidence score indicates how certain the parser is about the interpretation. Ambiguous inputs are rejected with explicit clarification questions rather than silently guessed.
Determinism and simulation
A cron expression cannot be simulated without running against a clock. You can mentally parse the fields, but verifying that it does what you intend on specific edge cases (end of month, DST, leap years) requires a test environment.
A RRule is a pure mathematical function: given a start date and a rule, the full sequence of occurrences is deterministic and computable without side effects. This makes it trivial to preview, test, and audit.
// rrule.net — POST /v1/schedules/simulate
{
"rrule": {
"dtstart": "2026-02-10T17:00:00.000Z",
"rule": "FREQ=MONTHLY;BYDAY=TU;BYSETPOS=2;BYHOUR=18;BYMINUTE=0"
},
"timezone": "Europe/Paris",
"count": 5
}
// Response
{
"occurrences": [
"2026-02-10T17:00:00.000Z",
"2026-03-10T17:00:00.000Z",
"2026-04-14T16:00:00.000Z", // DST: Paris moves to UTC+2
"2026-05-12T16:00:00.000Z",
"2026-06-09T16:00:00.000Z"
]
}Standardization
Cron has no single specification. Vixie cron, systemd timers, AWS EventBridge, Google Cloud Scheduler, and GitHub Actions each implement slightly different syntax. A cron expression that works in one context may be invalid or behave differently in another.
RRule is defined by RFC 5545 (iCalendar, 1998) and its successor RFC 7986. The same rule parsed by any conforming implementation produces the same result. It is the format used internally by Google Calendar, Outlook, and Apple Calendar to store recurring events.
When to use each
Cron is not a bad tool. It is a simple, battle-tested tool for simple cases. The friction starts when your requirements grow beyond its expressive range.
Cron is appropriate for
- ✓ Simple periodic system tasks ("every 5 minutes")
- ✓ Infrastructure jobs with no business logic dependency
- ✓ Single-server environments in a fixed timezone
- ✓ Jobs that run indefinitely with no end condition
RRule is appropriate for
- ✓ User-defined schedules (billing, reminders, reports)
- ✓ Multi-timezone SaaS with international users
- ✓ Calendar-aware patterns (last day of month, nth weekday)
- ✓ Bounded schedules (COUNT or UNTIL)
- ✓ Contexts requiring auditing and explainability
- ✓ AI agents that need to reason about time
rrule.net exposes RRule validation, simulation, and scheduling as a REST API, with natural language input and timezone-aware DST handling.