Cron Expression Parser
Explain a cron expression in plain English and list the next times it will actually run, in UTC or your own timezone — step values and weekday ranges included.
Every 15 minutes.
96 runs per day
Next runs
UTCCalculating…
Field by field
| Field | You wrote | Matches |
|---|---|---|
| minute0–59 | */15 | 0, 15, 30, 45 |
| hour0–23 | * | every value |
| day of month1–31 | * | every value |
| month1–12 or JAN–DEC | * | every value |
| day of week0–7 or SUN–SAT | * | every value |
Run times are computed in your browser from your own clock. UTC is what a server, a Kubernetes CronJob and most managed schedulers use unless told otherwise; Local is your machine’s timezone, your timezone.
About the Cron Expression Parser
Paste a cron expression and read what it actually does, in English, alongside the next eight times it will fire. Cron syntax is five fields of positional shorthand with no redundancy at all — one character in the wrong column turns a nightly backup into a job that runs every minute, and nothing about the expression itself will tell you.
The run list is the part that settles arguments. An explanation can be read the way you already believed the expression worked; a list of timestamps cannot. It is computed by walking the schedule forward from now, so `0 0 31 * *` visibly skips the months without a 31st, and `0 0 29 2 *` visibly waits for the next leap year.
Both the five-field crontab form and the six-field form used by Quartz, Spring and node-cron are accepted, and which one you pasted is inferred from the field count and stated back to you. Mixing them up is the most expensive cron mistake there is: the same five characters mean "every 15 minutes" in one dialect and "every 15 seconds" in the other.
- Plain-English description of any valid expression
- The next eight run times, in UTC or your own timezone
- Five-field and six-field (with seconds) expressions, with the dialect named
- Macros: @daily, @hourly, @weekly, @monthly, @yearly
- Field-by-field breakdown showing exactly which values each field expands to
- Errors that say which field is wrong and why, including a warning for the day-of-month/day-of-week rule
How to use it
- Type or paste your cron expression, or start from one of the presets.
- Read the description under the box, and the next runs on the left.
- Switch between UTC and Local. Servers, Kubernetes CronJobs and most managed schedulers use UTC unless configured otherwise.
- Check the field table if a value is not what you expected — it lists exactly what each field expanded to.
- Copy the run times if you need them for a ticket or a test.
Real-world use cases
DevOps & platform engineers
Sanity-check a Kubernetes CronJob's schedule field or a new crontab line before it merges — see the plain-English description and the next eight runs instead of trusting the field order by eye.
Backend developers
Work out why a scheduled job fired at 2:00 instead of 2:30, or fired twice in one hour — paste the exact expression from the codebase and compare the field breakdown against what you assumed each column meant.
SREs & on-call engineers
Translate an inherited crontab line during an incident, without a shell open on the box that runs it, and confirm whether the next run is minutes away or a day away.
Engineers migrating between schedulers
Check whether an expression written for Quartz or Spring's six-field dialect means something different — or is silently invalid — when it lands in a five-field Unix crontab, or the reverse.
QA & release engineers
Verify a batch job's schedule during a postmortem or a release checklist by generating the exact run list, rather than reasoning about `*/15` and day-of-week ORing from memory.
Examples
The one everybody writes
*/15 * * * *
Every 15 minutes — 96 runs per day
`*/15` in the minute field means 0, 15, 30 and 45. It is a step over the whole range, not "15 minutes after the job last finished".
Weekday mornings
30 9 * * 1-5
At 09:30 on Monday, Tuesday, Wednesday, Thursday and Friday
Day-of-week is 0–7 with both 0 and 7 meaning Sunday. Names work too: `MON-FRI` is the same expression and reads better in a repository.
The rule that catches everyone
0 0 1 * 1
At 00:00 on day 1st of the month or on Monday (cron matches either, not both)
When day-of-month and day-of-week are both restricted, cron ORs them. Every implementation since Vixie cron does this, and almost nobody expects it. If you want "the first Monday", cron cannot express it — schedule it daily and check the date in your job.
Six fields is a different dialect
*/15 * * * * *
Every 15 seconds
Quartz, Spring and node-cron put seconds first. Pasting a six-field expression into crontab, or a five-field one into Spring, is off by a factor of sixty in one direction or the other.
Common errors
| Message | Cause | Fix |
|---|---|---|
| A cron expression has 5 fields (or 6 with seconds); this has N | A missing or extra field — usually a five-field expression pasted into a scheduler that wants seconds, or the reverse. | Check which dialect your scheduler uses. crontab, Kubernetes and GitHub Actions take five; Quartz, Spring and node-cron take six. |
| "60" is not valid in the minute field | Minutes and seconds run 0–59, hours 0–23. There is no 60, and no 24. | Midnight is `0 0 * * *`. The off-by-one comes from counting the values rather than their range. |
| "22-5" runs backwards | A range that wraps around the end of the field. Cron ranges do not wrap. | Write it as two terms: `22-23,0-5`. |
| L, W or # is not implemented | Quartz extensions — last day of month, nearest weekday, nth weekday. They are not part of the crontab syntax this tool parses. | They work in Quartz and Spring but not in Unix cron, Kubernetes or GitHub Actions, so an expression using them is not portable in the first place. |
| This expression can never fire | A date that does not exist, such as `0 0 30 2 *` — 30 February. | Check the day and month together. Cron will accept the expression and simply never run it, which is why this is worth catching here. |
| The job runs at the wrong hour twice a year | Daylight saving. A job scheduled in a zone that observes DST is skipped when the clock jumps forward and runs twice when it goes back. | Schedule in UTC wherever you can. If the job must run at a local wall-clock time, make it idempotent so a double run is harmless. |
Frequently asked questions
›What do the five fields mean?
In order: minute (0–59), hour (0–23), day of month (1–31), month (1–12 or JAN–DEC), day of week (0–7 or SUN–SAT, where both 0 and 7 are Sunday). Each field takes `*` for every value, a list `1,15`, a range `1-5`, or a step `*/15` — and steps can be combined with ranges, as in `0-30/10`.
›Why does my job with both a date and a weekday run more often than I expected?
Because when day-of-month and day-of-week are both restricted, cron runs the job if *either* matches. `0 0 1 * 1` fires on the 1st of every month and on every Monday. This is documented behaviour going back to Vixie cron, and it is the single most common cron misunderstanding. If only one of the two fields is `*`, the other simply applies, which is why most expressions behave as expected.
›Is `*/15` the same as "every 15 minutes"?
In the minute field, yes: it expands to 0, 15, 30, 45, so the gap between runs is always 15 minutes. But a step divides the field's own range, so `*/7` in the minute field gives 0, 7, 14 … 56 and then jumps back to 0 — a four-minute gap across the hour boundary. Steps that do not divide their range evenly are never quite periodic.
›Which timezone does cron use?
Whatever the machine or scheduler is configured with. Unix cron uses the system timezone; Kubernetes CronJobs default to UTC; GitHub Actions is always UTC; managed schedulers vary. This tool shows both UTC and your browser's local zone so you can see the difference, and it computes both exactly rather than approximating.
›What do the @ macros expand to?
`@hourly` is `0 * * * *`, `@daily` and `@midnight` are `0 0 * * *`, `@weekly` is `0 0 * * 0`, `@monthly` is `0 0 1 * *`, and `@yearly`/`@annually` are `0 0 1 1 *`. There is also `@reboot`, which has no schedule at all — it runs once when the machine starts, so there are no future run times to compute.
›Are my expressions sent anywhere?
No. Parsing and the run-time calculation both happen in your browser, using your own clock. Nothing is transmitted, stored or logged.
›Does this support the Quartz L, W and # extensions?
No, and it says so rather than silently ignoring them. L (last day), W (nearest weekday) and # (nth weekday of the month) are Quartz and Spring extensions to the day-of-month and day-of-week fields, not part of the crontab grammar this tool parses. An expression using them will not run under Unix cron, Kubernetes CronJobs or GitHub Actions either, which is the more useful thing to know if you pasted one here expecting it to work everywhere.
›Can I use names like MON-FRI or JAN-DEC instead of numbers?
Yes, in the month and day-of-week fields. JAN through DEC and SUN through SAT (case-insensitive) work exactly like their numeric equivalents, including in ranges and lists — MON,WED,FRI is valid. The minute, hour and day-of-month fields are numeric only, since they have no names to use.
›What does @reboot do here, if there's no schedule to compute?
@reboot is accepted as a valid macro and described, but it produces no run-time list, because it isn't a schedule — it fires exactly once, when the machine or container starts, and never again on a timer. That is different from every other macro on this page, all of which expand to a normal five-field expression.
›How is this different from crontab.guru?
Both explain an expression in English. This tool adds the actual next eight timestamps computed against a real calendar — so `0 0 31 * *` visibly skips April, and a nonexistent date like 30 February visibly never runs — plus explicit dialect detection for the six-field Quartz/Spring form, and a field-by-field table showing exactly what each column expanded to rather than just a summary sentence.
Last updated