Cron expression cheat sheet: fields, steps, time zone traps

The five cron fields, ranges, lists, steps and names, the day-of-month OR day-of-week rule, ready-made schedules and the time zone and DST gotchas.

On this page

A cron expression has five fields separated by spaces: minute, hour, day of month, month and day of week. 0 9 * * 1-5 reads "at minute 0 of hour 9, on any day of the month, in any month, on Monday to Friday", which is 9:00 on weekdays. Each field can be a number, * for every value, a list like 1,15, a range like 9-17 or a step like */15. This cheat sheet lists the syntax, a set of ready-made schedules whose run times were calculated, the one rule that surprises almost everyone (day of month and day of week work as OR), and the time zone and daylight saving traps that make a job run at the wrong hour.

The five fields

PositionFieldAllowed valuesNames
1Minute0 to 59
2Hour0 to 23
3Day of month1 to 31
4Month1 to 12JAN to DEC
5Day of week0 to 7, where 0 and 7 are both SundaySUN to SAT

A sixth field in front of these is the seconds, in the layout of Spring and Quartz, so */10 * * * * * is "every 10 seconds". Classic cron has no seconds; its shortest interval is one minute.

The four special characters

CharacterMeaningExample
*Every value* * * * * runs every minute
,A list1,15 in the day field is the 1st and the 15th
-A range, inclusive9-17 in the hour field is 9 to 17
/A step*/15 is every 15th value; 10-30/5 is 10, 15, 20, 25, 30; 5/20 starts at 5 and repeats every 20

Names work in lists and ranges in any case: MON-FRI, jan,jul. These shortcuts replace a whole expression: @hourly is 0 * * * *, @daily and @midnight are 0 0 * * *, @weekly is 0 0 * * 0, @monthly is 0 0 1 * * and @yearly is 0 0 1 1 *.

Ready-made schedules

ExpressionIn words
* * * * *Every minute
*/5 * * * *Every 5 minutes
0 * * * *Every hour, on the hour
0 */6 * * *Every 6 hours: 00:00, 06:00, 12:00, 18:00
0 0 * * *Every day at midnight
0 9 * * 1-5At 9:00 on weekdays
0 9-17 * * 1-5On the hour from 9:00 to 17:00 on weekdays
15 10 * * MON,WED,FRIAt 10:15 on Monday, Wednesday and Friday
0 0 1,15 * *At midnight on the 1st and the 15th
0 0 * * 0At midnight every Sunday
0 0 1 * *At midnight on the 1st of every month
0 0 1 */3 *At midnight on 1 January, April, July and October
0 0 1 1 *At midnight on 1 January
0 0 29 2 *At midnight on 29 February only

Here is what some of these do on a real calendar, calculated for Ho Chi Minh City (UTC+7) from 10:00 on Tuesday 13 October 2026:

  • 0 9 * * 1-5 next runs on Wed 14, Thu 15 and Fri 16 October, then Mon 19 October at 09:00. Saturday and Sunday are skipped.
  • 0 0 1,15 * * runs on 15 October, 1 November, 15 November and 1 December, always at 00:00.
  • 0 0 29 2 * runs on 29 February 2028, then 29 February 2032.

Steps that do not divide evenly

A step is counted from the start of the field's range, and it begins again when the next cycle starts. */7 in the minute field means minutes 0, 7, 14, 21, 28, 35, 42, 49 and 56, and then 0 again at the top of the next hour. The gap between 56 and 00 is 4 minutes, not 7. A step runs evenly only when it divides the field: for minutes that is 1, 2, 3, 4, 5, 6, 10, 12, 15, 20 and 30. 5-59/15 gives 5, 20, 35 and 50. A true "every 7 minutes" cannot be written as one cron line: run the job every minute and let it check how long ago it last ran, or use a scheduler that supports plain intervals.

Day of month and day of week are OR

