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

CapabilityCron 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 weeks

RRule

FREQ=WEEKLY;INTERVAL=2;BYDAY=MO;BYHOUR=9;BYMINUTE=0

First 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=0

Last 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=0

BYMONTHDAY=-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 runs

RRule

FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;BYHOUR=9;BYMINUTE=0;COUNT=10

Bounded 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=20261231T000000Z

The 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 handled

The 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.