Published July 2026 · 7 min read

Cron syntax,
by example.

Cron is five numbers and four symbols, and almost everyone learns it by copying a line that looked close enough. Two of its rules are genuinely counterintuitive, and both of them silently run your job at the wrong time rather than failing.

Fields 5, standard cron Day fields OR, not AND Timezone the machine, not you Step */n from the range start

The five fields

*  *  *  *  *
|  |  |  |  |
|  |  |  |  +-- day of week   0-7, 0 and 7 both Sunday
|  |  |  +----- month         1-12
|  |  +-------- day of month  1-31
|  +----------- hour          0-23
+-------------- minute        0-59

A sixth field for seconds exists in Quartz and in some schedulers, and is not standard cron. If a line has six fields, find out which flavour you are in before trusting it.

The day of month and day of week trap

This is the rule that catches everyone. If both day fields are restricted, cron runs when either matches. It is an OR, not an AND.

0 0 13 * 5    every 13th, AND every Friday.
              Not "Friday the 13th".

That line fires on the 13th of every month and on every Friday, which is about sixty times a year rather than the once or twice you wanted. To get a real AND you restrict one field in cron and check the other inside the job.

If only one of the two is restricted the behaviour is the obvious one, which is why the trap stays hidden until the first time someone uses both.

Steps, and what they count from

*/15 in the minute field means every 15 steps from the start of the range, so 0, 15, 30, 45. It does not mean "every 15 minutes from now".

The consequence shows up when the step does not divide the range evenly. */45 gives 0 and 45, then the hour restarts and it fires at 0 again. The gap is 45 minutes, then 15. If you want a genuine 45 minute cycle, cron is the wrong tool.

ExpressionFires
*/15 * * * *:00, :15, :30, :45
0 */3 * * *Midnight, 3am, 6am, and so on
0 9-17 * * 1-5Every hour, 9am to 5pm, weekdays
30 2 1 * *2:30am on the first of the month
0 0 * * 0Midnight on Sunday

Timezones and the twice yearly bug

Cron uses the machine timezone, which is usually UTC on a server and local time on a laptop. That difference alone accounts for a lot of jobs that appear to run at the wrong hour.

Worse, if the machine runs local time with daylight saving, the clock jumps. A job scheduled at 2:30am does not run at all on the spring forward day, because 2:30 never happens, and runs twice on the autumn day. Scheduling anything financial or idempotency sensitive between 1am and 3am local is asking for it.

!

Run servers in UTC and convert in your application. It removes the whole class of problem.

The shorthands

@daily, @hourly, @weekly, @monthly, @yearly and @reboot are supported by most implementations. They are clearer than the equivalent numbers and worth using where they fit. @reboot is the odd one out: it runs at startup, not on a schedule.

Things that are not standard cron

Copying one of those into a standard crontab produces a parse error at best, and a silently unscheduled job at worst.