Skip to content

Cron expressions, explained

Cron is the shorthand language people use to tell a scheduler when a job should run. Instead of writing "every weekday at 9:00 AM" in plain English, you write a compact pattern such as 0 9 1-5.

That compactness is useful and annoying at the same time. Once you know the format, cron feels quick. When you do not, every five-part string looks like a code puzzle, and small mistakes can make a job run at the wrong time or not at all.

This guide breaks cron down field by field, explains ranges, steps, and lists, calls out day-of-week quirks and timezone trouble, and walks through a worked example from plain-language schedule to expression.

What a cron expression is

A cron expression is a schedule pattern. A scheduler reads it and decides when to run the task attached to it.

The classic Unix cron format has five fields:

minute hour day-of-month month day-of-week

So this:

0 9 * * 1-5

means:

  • minute: 0
  • hour: 9
  • day of month: *
  • month: *
  • day of week: 1-5

In plain English: run at 9:00 every weekday.

Some systems add a seconds field at the front. Some add a year field at the end. That is one reason copied cron strings do not always behave the same way across environments. The exact format depends on the scheduler, not just the general idea of cron.

The five classic fields

Minute

The first field is minute, from 0 to 59.

Examples:

  • 0 means at minute zero
  • 30 means at minute thirty
  • */15 means every 15 minutes

Hour

The second field is hour, usually 0 through 23.

Examples:

  • 0 means midnight
  • 9 means 9 AM
  • 18 means 6 PM

Day of month

The third field is day of month, usually 1 through 31.

Examples:

  • 1 means the first day of the month
  • 15 means the fifteenth
  • * means any day of the month

Month

The fourth field is month, usually 1 through 12.

Examples:

  • 1 means January
  • 6-8 means June through August
  • * means every month

Many cron systems also accept month names such as JAN or OCT.

Day of week

The fifth field is day of week. This is the one that causes the most confusion.

Many cron systems treat:

  • 0 as Sunday
  • 1 as Monday
  • 6 as Saturday

Some also allow 7 for Sunday. Many allow names like MON, TUE, or FRI.

That sounds manageable until you move between systems or assume one interpretation from another tool. Day-of-week handling is a place where checking the specific scheduler docs pays off.

Wildcards, lists, ranges, and steps

Most cron power comes from four small pieces of syntax.

* wildcard

* means "any value" for that field.

0 means every hour on the hour because the minute is fixed at 0 and every other field is unrestricted.

Lists with commas

Commas let you name several values.

0 9,17 * means run at 9:00 and 17:00 every day.

Ranges with hyphens

Hyphens define a range.

0 9 1-5 means weekdays only.

Steps with slashes

Slashes define an interval.

/10 * means every ten minutes.

0 /2 means every two hours, on the hour.

These forms combine. For example:

*/15 9-17 * * 1-5

means every 15 minutes during the 9 AM through 5 PM hours, Monday through Friday.

The day-of-month and day-of-week trap

This is one of the classic cron surprises.

In many cron implementations, if both day-of-month and day-of-week are restricted, the scheduler may treat that as an OR, not an AND.

For example:

0 9 1 * 1

might mean:

  • run at 9:00 on the first day of every month
  • and at 9:00 every Monday

not "run only when the first day of the month is a Monday."

That catches people all the time. If you need truly specific calendar logic, cron may not be expressive enough by itself, or your scheduler may offer an extended syntax to help.

Timezone gotchas

Cron does not magically know what "9 AM for the business" means.

It runs in some timezone:

  • the server timezone
  • the container timezone
  • the app scheduler's configured timezone
  • a cloud scheduler's account or job timezone

If those differ from the people depending on the task, jobs fire at the wrong local time.

Daylight saving time adds another wrinkle. A job scheduled for a particular local hour may run differently on DST transition days. Some local times happen twice. Some never happen at all.

If the schedule matters to humans, always answer these questions explicitly:

3. What should happen across DST changes?

Most production scheduling bugs are not caused by cron syntax alone. They are caused by syntax plus timezone assumptions.

Common examples

Here are a few patterns you will see often:

0 * * * *      # every hour
*/5 * * * *    # every 5 minutes
0 0 * * *      # every day at midnight
0 9 * * 1-5    # weekdays at 9:00
30 2 * * 0     # Sundays at 2:30
0 1 1 * *      # first day of every month at 1:00

These are handy reference points, but always read them left to right and say them out loud. That habit catches mistakes faster than staring at the numbers.

A worked example: turn a human schedule into cron

Suppose the requirement is:

"Run this job every weekday at 9:30 AM in the scheduler's local timezone."

Break that into fields:

  • minute = 30
  • hour = 9
  • day of month = *
  • month = *
  • day of week = 1-5

So the expression is:

30 9 * * 1-5

Now say it back in English: at minute 30 of hour 9, every day of the month, every month, Monday through Friday.

Let us make it trickier:

"Run every 20 minutes between 8 AM and 6 PM on weekdays."

That becomes:

*/20 8-18 * * 1-5

That means every twentieth minute during hours 8 through 18, Monday through Friday.

But notice a subtle question: should the job run at 18:20, 18:40, and 18:59-window values inside that hour? In most cron systems, yes, because the whole 18th hour is included. If you meant "stop before 6 PM" rather than "include the 6 PM hour," the expression needs to change.

That is why speaking the result back in plain language is so useful. It exposes the difference between what you meant and what you actually wrote.

Try it in your browser

Our Cron Expression Parser is useful when you want a quick read on what an expression means before you trust it in a production job.

That is especially handy when:

  • you inherited a cron string from someone else
  • a job is firing at odd times
  • you are moving between schedulers with slightly different syntax
  • you want to check whether a range, list, or step says what you think it says

A parser will not fix timezone policy for you, but it can save you from simple syntax misunderstandings.

Common mistakes

Forgetting which field is which. Minute comes first, not hour.

Assuming every scheduler uses the same format. Some systems include seconds. Some support special extensions. Some do not.

Misreading day of week. 0, 7, and named weekdays are not handled exactly the same way everywhere.

Ignoring timezone configuration. The syntax can be perfect and the run time still wrong for the humans waiting on it.

Expecting AND behavior between day-of-month and day-of-week. Many cron systems do not work that way.

Using cron where calendar logic is more complex than the format wants to express. Cron is good at recurring schedules, not every calendar rule a business can dream up.

FAQ

Every minute. All five fields allow any value.

Because many cron systems treat restricted day-of-month and day-of-week fields as an OR condition.

No. It depends on the scheduler or host configuration. Sometimes it is server local time, sometimes UTC, sometimes a configured job timezone.

Sometimes with scheduler-specific extensions, but not reliably in plain classic five-field cron. That kind of rule often belongs in application logic or a richer scheduler.

In many systems they behave the same in a field that starts at zero, but support varies. If you want portability and clarity, test the exact scheduler you use.

Related guides