Cron Expression Playground: Builder + Timeline Showing Next Runs

A developer-focused walkthrough of the Systhoughts cron expression playground: build and edit expressions, read plain-English explanations, and inspect next runs on a timeline. Four worked examples, the weekday/dialect/timezone mistakes to avoid, and small challenges to try.

Cron expressions look like they should be simple: five fields (see Open Cron Pattern Specification 1.0), one line, what could possibly go wrong? Plenty, and the failure usually isn't in the syntax, it's in the *next run*. 30 8 1 * * parses fine and reads fine, then does the opposite of what you meant the moment you add a weekday.

After a decade of crontabs, I've stopped trusting what an expression *says* and started trusting what the machine will actually do: the exact datetimes it will fire.

This post is about building, testing, and debugging cron expressions with a playground that shows you those datetimes instead of making you guess.

A cron expression is a five-field string that tells a scheduler when to run a job: minute, hour, day of month, month, and day of week. */5 * * * * means "every five minutes"; 0 9 * * 1-5 means "weekdays at 09:00." Most of getting fluent is seeing real schedules render, which is exactly what the playground is for.

The short version:

  • A next-run timeline is the only output that can't lie. Parsers prove validity, explanations prove intent, the run list proves what actually happens.
  • The day-of-week field is where expressions go to die. Numbering differs by dialect, and combining it with day-of-month turns cron into an OR, not an AND.
  • Time zones and DST are silent killers. Always check which timezone a next-run list was computed in.

The Systhoughts cron expression playground

Everything below is built around one tool: the Systhoughts cron expression playground, a builder and tester in one with three parts:

  • Builder. Type an expression or assemble it field by field (minute, hour, day of month, month, day of week), with lists, ranges, steps, and names.
  • Explain. It rewrites the expression in plain English, so 0 9 * * 1-5 becomes "at 09:00, Monday through Friday."
  • Timeline. It computes the next scheduled runs and draws them on a timeline, with a list of exact datetimes underneath.
Try the cron expression playground

Keep it open while you read. Every example below is meant to be typed in, inspected, and modified; change one field and watch the timeline react. That loop - build, explain, preview - is how you actually learn cron: faster than reading man pages, and it catches the mistakes before they hit production.

Cron in 60 seconds

Five fields, in this order:

Cron fields
FeatureValuesWhat it means
minute0–59Minute of the hour
hour0–23Hour of the day
day of month1–31Day of the month
month1–12 or JAN–DECMonth
day of week10–7 or SUN–SATDay of the week
  • minute

    Values
    0–59
    What it means
    Minute of the hour
  • hour

    Values
    0–23
    What it means
    Hour of the day
  • day of month

    Values
    1–31
    What it means
    Day of the month
  • month

    Values
    1–12 or JAN–DEC
    What it means
    Month
  • day of week1

    Values
    0–7 or SUN–SAT
    What it means
    Day of the week
  1. 0 and 7 are both Sunday in classic Vixie cron

The building blocks repeat across fields: * for every value, lists (1,15,30), ranges (1-5), steps (*/10, 10-30/5), and names (MON, JAN) where the field allows them. That's the whole language; the difficulty is never the syntax, it's what the fields combine into. The canonical reference is crontab(5), but you'll spend less time there once the timeline does the explaining.

Why a next-run timeline beats syntax alone

The cron next run is the only fact about a schedule you can verify without waiting for it to fire. A parser can tell you an expression is valid. An explanation can tell you what you intend. Only the next-run list tells you what the scheduler will do and it's the one that catches you. Three ways the timeline earns its keep:

  • Calendars are weird. Month lengths, weekday boundaries, and leap years bend a naive reading of "the 31st" or "every day." The timeline shows the real gaps between runs instead of the idealized pattern.
  • The OR trap is invisible until you see it. In standard cron, if you set both day-of-month and day-of-week, the job runs when *either* matches. 0 9 1 * 1 does not mean "the first Monday." The timeline renders two separate clusters of dots and the bug becomes obvious in one glance.
  • It's the same primitive you need in production. systemd's `list-timers` prints next fire times for timers, and most schedulers can answer "when is the next run?" A playground just makes that check instant, for any expression, before you paste it into a crontab.

Next runs are also how you answer the questions that actually come up in review: "is this every two hours from midnight, or every two hours from when I deploy?" Only one of those is what cron does, the timeline will show you which.

Four cron expression examples to type in

Grab the playground and walk through these four. They cover the patterns you'll write most: steps, daily schedules, weekdays, and month-specific runs.

*/5 * * * * — every five minutes

The classic health-check cadence: minute field stepped by five. The timeline shows runs at minute marks 0, 5, 10, 15, 20, and so on, every hour, all day. Change it to */15 for quarter-hour, then try 1-10/2 to see a step inside a range (minutes 1, 3, 5, 7, 9), the part of the syntax most people only discover by breaking something.

0 9 * * * — daily at 09:00

