Cron expression generator and explainer

Build a cron schedule from plain-language dropdowns, or paste an expression to get a readable explanation and the next five run times in both your local time zone and UTC. Everything is computed in your browser — nothing is uploaded.

Pick Custom… in any dropdown to type a raw field such as */5, 10-30/5 or MON-FRI. In the day-of-week field both 0 and 7 mean Sunday.

* * * * *

What this schedule means

Field-by-field breakdown
FieldValueMeaning

Next 5 runs

Upcoming run times
#Local timeUTCFrom now

Presets

Click any preset to load it into the builder.

Common schedules

Reference pages for the schedules people ask for most often. Each one shows the expression, what it means and when it fires next.

How it works

A standard cron expression is five fields separated by whitespace. Read left to right, they answer: at which minute, in which hour, on which day of the month, in which month, and on which day of the week should this job run.

*  *  *  *  *
|  |  |  |  |
|  |  |  |  +-- day of week   (0-7, 0 and 7 both mean Sunday)
|  |  |  +----- month         (1-12 or JAN-DEC)
|  |  +-------- day of month  (1-31)
|  +----------- hour          (0-23)
+-------------- minute        (0-59)

A job runs at a given minute only when every field matches that moment — with one famous exception covered below. The fields are not a countdown or an interval; they are a filter applied to the clock. That is why */20 * * * * fires at :00, :20 and :40 of every hour rather than "every 20 minutes starting whenever you saved the crontab".

The five fields and their allowed values

PositionFieldAllowed valuesNotes
1Minute0-59Smallest unit standard cron understands.
2Hour0-2324-hour clock. Midnight is 0, not 24.
3Day of month1-31Days that do not exist in a month are simply skipped.
4Month1-12Names also work: JANDEC.
5Day of week0-70 and 7 are both Sunday. Names work: SUNSAT.

Operators you can use in any field

OperatorExampleMeaning
** * * * *Every value of that field.
,0,15,30,45A list of specific values.
-9-17An inclusive range.
/*/5A step across the whole field: 0, 5, 10, …
- 10-30/5A step across a range: 10, 15, 20, 25, 30.
@@dailyShorthand for 0 0 * * *. Also @hourly, @weekly, @monthly, @yearly.

How the next run times are calculated

This tool does not guess. It parses each field into the exact set of values it matches, then walks forward one calendar day at a time from the current minute, testing the month and day rules for each day and expanding the matching hours and minutes in order. That approach handles leap years, months of different lengths and unsatisfiable expressions correctly.

The search stops after roughly four years. If an expression such as 0 0 30 2 * (30 February) can never match, the tool reports that there are no upcoming runs instead of freezing. Local times are rendered using your device time zone; UTC is shown alongside because most servers and most hosted schedulers run on UTC.

Worked examples

1. Quarter-hourly during office hours on weekdays

*/15 9-17 * * 1-5

Minute steps by 15 (:00, :15, :30, :45), the hour is restricted to the range 9 through 17, and the day of week is Monday through Friday. That is 36 runs per working day.

The trap here is the hour range. 9-17 includes hour 17 in its entirety, so the last run of the day is 17:45, not 17:00. If you want the final run to be exactly 17:00, use 0 17 * * 1-5 as a separate line, or narrow the range to 9-16 and accept that nothing fires after 16:45.

2. Monthly versus quarterly

0 0 1 * * runs at midnight on the 1st of every month. 0 0 1 */3 * runs at midnight on the 1st of every third month.

Steps in the month field are anchored to the start of the field, not to today. */3 expands to months 1, 4, 7 and 10 — January, April, July and October — regardless of when you install the crontab. If your fiscal quarters start in February you need 2,5,8,11 instead. The same anchoring applies everywhere: */7 in the day-of-month field means days 1, 8, 15, 22 and 29, which is not a stable weekly cadence because the count restarts each month.

3. The day-of-month / day-of-week OR rule

0 12 13 * 5

Read literally this looks like "noon on the 13th, but only if it is a Friday". It is not. When both the day-of-month field and the day-of-week field are restricted, cron combines them with OR: the job runs at noon on the 13th of any month and on every Friday. In a typical year that is more than sixty runs, not one or two.

