Cron Expression Builder
Build a cron expression and see the next times it fires, in any time zone. Flags the two cases that silently do the wrong thing: a time that does not exist on a daylight-saving day, and one that happens twice.
expression
What Vixie cron does, and what most systems do. A time that does not exist on a given date is skipped; a time that happens twice runs twice.
in words
At 02:30.
On every day.
Press Next runs to see when this fires. Nothing is computed until you do — and nothing is computed on the server either, because “now” is the one thing a static page cannot know.
examples
Runs in this browser tab, using this browser’s time zone data. No schedule is created and nothing is sent anywhere — the times below are computed here, from the fields you typed.
How to use this tool
A cron expression is five fields that say when something should run. It is small enough to learn in an afternoon and it has enough sharp edges to lose you an afternoon every few months, because none of the sharp edges are syntax errors — the expression is valid, the tool accepts it, and it does not do what you meant.
- 1Type the five fields, or pick one of the examples. The page says what it parsed and describes it in words.
- 2Set the time zone. A schedule is a wall-clock time somewhere in particular, and the zone is part of what it means.
- 3Press **Next runs**. Nothing is computed before you do, because "now" is the one thing a static web page cannot know.
The five fields
| Field | Range | Means |
|---|---|---|
| minute | 0-59 | Which minutes past the hour. |
| hour | 0-23 | Which hours of the day. |
| day of month | 1-31 | Which day of the month. |
| month | 1-12 or JAN-DEC | Which months. |
| day of week | 0-7 or SUN-SAT | Which days. Both 0 and 7 are Sunday. |
The order is minute first, which is the opposite of how a time is written, and it is the single most common source of a schedule that fires at the wrong time entirely. `30 2 * * *` is half past two in the morning. `2 30 * * *` is impossible and will be refused.
* every value */15 every 15th, counting from 0
a one value 5 minute 5
a-b a range 9-17 hours 9 to 17 inclusive
a-b/n a range, stepping 0-30/5 minutes 0, 5, 10 ... 30
a,b,c a list 0,30 minutes 0 and 30
? same as * - Quartz and several hosted schedulersRanges that run backwards, like `22-2` for hours, are the one case where implementations differ. Vixie cron treats the range as wrapping, so it means 10pm to 2am. This page does the same and says so in the words, rather than quietly returning nothing.
The one that catches everyone: day-of-month OR day-of-week
When **both** the day-of-month and the day-of-week fields are restricted, cron does not require both to match. It requires **either**. Every implementation does this, and it has done so for decades.
0 0 13 * FRI
You probably meant: the 13th, if it happens to be a Friday
It actually means: the 13th of every month, AND every FridayIn a month with five Fridays that is six runs, not one. The fix is to make one of the two fields a wildcard — `0 0 13 * *` for "the 13th" — or to accept that you meant both. This page implements the OR because that is what cron does, and says "either, not both" in the description, because a tool that silently "fixed" it would be generating a schedule that behaves differently from the one you are about to install.
Daylight saving, and the two answers
A cron schedule names a **wall-clock time** — 02:30, every day. Twice a year, in most of the world, 02:30 either does not happen or happens twice.
On a spring-forward day the clocks jump an hour forward, so the hour in between never arrives. On a fall-back day the clocks go back, so the hour before them arrives twice. Neither is an error in the expression. Both are a genuine question about what the job should do, and **cron implementations do not agree**.
| Vixie cron (and most systems) | The alternative | |
|---|---|---|
| A time that does not exist | Skipped. The job does not run that day. | Runs at the first moment that does exist. |
| A time that happens twice | Runs twice. | Runs twice. There is no third answer. |
So this page does not pick one silently. It defaults to **Vixie cron's behaviour**, because that is what most systems actually do, and it says so on the page rather than in a footnote. The alternative is one click away, and either way **every affected run is marked in the table**.
30 1 * * * in Europe/Lisbon
On the autumn day the clocks go back at 02:00, so 01:30 happens
twice - once at +01:00 and once at +00:00. The job runs twice.
The table marks both.
On the spring day the clocks go forward at 01:00, so 01:30
never happens. Vixie skips it. The "shift forward" option runs
it at 02:00 instead, and the table shows both times.The time zone is part of the schedule. `0 9 * * *` in `Europe/Lisbon` and in `America/New_York` are the same expression and different hours, and neither is wrong. This page defaults to UTC so the answer is the same for everyone reading it, and it tells you which zone it is showing you.
It also does not assume offsets are whole hours. Kathmandu is UTC+05:45 and Chatham is +12:45, and a scheduler that rounds to the hour is wrong in both — for Chatham, a 45-minute error twice a year when the clocks move.
29 February, and the century rule
`0 0 29 2 *` fires in leap years and not otherwise, which is occasionally useful and frequently a sign that you meant something else. The rule for what a leap year is has a part almost nobody remembers:
- Divisible by 4 — a leap year. 2024 is.
- **Unless** it is also divisible by 100. 1900 was not.
- **Unless** it is divisible by 400. 2000 was.
0 0 29 2 *
2028 2032 2036 2040 2044 2048 2052 2056 2060
2064 2068 2072 2076 2080 2084 2088 2092 2096
2100 <- divisible by 100 and not by 400, so no 29 February
2104 2108 2112 2116 2120 2124 2128 2132 2136The 2100 gap is the reason a schedule written to run on 29 February will sit dormant for four years and then skip a year, with no error anywhere. There is a test in this project's suite for exactly that.
What the fields do not say
**The seconds.** Cron has no seconds field. `*/5 * * * *` means every five minutes past the hour, not every five seconds. If you need seconds you need a different scheduler, or a wrapper — and a surprising number of people write a five-field expression expecting six.
**How long the job may take.** Cron starts jobs on schedule. It does not wait for the last one, so a job that takes longer than its interval will overlap itself, and you will find out from the log rather than from cron. Every implementation has added some protection and none of them is a substitute for an idempotent job.
**Whether it is running at all.** An expression is a request, not a schedule. Nothing happens until the expression is in a crontab on a machine that is awake, and this page does not create one.
Checking an expression you did not write
The order that finds problems fastest, and it is not top to bottom:
- **The zone.** Get this wrong and every other answer is wrong in a way that looks right.
- **The OR.** If both day fields are restricted, count the runs per month. More than you expected is the OR, not a bug.
- **The hour the clocks move.** Look at the next spring-forward and fall-forward dates and see what this expression does on them.
- **Then the fields**, left to right, against the table above.
This page is built to answer the first three: it shows the zone, it spells out the OR, and it marks every run that daylight saving touches.
Doing it yourself
On a machine, `crontab -e` and nothing else. To see what an expression does without installing it, and to read what the system itself thinks:
crontab -l # what is installed
crontab -e # edit it
# check the syntax without saving - varies by implementation
crontab /tmp/check.cron || echo "rejected"
# what the system's own parser makes of it, with no ambiguity
# about the OR and no ambiguity about the zone
systemd-analyze calendar '30 2 * * *'
# and run it under a different zone, which is the other thing
# people forget to check
TZ=America/New_York systemd-analyze calendar '30 2 * * *'In code, the same job in every language has a library, and the language's standard library is usually enough to check the arithmetic even if you do not want its parser. The two things worth doing yourself rather than trusting a library: know which DST policy you want, and know whether your day fields are about to be OR'd.
Frequently asked questions
- Why does 0 0 13 * FRI run more often than I expected?
- Because when both the day-of-month and day-of-week are restricted, cron requires either rather than both. It runs the 13th of every month and every Friday. Use 0 0 13 * * for the 13th alone, or accept both.
- What happens to a job scheduled during a daylight-saving change?
- It depends on the implementation, which is why this page asks. Vixie cron skips a time that does not exist and runs one that happens twice; the alternative here runs the missing one at the first moment that exists. Either way every affected run is marked in the table.
- Will my job run twice when the clocks go back?
- With the common implementations, yes - a time in the repeated hour runs twice, once at each offset. It happens once a year and is a genuine hazard for anything that charges, emails or locks. The table marks both runs so you can see it coming.
- What does 5/10 mean in the minute field?
- Minute 5, and then every 10 minutes from there to the end of the field - so 5, 15, 25, 35, 45, 55. People usually mean either 5 and 15, which is 5,15, or a range, which is 5-55/10. It is the same on every implementation, and it is not what it looks like.
- Why is there no seconds field?
- Cron has always been a minute-resolution scheduler, and adding seconds would break every existing crontab. For sub-minute scheduling you need something else - systemd timers, a language's own scheduler, or a queue with a delay.
- Does this page actually schedule anything?
- No. It parses the expression and computes when it would fire, in this browser. Nothing is created, sent or stored, and no crontab is installed. Reading an expression and running a job are different things, and this does the first.