The backup-at-a-civilized-hour schedule. Minute 0, hour 9, everything else wildcarded: one dot per day on the timeline. Change the hour to 14 for 2 PM, or make it 30 9 for a 09:30 start. The timeline stays one dot per day no matter where you move the time, which is a good sanity check for how fields map to the timeline.

0 9 * * 1-5 — weekdays at 09:00

The work-hours schedule, and the first place people hit the weekday field. The timeline shows dots Monday through Friday and empty weekend gaps. Edit the range to 2-6 and watch the dots shift to Tuesday through Saturday - instant, visual proof that 1 is Monday in this dialect. Then try 0 9 * * MON-FRI and confirm the names match the numbers.

30 8 1 * * — 08:30 on the 1st

The monthly report. Day-of-month 1, everything else wildcarded: one dot per month at 08:30, with the long gaps between dots visible on the timeline. Now the experiment that justifies this whole article: add a weekday, 30 8 1 * 1. The timeline suddenly fills with weekly dots *plus* the 1st of every month, because day-of-month and day-of-week are OR'd in standard cron. That's not what anyone means by "the first Monday." The timeline just caught a bug you'd have shipped.

Common mistakes (and how the timeline exposes them)

Weekday numbering differs by dialect

The most common source of "why did this fire on the wrong day":

  • Vixie cron (Linux, macOS, BSD): 0–7, where 0 and 7 are both Sunday.
  • Quartz / Spring (Java): 1–7, where 1 is Sunday.
  • AWS EventBridge: 1–7, where 1 is Sunday.
  • [GitHub Actions](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions): 0–6, where 0 is Sunday.

0 0 7 * * means Sunday in Vixie cron and Saturday in Quartz. Everyone copies expressions between systems, the next-run timeline is the only cheap verification that the day is what you think it is.

Steps are anchored, not relative

*/2 in the hour field means hours 0, 2, 4, 6… even hours only, never 01:00, and not "every two hours from when the job was created." Steps always run from the lower bound of the field's range, so 30 */2 * * * fires at 00:30, 02:30, 04:30, and so on. If you need 01:30 and 03:30, write a list: 30 1,3,5,7,9,11,13,15,17,19,21,23 * * *.

The day-of-month / day-of-week OR trap

Dialects go deeper than weekdays

  • [Quartz](https://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html) uses six or seven fields (seconds first, optionally a year) and ? for "no specific value", 0 0 9 * * ? is valid Quartz and invalid in Vixie cron.
  • systemd timers don't use cron syntax at all; OnCalendar= has its own grammar.
  • Cloud schedulers (AWS EventBridge, GCP Cloud Scheduler) each carry their own quirks on top of the five fields.
  • Libraries like Python's croniter and JS's cron-parser are mostly Vixie-compatible, and "mostly" is exactly where the bugs live.

Whenever a job's schedule comes from a different stack than the box it runs on, verify with a cron expression tester that matches the target dialect.

Time zones and DST

The four examples as a crontab

This is what the four expressions look like in a real crontab. The same cadences that drive the automations in my self-hosted n8n setup and my release-tracking scripts. Run crontab -l to see yours:

crontab -l (excerpt)
# every 5 minutes
*/5 * * * *

# daily at 09:00
0 9 * * *

# weekdays (Mon-Fri) at 09:00
0 9 * * 1-5

# 08:30 on the 1st of each month
30 8 1 * *

Challenges to try yourself

If you've been typing along, you're ready for these. Build each one in the playground, read the explanation, and compare the timeline with your guess before peeking:

  1. Every 15 minutes between 9 AM and 5 PM, weekdays only. Hint: */15 in the minute field, a range in the hour field, a range in the weekday field. Answer: */15 9-17 * * 1-5.
  2. Every 2 hours at minute 30, but only on Sunday. Watch the numbering - Sunday is 0 in Vixie cron. Answer: 30 */2 * * 0.
  3. "Every 10 minutes starting at :05." */10 gives 0, 10, 20… the offset version needs a list. Answer: 5,15,25,35,45,55 * * * *.
  4. The impossible one: the last day of the month. Standard cron has no "L" for last day - that's a Quartz extension (0 0 8 L * *). Type 0 0 8 28-31 * into the playground and study the next month of runs: four dots in 31-day months, three in 30-day months, one in February. The last dot is the last day only by luck.

That last one is the real lesson: when the timeline shows a pattern you can't put into words, the expression is wrong - and when a need can't be expressed at all, it's time to change schedulers.

Keep this bookmarked

Next time you write a cron expression, do three things: type it into the playground, read the plain-English explanation out loud, and look at the next runs before you deploy. The explanation tells you what you asked for; the timeline tells you what you'll get. When they disagree, the timeline is right.

Got a cron expression that took you an hour to debug? Drop it in the comments! The weird ones are the best teaching material.

Until next time, keep your systems thoughtful.

No comments yet