To actually get "Friday the 13th only" you cannot use a single standard cron expression. The usual workaround is to schedule 0 12 13 * * and start the command with a guard such as [ "$(date +\%u)" = 5 ] && /path/to/job. Note the escaped percent sign: in a crontab an unescaped % is turned into a newline and everything after it is fed to the command on standard input.

4. The step-wraparound trap

*/45 * * * *

This does not run every 45 minutes. The minute field only counts from 0 to 59, so */45 expands to minutes 0 and 45. The job fires at 00:00 and 00:45, then again at 01:00 — a gap of fifteen minutes, not forty-five.

Any interval that does not divide evenly into 60 minutes (or into 24 hours) will behave like this. For a genuine 90-minute cadence you need to enumerate the times yourself, for example 0 0,3,6,9,12,15,18,21 * * * combined with 30 1,4,7,10,13,16,19,22 * * *, or move the interval logic into the job itself.

5. A nightly job and daylight saving time

0 2 * * *

On a server set to UTC this is unremarkable: one run per day, 24 hours apart, forever. On a server set to a zone that observes daylight saving time, 02:00 is exactly the hour that disappears in spring and repeats in autumn. Depending on the cron implementation the job may be skipped on the spring transition day, or run twice on the autumn one.

The reliable fix is to run schedulers in UTC and convert at the edges, or to pick an hour that no common time zone shifts across, such as 04:00 or 05:00. Vixie cron on Linux has special handling for jobs in the skipped hour; container schedulers and cloud cron services usually do not.

Frequently asked questions

What do the five fields actually mean?

In order: minute (0–59), hour (0–23), day of month (1–31), month (1–12) and day of week (0–7). A run happens at any minute where all the fields are satisfied. There is no seconds field in standard cron and no year field — those belong to Quartz and Spring, which use six or seven fields.

If I restrict both day-of-month and day-of-week, are they AND-ed or OR-ed?

OR-ed. This is the single most misunderstood rule in cron. When either of the two day fields is *, the other one simply filters the days as you would expect. But when neither is *, the daemon runs the job on any day that matches the day-of-month field or the day-of-week field.

So 0 0 1 * 1 is not "the 1st, if it is a Monday". It is "the 1st of every month, plus every Monday". If you need both conditions to hold, use a wildcard in one field and test the other condition inside the command.

One subtlety: a field beginning with * counts as unrestricted for the purposes of this rule, including */2. That matches the behaviour of Vixie cron, the implementation behind cron on most Linux distributions, and this tool follows it.

Is Sunday 0 or 7?

Both. Standard cron accepts 0 through 7 in the day-of-week field and treats 0 and 7 as Sunday, which makes ranges such as 5-7 (Friday, Saturday, Sunday) expressible. Monday is always 1. Some non-cron schedulers — notably Quartz — use 1 for Sunday and 7 for Saturday, so double-check before copying an expression between systems.

What is the difference between 5 * * * * and */5 * * * *?

5 * * * * runs once an hour, at five minutes past the hour — 24 runs a day. */5 * * * * runs every five minutes, at :00, :05, :10 and so on — 288 runs a day. The slash introduces a step; a bare number is a single value. Mixing them up is the most common cause of a job that runs 12 times more often than intended.

Which time zone does cron use?

The system time zone of the machine running the daemon, unless the crontab sets CRON_TZ (Vixie cron) or TZ at the top of the file. Managed schedulers vary: GitHub Actions schedule triggers are always UTC, Kubernetes CronJob defaults to the controller time zone but accepts a timeZone field, and most cloud schedulers let you pick a zone explicitly. The tool above shows both your local time and UTC so you can check which one your expression really targets.

What happens to my job during a daylight saving change?

It depends on the implementation. Vixie cron treats jobs scheduled in the hour that is skipped in spring as due immediately after the jump, and suppresses duplicate runs in the repeated autumn hour for fixed-time jobs. Interval jobs such as */10 * * * * simply follow the wall clock, so you get one short hour and one long one. Container and cloud schedulers frequently have no special handling at all. Running everything in UTC removes the problem entirely.

Can I schedule something every 30 seconds?

