Cron Expression Evaluator

Paste a schedule you already have · plain English, every matched value, and the next 10 real run times

What each field matches

#FieldYou wroteMeaningIt matches
No expression evaluated yet. Paste one above, or pick an example.

Next 10 run times

#In your selected timezoneUTCISO 8601
No run times yet. Evaluate an expression first.

The same schedule on other platforms

What This Tool Answers That a Chatbot Cannot

Ask a chatbot what 0 9 * * 1-5 means and you will usually get something plausible. Plausible is not the same as right. Cron syntax hides three decisions inside five short fields: whether the fourth and fifth fields combine with OR or AND, whether the weekday is numbered from zero or from one, and which clock the schedule is read against. Those three answers change what the job actually does, and they differ between the Unix cron on a server, Quartz inside Java, and the six-field dialect that GitHub Actions accepts. A chatbot has one set of crontab syntax in its weights and no way to tell which one you are asking about, so it fills the gap with the most common one and answers without flagging the guess.

This tool does the opposite. It parses the expression you actually pasted, tells you which dialect it detected, walks each field one at a time and shows the concrete set of values that field produces, and then shows you the next ten real run times computed in the timezone you choose. When a field carries a trap, the trap is named and explained rather than passed over in silence. The goal is not a sentence of English about your schedule. The goal is that you can look at the output, agree with it, and know it was not guessed.

The Five Fields, One at a Time

A cron expression is a row of numbers, and the leftmost field moves fastest. Position decides what a number means, which is why the same digit is 9 o'clock in one column and 9 January in the next. Reading the row right to left answers the question that people actually have, which is how often does this run and at what time.

Minute runs first and cycles through 0 to 59, so it is the field that controls frequency inside an hour. Hour runs second, 0 to 23, and sets the clock time of day. Day of month is third, 1 to 31, and is the calendar date. Month is fourth, 1 to 12, and can be written as JAN through DEC. Day of week is last, and here is the first trap: it is numbered 0 to 6 in Unix cron with 0 meaning Sunday, but 1 to 7 in Quartz with 1 meaning Sunday. The expression 0 0 * * 1 means Sunday midnight on a Unix server and Monday midnight inside a Quartz scheduler. Both are valid. Both are commonly written by people who read documentation from the other dialect.

Quartz adds a seconds field in front of minute and sometimes a year field at the end, giving six or seven fields. Six fields starting with a number are far more likely to be Quartz with seconds than Unix, because Unix cron never had six. GitHub Actions, AWS EventBridge and Kubernetes CronJobs each accept a different subset of that, which is why the same job scheduled in two places so often fires at different minutes.

The Traps This Tool Looks For

Most of the damage done by cron comes from six specific mistakes. Each one parses cleanly, so nothing errors and nothing warns you. This tool looks for all six and explains what the expression will actually do.

The first is the AND versus OR rule. When both day of month and day of week are restricted, most implementations run the job when either one matches, not when both match. 0 0 13 * 5 does not mean midnight on Friday the 13th. It means midnight on the 13th of every month, and also at midnight on every Friday. You almost never want that, and the fix is to leave one of the two fields as a star.

The second is step anchoring. */15 in the minute field means 0, 15, 30 and 45, because the step counts from the bottom of the range and not from the current minute. 5/15 means 5, 20, 35 and 50, because the range starts at 5. People who assume the step runs from the moment the job last fired get a different schedule than the one they wrote.

The third is a day that never arrives. 0 0 30 2 * asks for February 30th, which does not exist. The expression is perfectly valid syntax, the scheduler accepts it, and the job silently never runs. Day 29 in February is the same trap one year in four.

The fourth is the six field assumption. A schedule moved from Quartz to a five field scheduler keeps its seconds value and shifts every field left by one, so 0 30 9 * * * that meant 09:30:00 now means 09:09, 30 minutes, and the month field is whatever the weekday used to be.

The fifth is timezone. Cron expressions carry no timezone. The scheduler reads them in whatever the machine is set to, so the same file behaves differently on a server configured for UTC and on a laptop set to Europe/Berlin. When daylight saving moves the clock, a job scheduled at 02:30 either runs twice, skips an hour, or lands an hour out of place, and which of the three happens depends on the zone rather than on your expression.

The sixth is assuming a skipped run is caught up. Most schedulers simply start the next occurrence. A job that missed because the machine was down at the time does not run late when it comes back. If the job must eventually happen, that has to be built into the job itself.

Timezone: the Field Nobody Writes Down

Because the expression itself is timezone free, the only honest way to answer when a job runs next is to say in which clock. The default here is the browser's own timezone, which is usually the one you are thinking in. The selector changes it.

Run times are computed as wall clock time in the selected zone and then converted to a real instant, so the daylight saving answer is the correct one rather than an approximation. A schedule at 02:30 on a day when clocks go forward has no 02:30 that year, and you will see the run land at the first real time that exists instead of being told something that never happens. The UTC column is there because a great many servers, most hosted cron services and every GitHub Actions schedule run in UTC regardless of what the machine believes.

If you paste a time such as Europe/Berlin or America/New_York, the tool uses that zone. An unrecognised value falls back to UTC and says so rather than quietly using something else.

Porting a Schedule Between Systems

The same schedule written for four different platforms. GitHub Actions takes a six field expression with seconds first, always reads it in UTC, and has no concept of the Quartz extensions. AWS EventBridge also takes six fields with seconds first and is always UTC. Kubernetes CronJob takes five fields in the local time of the cluster, which is usually UTC in practice and not always. Quartz takes six or seven and adds the question mark, L, W and hash operators.

