View all services
Talk to QA Advisor
Free tool · Automation and API

Cron expression generator and explainer

Build a schedule or paste an expression, and see exactly what it means and when it will run: in plain English, field by field, and as a list of upcoming times in your time zone. Each dialect follows its own rules, because the differences between them are where scheduled jobs go wrong.

Cron expression generator

Type or paste a cron expression to read it in plain English and see when it will run, or build one from the controls below.

Examples
Dialect

cronie and Vixie cron on Linux. Five fields; 0 and 7 are Sunday; if both day fields are set, either one matching is enough.

Valid Unix crontab expression

Builder

On
At

Changing the builder rewrites the expression. Edit the expression directly for anything more specific.

Time zone

Your browser's zone: stands in for the server's zone.

In plain English

Every 15 minutes between 09:00 and 17:59 on every day from Monday to Friday

Calculating run times.

What each field of the expression means
FieldValueAllowedMeaning
Minute*/150-59Every 15 minutes: 0, 15, 30, 45
Hour9-170-23Hours 09, 10, 11, 12, 13, 14, 15, 16, 17
Day of month*1-31Any day of the month
Month*1-12Every month
Day of week1-50-7Monday to Friday

Next 10 runs

In your time zone, with the same moment in UTC. Daylight saving changes are flagged on the run they affect.

Calculating run times.

Ready
How to use

How to use the Cron expression generator

  1. 1Choose the dialect your scheduler uses: Unix crontab, Quartz, Spring, GitHub Actions or Kubernetes. Switching converts the expression when the meaning can be kept exactly.
  2. 2Paste an expression, or build one with the controls. The plain-English description, the field table and any errors update as you type.
  3. 3Pick the time zone the scheduler runs in and check the next run times, including any run that falls in an hour skipped or repeated by a daylight saving change.
  4. 4Read the warnings, then copy the configuration for your scheduler or export the run times as CSV or JSON to use as expected values in a test.

The five fields, and what each one accepts

A standard cron expression has five fields separated by spaces: minute (0 to 59), hour (0 to 23), day of the month (1 to 31), month (1 to 12, or JAN to DEC) and day of the week. Each field takes a single value, a list such as 1,15, a range such as 9-17, an asterisk for every value, or a step such as */15. A step counts through the range it follows, so 0-30/10 means minutes 0, 10, 20 and 30.

Steps restart at the start of each larger unit rather than counting continuously. */7 in the minute field runs at 0, 7, 14 and so on to 56, then at 0 again: the last gap of each hour is 4 minutes, not 7. */5 in the hour field runs at 0, 5, 10, 15 and 20, leaving 4 hours between 20:00 and midnight. If you need an exact interval that does not divide the hour or the day, cron is the wrong tool and a fixed-rate timer is better.

Days that do not exist are skipped, never moved. 0 0 31 * * runs in only seven months a year, and 0 0 30 2 * never runs at all. The generator lists the next runs, so a schedule that runs rarely or never shows up immediately instead of in production.

  • Every 15 minutes

    */15 * * * *

    At minutes 0, 15, 30 and 45 of every hour.

  • Weekdays at 09:30

    30 9 * * 1-5

    Monday to Friday, once a day.

  • Business hours

    */15 9-17 * * 1-5

    Every 15 minutes from 09:00 to 17:45 on weekdays.

  • First of the month

    0 0 1 * *

    Midnight at the start of every month.

Dialect differences that cause real bugs

Day-of-week numbering is the most common trap. In Unix cron and Spring, 0 and 7 are both Sunday, so 1 is Monday. In Quartz, 1 is Sunday and 7 is Saturday, so 2-6 means Monday to Friday. An expression copied from a Quartz job into Spring therefore shifts every day by one. Names such as MON-FRI mean the same everywhere, so use them where the dialect allows. Kubernetes accepts only 0 to 6 and rejects 7.

The two day fields combine differently. In Unix cron, if both day-of-month and day-of-week are restricted, a day that matches either one runs: 0 9 13 * 5 runs on every 13th and on every Friday, not only on Friday the 13th. cronie and Vixie cron treat a field that starts with an asterisk as unrestricted, so */2 in day-of-month switches back to requiring both. Kubernetes, which parses schedules with the robfig/cron library, treats */2 as restricted. Spring always requires both fields to match, and Quartz refuses to set both at all: one of them must be a question mark.

Quartz and Spring add a seconds field at the start, which is why a five-field expression pasted into them fails, and a six-field one pasted into a crontab is read with the seconds as minutes. They also support L for the last day of the month, W for the nearest weekday and # for the nth weekday, such as 6#3 for the third Friday in Quartz. None of these exist in Unix cron, GitHub Actions or Kubernetes.

Time zones and daylight saving

