Next run timesPer-field validation5-field crontab dialect

Cron Expression Generator & Tester

Build a 5-field cron expression, read it back in plain English, and check the next six run times before you deploy it. All processing happens locally in your browser.

5-field crontab dialect

Build a cron expression and see when it will actually run

Edit the whole expression or one field at a time. Each field is validated separately, and the next six run times are calculated so you can confirm the schedule before deploying it.

Minutes matched per hour1
Hours matched per day1
Runs per dayVaries by date
Approx. runs per year261
ExpressionFull cron expression
Field by field
Which minute of the hour.
Which hour of the day, on a 24-hour clock.
Which calendar day. Note that 31 is skipped in short months.
Which month.
Which weekday. 0 is Sunday; 7 is also accepted for Sunday.

In plain English: At 02:30 AM, Monday through Friday.

Next runsUpcoming run times in your local zone

Calculating run times…

Presets

What is a cron expression?

A cron expression is five values separated by spaces. Each value answers one question about when, and a job runs at every minute where all five answers are satisfied.

┌───────────── minute        (0 – 59)
│ ┌─────────── hour          (0 – 23)
│ │ ┌───────── day of month  (1 – 31)
│ │ │ ┌─────── month         (1 – 12  or JAN – DEC)
│ │ │ │ ┌───── day of week   (0 – 6   or SUN – SAT, 0 = Sunday)
│ │ │ │ │
30 2 * * 1-5    →  02:30, Monday to Friday

There is no "every 90 minutes" and no "every second Tuesday" in this dialect. Cron matches a calendar pattern; it does not count intervals from a start point. That distinction explains most of the confusion around it.

Which dialect this page uses: the 5-field Vixie/POSIX form. That is what crontab, Kubernetes CronJob, GitHub Actions, GitLab CI and most schedulers accept. Quartz and Spring use 6 or 7 fields with a leading seconds field, and add L, W and # operators. Those are detected and reported here rather than parsed, because guessing at them would show you run times your scheduler will not honour.

The five operators

Operator Meaning Example Matches
* Every value in the field * * * * * Every minute, 1,440 times a day
, A list of specific values 0 6,12,18 * * * 06:00, 12:00 and 18:00
- An inclusive range 0 9-17 * * * Every hour from 09:00 to 17:00, nine runs
/ A step through a range */15 * * * * Minutes 0, 15, 30, 45
names Three-letter month or day 0 0 * JAN MON Mondays in January, and every day in January

Steps are the operator people misread most often. */15 does not mean "every 15 minutes from now". It means "every value in 0–59 divisible by 15", which is minutes 0, 15, 30 and 45 of every hour. The difference shows up as soon as the range does not divide evenly:

*/20 * * * *   →  minutes 0, 20, 40        (gaps of 20, 20, 20)
*/25 * * * *   →  minutes 0, 25, 50        (gaps of 25, 25, 10)
0 */7 * * *    →  hours 0, 7, 14, 21       (then a 3-hour gap at midnight)

A step can also start partway through: 10-50/10 in the minute field gives 10, 20, 30, 40, 50 and skips minute 0.

How to build a schedule here

  1. Start from a preset or type an expression The presets cover the schedules that account for most real cron entries. Both the full expression box and the five individual field boxes stay in sync.
  2. Read the plain-English description Generated with the cronstrue library. If it does not describe what you meant, the expression is wrong even though it is valid.
  3. Check the next six run times This is the real verification step. A schedule can parse perfectly and still fire at the wrong moment.
  4. Switch between UTC and local time Servers and CI runners almost always schedule in UTC, while your intuition is in local time. Comparing both catches off-by-an-hour mistakes before deployment.

The day-of-month and day-of-week trap

This is the single most surprising rule in cron, and it is in the POSIX specification rather than a bug in any particular implementation.

When both the day-of-month and day-of-week fields are restricted, they are combined with OR, not AND.

0 0 13 * 5

Reads like:  "midnight on Friday the 13th"
Actually:    "midnight on the 13th of any month,
              AND midnight on every Friday"

That expression fires roughly 64 times a year, not once or twice. When only one of the two day fields is restricted, the rule does not apply and the schedule behaves as expected.

There is no way to express a true AND in a 5-field expression. The standard workaround is to schedule on the weekday and check the date at the start of the job:

# crontab: every Friday at midnight
0 0 * * 5 [ "$(date +\%d)" = "13" ] && /usr/local/bin/run-job

Note the escaped \%. In a crontab file an unescaped % is treated as a newline in the command, which silently breaks any command containing a date format string.

This page warns you whenever both day fields are restricted, and the calculated run times reflect the real OR behaviour rather than the intuitive one.

Examples that come up repeatedly