Converting between them is mechanical once the expression has been parsed, because each platform only changes which fields exist and what the clock is. What is not mechanical is the part everyone gets wrong: GitHub Actions silently drops a schedule that runs more often than once every five minutes, and it does not tell you. That limit is why a converted six field expression with a step under five in the minute field is flagged here rather than handed over as a working answer.

How It Works

Everything runs in this page. The expression is split on whitespace, each field is expanded into the concrete list of values it matches, and the next ten runs are found by stepping forward through the calendar in the selected zone until ten matching wall clock times have been collected. Nothing is uploaded, nothing is stored and no request leaves your browser once the page has loaded.

The plain English summary is generated by cRonstrue, a small open source parser loaded from a public CDN. If that library cannot be reached the rest of the tool still works, because the field table, the next run times and the platform conversions are all computed here and do not depend on it. Only the sentence changes.

What is the difference between a cron generator and a cron evaluator?

A cron evaluator reads an expression you already have. It is the tool you reach for when a schedule is inherited from a crontab file, copied out of a docker image, or written by whoever set the system up before you, and nobody can say what it does. It answers that question by parsing the expression field by field, showing the exact set of times it matches, and listing the next ten real run times in the timezone you pick. Most cron generators start from a form where you choose a time and hand you the string, which is the opposite direction of travel and does not help when the string is the thing you were given.

Why does the same cron expression mean different things on different servers?

Because there is no single cron standard, there are several dialects that all call themselves cron. The original Vixie cron on Linux, BSD and macOS uses five fields and numbers the weekday from zero with Sunday as both zero and seven. Quartz, which ships inside Java and in Spring's scheduling annotations, adds a seconds field in front and a year field at the end, and numbers the weekday from one. Between them sit the hosted schedulers, where AWS EventBridge takes six fields and always runs in UTC, GitHub Actions takes six fields and always runs in UTC, and Kubernetes CronJob takes five fields in the cluster's local time. The syntax looks close enough between these that an expression written for one is usually accepted by another and behaves differently. When the parser here sees a six field expression it assumes Quartz with seconds and tells you that is what it assumed.

Why does the day of month and day of week rule matter?

When both fields are restricted to something other than a star, the standard implementations run the job when either field matches, not when both match. The two are combined with OR. That is why 0 0 13 * 5, which looks like Friday the 13th, actually fires at midnight on the 13th of every month and at midnight on every Friday, so thirteen times a month in a normal one. If you want the intersection, the usual workaround is to check the other field inside the job itself, because the scheduler has no AND mode. This behaviour is not universal. Vixie cron, Quartz and systemd all use OR, but the specification language in the original cron documentation is ambiguous enough that some implementations and some hosted schedulers differ, which is another reason not to rely on the combination for anything important.

Why is 0 0 30 2 * a valid cron expression that never runs?

Because the parser checks the range of each field, not the calendar. 30 is inside the legal range for day of month, which is 1 to 31, and 2 is inside the legal range for month, so nothing objects. But no month ever has thirty days, so the set of matching dates is empty and the job never fires. The same trap catches day 31 in a thirty day month, and day 29 in February in any year that is not a leap year. Many schedulers treat a schedule that has not matched yet as an error, and some accept it and stay silent. This tool reports the empty match set explicitly instead of showing you ten runs that are never going to happen.

Does this tool run my cron job?

No, and it is not connected to anything. It reads a string and does arithmetic. Nothing you paste is sent anywhere after the page loads, there is no account, and nothing is stored. If you want the next runs for a schedule that needs seconds precision or one of the Quartz extensions, this tool will show you the runs it can compute and name the ones it cannot, rather than printing a plausible answer it did not calculate.

What does the star slash step notation actually do?

A step counts from the bottom of the field's range, not from the last time the job ran and not from the current minute. In the minute field, */15 matches 0, 15, 30 and 45 because the range starts at zero. 5/15 matches 5, 20, 35 and 50 because the range starts at five. */2 matches every minute that is even, which is every other minute rather than every two minutes counted from whenever you happened to look. The same anchoring applies in every field, so */2 in the hour field matches 0, 2, 4 and so on through 22, and a range like 10-40/10 in the hour field matches 10, 20, 30 and 40. The field table shows the expanded list rather than asking you to redo the arithmetic in your head.

Which timezone is used?

The browser's own timezone by default, which is normally the one you are thinking in. The selector overrides it with any IANA timezone name, such as Europe/Berlin or America/New_York. Run times are computed as wall clock time in that zone and converted to a real instant, so a schedule that lands on a daylight saving jump shows the first time that actually exists. The UTC column is shown next to every run because hosted schedulers and GitHub Actions interpret schedules in UTC no matter what the machine thinks, and that is the single most common reason a job fires at the wrong hour.

What does an asterisk mean?

An asterisk means every value in that field's range, so * * * * * runs every minute of every day. Paired with a slash it becomes the step form, */5 in the minute field runs every five minutes. Paired with a range it becomes 9-17, which limits that field to nine through seventeen. The whole expression is usually read as a conjunction: every one of these conditions has to be true at the same moment for the job to fire, and the job fires again when the next such moment arrives.

How do I schedule a job for the last Friday of the month?

Both operators live in the day of week field, so the day of month in front of them stays a star. On a five field Unix expression Friday is 5, so the last Friday is 0 0 * * 5L and the third is 0 0 * * 5#3. The same job in a six field Quartz expression moves to 6, because Quartz counts Sunday as 1, and becomes 0 0 0 * * 6L. Unix cron supports neither, so the portable approach is to run on every Friday and let the job itself check the date, or to run daily and exit early. Cron on this site will describe both forms and show the matching days rather than pretending a plain 5 will land on the last Friday.

Pairs Well With

Comments & Ratings

Be the first to comment.

References