DEV Community

jiebang-tools
jiebang-tools

Posted on

Cron Expressions Demystified: A Practical Guide for Developers

If you've ever set up a scheduled job, you've probably encountered cron expressions. They look cryptic at first glance — 0 30 9 * * MON-FRI — but once you understand the pattern, they're surprisingly logical.

This guide breaks down cron syntax from scratch, covers common gotchas, and shows you how to test your expressions before putting them in production.

The Five Fields

A standard cron expression has five fields separated by spaces:

┌───────────── minute (059)
│ ┌───────────── hour (023)
│ │ ┌───────────── day of month (131)
│ │ │ ┌───────────── month (112)
│ │ │ │ ┌───────────── day of week (07, where 0 and 7 are Sunday)
│ │ │ │ │
* * * * *
Enter fullscreen mode Exit fullscreen mode

Each field accepts:

  • Exact values: 5 (at minute 5)
  • Ranges: 1-15 (from 1 to 15)
  • Steps: */5 (every 5 units)
  • Lists: 1,15,30 (at 1, 15, and 30)
  • Asterisk: * (every possible value)

Common Patterns

Expression Meaning
* * * * * Every minute
*/5 * * * * Every 5 minutes
0 * * * * Every hour, on the hour
0 9 * * * Every day at 9:00 AM
0 9 * * MON Every Monday at 9:00 AM
0 0 1 * * First day of every month at midnight
0 0 1 1 * January 1st at midnight (yearly)
30 4 * * SUN Every Sunday at 4:30 AM

The Step Operator Gotcha

*/15 in the minute field means "every 15th minute starting from 0" — so it fires at 0, 15, 30, 45. It does not mean "15 minutes after the last run."

This matters when you combine steps with ranges:

  • 0-30/15 * * * * → fires at minutes 0, 15, 30
  • */15 * * * * → fires at minutes 0, 15, 30, 45

Day-of-Month vs Day-of-Week: The OR Logic

This trips up a lot of people. When you specify both day-of-month and day-of-week, cron uses OR logic, not AND.

0 9 15 * FRI means "9 AM on the 15th OR every Friday," not "9 AM on Friday the 15th."

If you need AND logic, you have to add a conditional check inside your script:

0 9 15 * * [ "$(date +\%u)" = "5" ] && your_command
Enter fullscreen mode Exit fullscreen mode

Timezone Awareness

Cron runs in the system's local timezone by default. In containerized environments, this is often UTC. If your cron job needs to fire at 9 AM Shanghai time (UTC+8), you'd write 0 1 * * * in UTC.

Pro tip: Always document the timezone your cron expressions are set in. Future-you will thank present-you.

Testing Your Expressions

Before deploying a cron job, always verify the next few run times. You can use an online cron expression parser to quickly see when your expression will fire — no mental math required.

This is especially useful for complex expressions like 0 9 1-7 * MON (first Monday of every month... or is it? Remember the OR logic!)

6-Field vs 5-Field Cron

Some systems (like Spring's @Scheduled or AWS EventBridge) use a 6-field format with seconds:

┌──────── seconds (059)
│ ┌──────── minute (059)
│ │ ┌──────── hour (023)
│ │ │ ┌──────── day of month (131)
│ │ │ │ ┌──────── month (112)
│ │ │ │ │ ┌──────── day of week (07)
│ │ │ │ │ │
* * * * * *
Enter fullscreen mode Exit fullscreen mode

AWS EventBridge also uses ? for "no specific value" in either day-of-month or day-of-week (you must use ? in one of them when the other is specified). Make sure you know which format your system expects.

Quick Reference Cheat Sheet

# Every 10 minutes
*/10 * * * *

# Every weekday at 9:30 AM
30 9 * * MON-FRI

# First day of each quarter at midnight
0 0 1 1,4,7,10 *

# Every 6 hours
0 */6 * * *

# Last day of month (requires script support)
0 0 28-31 * * [ "$(date +\%d -d tomorrow)" = "01" ] && your_command
Enter fullscreen mode Exit fullscreen mode

Final Tips

  1. Start simple. Get your schedule working with a basic expression first, then add complexity.
  2. Always test. Parse the next 5–10 fire times before committing.
  3. Document your intent. A comment like # Runs every Monday at 9 AM Shanghai time (1 AM UTC) prevents misinterpretation.
  4. Watch for DST. Daylight saving time shifts can cause jobs to skip or fire twice.
  5. Use locks. If a job might overlap with itself, use a lock file or distributed lock to prevent concurrent runs.

Cron expressions aren't hard — they're just unfamiliar. Once you internalize the five-field pattern and the OR logic between day fields, you'll rarely need to look up the syntax again.

If you want to quickly validate a cron expression and see the next run times, try the free Cron Expression Parser — paste your expression and get instant results.

Top comments (0)