Expression When it runs Runs per year
*/5 * * * * Every 5 minutes 105,120
0 * * * * On the hour, every hour 8,760
30 2 * * * 02:30 daily 365
0 9 * * 1-5 09:00 on weekdays ≈ 261
0 0 1 * * Midnight on the 1st of each month 12
0 3 1 1,4,7,10 * 03:00 on the 1st of each quarter 4
0 0 * * 0 Midnight every Sunday 52
15 */4 * * * 00:15, 04:15, 08:15, 12:15, 16:15, 20:15 2,190

The nickname shortcuts @hourly, @daily, @midnight, @weekly, @monthly and @yearly are accepted here and expanded to their 5-field equivalents. @reboot is deliberately not, since it has no calendar schedule to calculate.

Common mistakes

  • Assuming */n means "every n from now". It divides the field's range from zero. */40 in the minute field gives minutes 0 and 40, then a 20-minute gap.
  • Scheduling on day 31. 0 0 31 * * skips April, June, September and November entirely — seven months a year rather than twelve. For "last day of month" you need a scheduler that supports L, or a job that checks the date.
  • Forgetting the server's time zone. Cron uses the system zone unless configured otherwise. A container built from a minimal base image is almost always UTC, so 0 9 * * * is 09:00 UTC, not 09:00 where you live.
  • Ignoring daylight saving. On a machine set to a DST-observing zone, a job scheduled at 02:30 can be skipped in spring and run twice in autumn. Scheduling in UTC, or outside 01:00–03:00, avoids the problem.
  • Using 7 for Sunday and expecting a range to work. 7 alone is accepted as Sunday by most implementations, but 5-7 is not portable. Write 0,5,6 instead.
  • Writing a backwards range. 0 22-4 * * * is rejected by most parsers rather than wrapping around midnight. Split it: 0 22-23,0-4 * * *.
  • Unescaped % in the command. Only affects crontab files, but it truncates the command at the percent sign, which looks like the schedule failing.

Use cases

  • Validating a Kubernetes CronJob. The schedule field is 5-field cron interpreted in UTC by default, so the UTC view here matches what the cluster will do.
  • Setting up a GitHub Actions workflow. on.schedule.cron uses this dialect and always runs in UTC. Confirming next-run times avoids a workflow that fires at 3am local.
  • Reviewing an inherited crontab. Paste an existing line to find out what it actually does — particularly useful when both day fields are set.
  • Sizing a job's load. The approximate runs-per-year figure makes it obvious when */1 was left in from testing.
  • Spreading out scheduled load. Rather than every job at 0 0 * * *, offsetting minutes across jobs avoids a midnight spike. Seeing the exact minutes helps plan that.
  • Checking a database backup window. Confirming the schedule falls inside the maintenance window before it runs in production.

Working with timestamps in the logs those jobs produce? The Epoch Converter turns Unix timestamps into readable dates.

Privacy

Parsing, validation, next-run calculation and the plain-English description all run in your browser. No expression is sent to a server, and nothing is stored between visits.

Frequently Asked Questions

Why does my expression run more often than I expected?

Almost always because both the day-of-month and day-of-week fields are restricted, and standard cron combines them with OR. "0 0 13 * 5" fires on every 13th and every Friday, roughly 64 times a year. The tool warns when your expression has this shape.

Can I schedule something every 90 minutes?

Not with a single expression. Cron matches calendar values, and 90 minutes does not align to an hour boundary. The usual approach is two entries — "0 0,3,6,9,12,15,18,21 * * *" and "30 1,4,7,10,13,16,19,22 * * *" — or a scheduler with interval support such as systemd timers.

Which time zone do the run times use?

Whichever you select. Local uses your browser zone; UTC uses UTC. This matters because most servers, containers and CI runners schedule in UTC, so a schedule that looks right in local time can fire hours away from what you intended.

Does this support Quartz expressions with seconds?

No, and it says so rather than guessing. Quartz and Spring use 6 or 7 fields plus L, W and # operators. A 6-field expression pasted here is reported as such, because parsing it as 5-field cron would shift every field by one and show run times your scheduler would never produce.

What happens to a job scheduled during a daylight saving change?

It depends on the implementation, which is why the safe answer is to avoid the affected hour. Vixie cron generally skips a job whose time does not exist in spring and may run it twice in autumn. Scheduling in UTC removes the issue entirely.

Is 0 or 7 Sunday?

Both work in most implementations, with 0 being the portable choice. The catch is ranges: 5-7 is not reliably interpreted, so write 0,5,6 for Friday through Sunday. This tool accepts 7 as a standalone value and normalises it to 0.

How many run times ahead does it calculate?

Six, searching up to four years forward. If fewer than six are found, the schedule is either extremely rare or impossible — "0 0 30 2 *" asks for 30 February and never fires. That case is flagged explicitly.