Back to Blog

Building a cron expression — visual guide

April 16, 2026
7 min read
Written by ConvertTime Team
cronschedulingsysadminvisual-guide

A cron expression has 5 fields and looks intimidating: `/15 9-17 * 1-5`. The visual approach — picking values for each field independently — makes it manageable. Here's how to build one for the schedule you actually want.

For an interactive cron builder that does the work for you, see the cron builder.

The 5-field structure

Visualize cron as a table:

```
┌─── minute (0-59)
│ ┌─── hour (0-23)
│ │ ┌─── day of month (1-31)
│ │ │ ┌─── month (1-12)
│ │ │ │ ┌─── day of week (0-7, where 0 and 7 = Sunday)
│ │ │ │ │
*
```

To build an expression, pick values for each field independently. Most fields will be `*` (any value) for typical schedules.

Step-by-step examples

"Every weekday morning at 9 AM"

Step 1: minute. The job should run at minute 0 (top of the hour). Field: `0`.

Step 2: hour. The job should run at hour 9. Field: `9`.

Step 3: day of month. Run on any day of the month. Field: `*`.

Step 4: month. Run in any month. Field: `*`.

Step 5: day of week. Run on weekdays only — Monday (1) through Friday (5). Field: `1-5`.

Final expression: `0 9 1-5`

"Every 15 minutes during business hours, weekdays"

Step 1: minute. Every 15 minutes. Field: `*/15` (0, 15, 30, 45).

Step 2: hour. During 9 AM through 5 PM. Field: `9-17`.

Step 3: day of month. Any day. Field: `*`.

Step 4: month. Any month. Field: `*`.

Step 5: day of week. Weekdays only. Field: `1-5`.

Final expression: `/15 9-17 * 1-5`

This runs every 15 minutes between 9 AM and 5 PM, Monday through Friday. About 36 fires per weekday × 5 weekdays = 180 per week × 52 weeks = 9,360 per year.

"Once a quarter on the 1st"

Step 1: minute. Run at minute 0. Field: `0`.

Step 2: hour. Run at hour 0 (midnight). Field: `0`.

Step 3: day of month. Run on the 1st only. Field: `1`.

Step 4: month. Run in months 1, 4, 7, 10 (January, April, July, October — start of each quarter). Field: `1,4,7,10`.

Step 5: day of week. Any day of the week. Field: `*`.

Final expression: `0 0 1 1,4,7,10 *`

This runs at midnight on the 1st of January, April, July, and October.

"First Monday of every month"

Step 1: minute. Run at minute 0. Field: `0`.

Step 2: hour. Run at hour 0 (midnight). Field: `0`.

Step 3: day of month. Run on days 1-7 (the first 7 days of any month). Field: `1-7`.

Step 4: month. Run in any month. Field: `*`.

Step 5: day of week. Run on Monday only. Field: `1`.

Final expression: `0 0 1-7 * 1`

This runs at midnight if the day is in days 1-7 AND the day is a Monday — which means specifically the first Monday of the month.

"Every 30 seconds" (Quartz cron syntax only)

Standard Unix cron is minute-precision. For seconds, use a Quartz-compatible scheduler:

`0/30 ? *` (Quartz, 7-field format)

Or use a sleep-based wrapper for standard cron:

```bash

Run job every 30 seconds via cron + sleep


* /path/to/script.sh
* sleep 30 && /path/to/script.sh
```

Field-by-field details

Minute (0-59)

What it represents: at what minute of the hour should the job fire.

Common patterns:
- `0` — at the top of the hour (e.g., 9:00, 10:00)
- `30` — at half past the hour
- `*/5` — every 5 minutes
- `*/15` — every 15 minutes
- `0,30` — at 0 and 30
- `0-5` — minutes 0 through 5
- `5,20,35,50` — specific minutes

Hour (0-23)

What it represents: at what hour of the day.

Common patterns:
- `0` — at midnight
- `9` — at 9 AM
- `17` — at 5 PM
- `0-7` — early morning hours (midnight through 7 AM)
- `9-17` — business hours
- `*/2` — every 2 hours starting at 0

Day of month (1-31)

What it represents: on which day of the month.

Common patterns:
- `1` — on the 1st
- `15` — on the 15th
- `1,15` — 1st and 15th
- `1-7` — first 7 days
- `*/3` — every 3 days starting from day 1
- `28-31` — last days of the month (combine with day-of-week for "last business day")

Note: "31" doesn't fire if the month doesn't have 31 days (April, June, September, November have 30; February has 28 or 29).

Month (1-12)

What it represents: in which month.

Common patterns:
- `*` — any month (most common)
- `1` — January only
- `12` — December only
- `1,7` — January and July
- `4-9` — April through September

Day of week (0-7)

What it represents: on which day of the week.