A cron expression has no time zone of its own. Unix cron reads it in the server's zone, or the zone in CRON_TZ on cronie. Quartz and Spring use the JVM default unless you pass one. GitHub Actions uses UTC unless the schedule sets timezone. Kubernetes uses the kube-controller-manager's zone unless the CronJob sets spec.timeZone, which has been stable since Kubernetes 1.27; putting CRON_TZ or TZ inside the schedule is rejected with a validation error.

In a zone with daylight saving, one night a year skips an hour and another repeats one. cron(8) documents what cronie does: a job at a fixed time in the skipped hour runs immediately after the change, a fixed-time job is not run twice in the repeated hour, and jobs that run every hour or more often are scheduled normally. GitHub documents that a run in a skipped hour advances to the next valid time, so 02:30 becomes 03:00. Quartz only warns that the change can cause a skip or a repeat, and the Spring and Kubernetes docs do not say. The run list here flags every affected run with what the docs say.

The safest schedule avoids the change window entirely: in Europe and North America the changes happen between 01:00 and 03:00 local time, so a nightly job at 04:00 or in UTC never meets one.

Testing scheduled jobs without waiting for them

Treat the schedule and the job as two things to test separately. The schedule is pure date arithmetic: assert that the expression produces the expected next run times from a fixed starting point, including a case that crosses a daylight saving change and one at a month end. Spring's CronExpression.next and Quartz's getNextValidTimeAfter run in milliseconds, and the JSON export here gives you the expected values.

Test the job by triggering it directly. Kubernetes can create a one-off Job from a CronJob's template with kubectl create job --from=cronjob/<name>. GitHub Actions workflows can add workflow_dispatch so the same workflow runs from a button. For Unix cron, run the command with cron's minimal environment, because a job that works in your terminal and fails from cron almost always depends on PATH or a variable set by your shell profile.

Then check what happens around the run: what the job does if the previous run is still going, if the machine was off at the scheduled time, and whether it is safe to run twice. Those are the failures that reach production, and none of them depend on waiting for the clock.

GitHub Actions and Kubernetes specifics

GitHub Actions runs scheduled workflows only from the default branch, no more often than every 5 minutes, and does not support macros such as @daily. GitHub warns that scheduled runs can be delayed at busy times, especially at the start of every hour, and that queued runs may be dropped under high load. A minute such as 17 rather than 0 makes delays less likely. In a public repository, scheduled workflows are disabled after 60 days with no repository activity.

A Kubernetes CronJob creates a Job for each scheduled time. concurrencyPolicy decides what happens if the previous Job is still running: Allow (the default) runs both, Forbid skips the new run and Replace stops the old one. startingDeadlineSeconds sets how late a missed run may still start. If the controller finds more than 100 missed schedules since the last run, it does not start the Job and logs an error, which is a reason to set startingDeadlineSeconds on any CronJob that runs often.

QAble service

DevOps automation

QAble sets up CI pipelines and scheduled jobs that run the right tests at the right time, with the time zones, retries and alerts that keep a nightly run trustworthy.

Explore the service
FAQ

Frequently asked questions

Is the expression or time zone sent anywhere?

No. Parsing, the description and the run times are calculated in your browser, using the time zone data built into it. Nothing is sent to a server.

Why does my expression work in crontab but fail in Spring or Quartz?

Spring and Quartz expect a seconds field first, so they need six fields where crontab has five. Add 0 at the front. Quartz also needs ? in either the day-of-month or the day-of-week field, and numbers days of the week from 1 for Sunday, so check those before relying on the converted expression.

How do I run a job on the last day of the month?

In Quartz and Spring, put L in the day-of-month field: 0 0 0 L * ? in Quartz. Unix cron, GitHub Actions and Kubernetes have no "last day" value. A common Unix workaround is to schedule 0 0 28-31 * * and have the command check that tomorrow is the 1st before doing anything.

Why did my job run on days I did not expect?

Usually because both day fields are set. In Unix cron, 0 9 1 * 1 runs on the 1st of every month and on every Monday, because a day matching either field is enough. To run only on the first Monday, use Quartz or Spring with MON#1, or schedule every Monday from the 1st to the 7th and check the date in the command.

What time zone does the next-run list use?

The one you choose: your browser's zone, UTC, or any IANA zone such as Asia/Kolkata or America/New_York. Choose the zone your scheduler actually runs in. For a server, that is usually UTC, and for GitHub Actions it is UTC unless the workflow sets timezone.

Does it support Jenkins H syntax or random values?

No. Jenkins H and cronie's ~ both pick a value that is only known to the scheduler, so no outside tool can list the exact run times. Replace them with fixed values here to check the rest of the expression. For a Unix expression, the Use it section shows the same schedule as a Jenkins trigger.

Need the edge cases found for you?

QAble testers work through the inputs, payment paths and data that break products in production, before your users find them.

Talk to QA Advisor