Not with standard five-field cron — one minute is the finest granularity. The classic workaround is a single crontab line that runs the command, sleeps and runs it again: * * * * * /path/to/job; sleep 30; /path/to/job. Cleaner alternatives are systemd timers with OnUnitActiveSec=30s, or a long-running process with its own internal loop. Quartz and Spring cron expressions do have a seconds field, but they are not compatible with system cron.

Why does my cron job never run?

In rough order of likelihood:

  • Environment. Cron runs with a minimal environment and a short PATH (often just /usr/bin:/bin). Use absolute paths for every binary and every file.
  • Unescaped percent signs. In a crontab, % becomes a newline. Any date +%Y must be written date +\%Y.
  • No trailing newline. Some cron implementations silently ignore the last line of a crontab file if it does not end with a newline.
  • Wrong crontab. A job added with crontab -e as your user is not the same as /etc/crontab or a file in /etc/cron.d, and the system files need an extra user column.
  • Output going nowhere. Redirect to a file (>> /var/log/myjob.log 2>&1) so failures are visible, then check journalctl -u cron or /var/log/syslog.
Does this tool support Quartz, Spring or six-field expressions?

No. It deliberately implements standard five-field Vixie/POSIX cron, which is what Linux crontabs, Kubernetes CronJob, GitHub Actions and most cloud schedulers use. If you paste a six- or seven-field expression, or one containing Quartz specials such as L, W or #, you will get an explicit error rather than a wrong answer. The Quartz-style ? placeholder is accepted and treated as *, since that is what it means in practice.

What does "no upcoming runs" mean?

The expression is syntactically valid but can never match a real date within the four-year search window. The classic case is 0 0 30 2 * — there is no 30 February. 0 0 31 4 * is another: April has 30 days. Note that a valid but rare expression such as 0 0 29 2 * (29 February) does have runs, just infrequent ones, so you may see fewer than five results rather than none.

Are my expressions sent anywhere?

No. The parser, the explainer and the next-run calculation are all plain JavaScript running in your browser. There is no API call and no server-side processing, so the expression you paste in is never uploaded or logged anywhere. You can save the page and use it offline, and the tool will keep working.

How do I write a schedule for the last day of the month?

Standard cron cannot express it — there is no L token. The usual pattern is to run the job on the 28th through the 31st and let the command decide: 0 3 28-31 * * [ "$(date -d tomorrow +\%d)" = "01" ] && /path/to/job. The guard is true only on whichever of those days is actually the last one. Quartz users can write 0 0 3 L * ? instead.

Use cases and limitations

What people schedule with cron

  • Backups and snapshots. Nightly database dumps, weekly full backups, monthly archive rotation. Usually run in the small hours in the server time zone.
  • Certificate renewal. ACME clients typically want a twice-daily check at a random minute so that renewals do not stampede the certificate authority on the hour.
  • Cache warming and index rebuilds. Frequent short jobs, often */5 or */15, restricted to business hours to save resources overnight.
  • Report generation and billing runs. Monthly or quarterly schedules pinned to a specific day of the month, such as 0 6 1 * *.
  • CI and repository automation. GitHub Actions, GitLab CI and most build systems accept five-field cron in a schedule block, always interpreted as UTC.
  • Kubernetes CronJob. Same syntax, plus a timeZone field and concurrency policies that plain cron does not have.

What cron cannot do

  • Sub-minute scheduling. One minute is the floor.
  • Relative intervals. "Every 90 minutes" and "every 45 minutes" cannot be expressed because steps are anchored to the start of the field and reset at its boundary.
  • Last day of month, nth weekday, business-day arithmetic. These require Quartz syntax or a guard inside the command.
  • Catch-up after downtime. If the machine is asleep or the daemon is stopped, the run is simply missed. anacron exists for exactly this reason on laptops and workstations.
  • Overlap protection. If a run takes longer than the interval, cron happily starts another one. Wrap long jobs in flock or an equivalent lock.

Limitations of this tool

The run times shown here are a simulation of what a correct cron implementation would do, computed against your browser clock and time zone database. They are a planning aid, not a guarantee: your daemon's time zone, its daylight-saving behaviour, missed runs during downtime and any scheduler-specific extensions can all shift reality. The search window is capped at about four years, so very sparse schedules may return fewer than five results. Quartz, Spring and other non-standard dialects are intentionally out of scope.