Common patterns:
- `*` — any day
- `0` or `7` — Sunday
- `1` — Monday
- `1-5` — weekdays
- `0,6` — weekends (Saturday and Sunday)
- `1,3,5` — Monday, Wednesday, Friday

Standard Unix cron treats `0` and `7` both as Sunday for backward compatibility. Some schedulers use 1-7 starting Monday — verify yours.

The day-of-month and day-of-week interaction

This is the trickiest part. When BOTH day-of-month (3rd field) and day-of-week (5th field) are restricted (not `*`), what happens?

Standard Unix cron: OR. Run if either matches.

Quartz/Spring: AND. Run if both match.

For "first Monday of the month" using Unix cron, use the trick: restrict day-of-month to 1-7 AND day-of-week to 1.

`0 0 1-7 * 1` runs on day 1-7 AND it's a Monday. The day-of-month field becomes a "Monday in the first week" filter.

For Quartz, use `?` in one field:

`0 0 0 ? * 2` (Quartz, 7-field format)

Common mistakes

Forgetting day-of-week 0 = Sunday: thinking Monday is 0. It's 1.

Step starting at non-zero: `*/5` starts at 0 (0, 5, 10, 15, ...). To start at 5: `5/5` (in implementations that support starting offsets).

Hour 24 doesn't exist: hours go 0-23. There's no 24:00.

Confusing `/5` and `5`: `/5` is "every 5"; `5` is "exactly minute 5."

Misunderstanding `1-30/5`: this is "minutes 1, 6, 11, 16, 21, 26" — every 5 from a starting offset of 1, capped at 30.

Hardcoding times to a specific time zone: cron uses local server time. For UTC: set the server clock to UTC.

Step-by-step worked example

Let's build: "Every day at 9 AM and 9 PM, except weekends."

Goal: run twice daily on weekdays, at 9 AM and 9 PM.

Step 1: minute. Both 9 AM and 9 PM are at top of hour, so minute is `0`.

Step 2: hour. 9 AM is hour 9; 9 PM is hour 21. We need both: `9,21`.

Step 3: day of month. Any day. `*`.

Step 4: month. Any month. `*`.

Step 5: day of week. Weekdays only. `1-5`.

Final: `0 9,21 1-5`

This fires at 9 AM and 9 PM, every weekday.

More complex examples

"Every 30 minutes during workday hours, no weekends"

`/30 9-17 * 1-5`

"Once per day at 3:30 PM"

`30 15 *`

"First and last day of every month"

This is hard in pure cron because "last day" varies. One approach: use day 1 OR days 28-31 with a script that filters:

`0 0 1,28,29,30,31 ` then in your script, check if today is the actual last day.

"Daylight Saving Time-aware"

To avoid DST issues, set the cron daemon to UTC and use UTC times. For "9 AM Eastern":

- Winter (EST): 14:00 UTC
- Summer (EDT): 13:00 UTC

Either: pick one (e.g., 14:00 UTC, accepting the seasonal shift) or use an application-level scheduler that handles time zones explicitly.

Tips for production cron

1. Use UTC for daemon time zone. Predictable, no DST issues.

2. Document your expressions. A comment explaining what `/15 9-17 * 1-5` means saves future-you 5 minutes.

3. Validate before deploying. Tools like crontab.guru explain what an expression actually does. The cron builder is the visual equivalent.

4. Handle failures. Wrap your cron jobs to log failures and notify on errors.

5. Use a scheduler with retries. Vanilla cron has no retry. For mission-critical jobs, use Airflow, Dagster, or similar.

6. Avoid 2 AM if possible. Even if you don't observe DST, 2 AM is when most edge cases lurk.

FAQ

What's the easiest way to build a cron expression?

The cron builder — pick options visually and the expression is generated. Easier than writing manually.

What if I make a mistake?

Test before deploying. Use crontab.guru to plain-English-explain your expression, or the cron builder to verify what fires when.

Can I run cron jobs at second-precision?

Standard Unix cron is minute-precision. For seconds, use Quartz-compatible schedulers (Spring, some app-level cron).

How do I handle DST?

Run cron in UTC. Don't tie cron schedules to local time. Application code can handle local-time conversion when needed.

What if I need conditional logic (e.g., only run on Friday the 13th)?

Cron doesn't support conditions. Use a script that checks the date and exits if conditions don't match.

Can I run multiple jobs in the same cron file?

Yes. Each line is independent. Order doesn't matter; cron handles them in parallel as scheduled.

Bottom line

Build cron expressions by picking each field independently: minute, hour, day-of-month, month, day-of-week. Use `` for "any value" and specific values, ranges, or steps for restrictions. The day-of-month and day-of-week interaction is the trickiest part — be careful when both are non-``.

For visual cron building with instant verification, the cron builder does the work for you.

Share this article

Related Articles