When both the day-of-month field and the day-of-week field are restricted, classic cron runs the job when either matches, not when both do. So 0 0 13 * 5 does not mean "Friday the 13th". It means "every 13th, and every Friday". In October 2026 it runs on the 2nd, 9th, 13th, 16th, 23rd and 30th.

Two ways to get what you meant:

  • Restrict only one field with the schedule and test the other one inside the job. For Friday the 13th, schedule 0 0 13 * * and make the script exit unless today is Friday.
  • To run on the last day of a month, schedule 0 0 28-31 * * and check inside the command whether tomorrow is the 1st. In a crontab, a percent sign must be written \%, as in [ "$(date -d tomorrow +\%d)" = "01" ] && /path/job.sh (GNU date).

In classic Vixie-style implementations, a field that starts with * counts as unrestricted for this rule, */2 included. So 0 0 */2 * 5 is not an OR: it means odd days of the month that are also a Friday. In 2026 that gives 9 and 23 October, then 13 and 27 November. Other schedulers, such as Quartz or the ones built into some frameworks, treat this differently or reject it, so always check the documentation of the one you use.

What classic cron does not have

The characters ?, L, W and # ("last day", "nearest weekday", "the 2nd Monday") and a year field belong to Quartz and Spring schedulers, not to a classic crontab.

Time zones

A cron expression has no time zone. The scheduler uses the zone of the machine, or a setting of the service. Servers are very often on UTC, so 0 9 * * 1-5 may mean 09:00 UTC, which is 16:00 in Ho Chi Minh City, not 09:00 local. Work out the zone first, then write the hour. Vietnam has no daylight saving time, so a job there never shifts, but a server in New York or London does.

Daylight saving time

On days when the clock changes, a local time can vanish or happen twice. For New York in 2026:

  • 30 2 * * * should run at 02:30 every day. On 8 March 2026 the clock goes from 01:59 straight to 03:00, so 02:30 does not exist. A scheduler that skips the missing time runs the job on 7 March and then on 9 March, with nothing on the 8th.
  • 30 1 * * * should run at 01:30. On 1 November 2026 the clock goes back from 02:00 to 01:00, so 01:30 happens twice, once in summer time and once in winter time. A scheduler can run it once or twice.

Real schedulers differ on what they do here, and the cron explainer's next-run list follows a stated rule: a non-existent time is skipped, and a time that happens twice runs once, at its first occurrence. The safe habits are to schedule important jobs in UTC, or at least outside the 00:00 to 03:00 window of your zone, and to make every job safe to run twice or to skip once. The article on Unix timestamps and time zones explains why the same instant has different clock readings.

Checking an expression with the tool

  1. Open the cron explainer and type or paste the expression. Presets such as "Every 5 minutes" and "Weekdays at 9:00" fill it in for you.
  2. Read the sentence under "In plain words" and the table "Field by field".
  3. Choose the Time zone your scheduler uses. The list "Next 10 runs" is calculated in that zone, with the real clock changes of your browser's time zone database. Compare it with what you expected, especially around the end of the month and at weekends.
  4. Read any note about the day rule or daylight saving before you deploy, and rewrite the expression with plain fields if the tool names an unsupported character.

Everything is calculated in your browser, and the expression is not sent anywhere. When you edit a crontab and want to see what changed, paste the old and new versions into the diff checker. To read the timestamps in a job's log, use the timestamp converter.

Common mistakes

  • *Using `/n where n does not divide the range**, as with */7`.
  • Expecting day of month and day of week to combine as AND.
  • Forgetting that a leading seconds field shifts every other field by one place.
  • Assuming the server uses your local time.
  • Relying on your shell environment. A job runs with a minimal environment, so use absolute paths.

A short checklist

Count five fields, or six with seconds. Check steps divide evenly. Remember that a restricted day of month and a restricted day of week mean "either". Know the time zone of the scheduler, avoid the hours around a clock change, and read the next ten run times before you save.

Tools mentioned in this article

Back to the blog