All free tools

Free tool · No signup

Cron Expression Generator & Explainer

Build a cron schedule or paste one to read it in plain English, with its next run times in any time zone.

At 9:00 AM, Monday through Friday

Runs in the server's time zone. cronie also reads a CRON_TZ line in the crontab.

Build a schedule

Common schedules

Run it every

On these days

Works in your browser. Nothing is sent to a server or saved.

Next 8 runs

Calculating…

Each field explained

FieldValueMeansAllowed
Minute0Minute 00-59
Hour9The hour that starts at 9:00 AM0-23
Day of month*Every day1-31
Month*Every month1-12 or JAN-DEC
Day of week1-5Monday, Tuesday, Wednesday, Thursday, and Friday0-7 (0 and 7 are Sunday) or SUN-SAT

Use it in Linux crontab

crontab -e

# At 09:00, Monday through Friday
# Some cron versions ignore CRON_TZ. Check your server.
CRON_TZ=UTC
0 9 * * 1-5 /path/to/your-command

How to read a cron expression

A cron expression is a line of fields that says when a job runs. Standard cron has five fields, separated by spaces, and a job runs when the current time matches all of them. 30 9 * * 1-5 reads as minute 30, hour 9, any day of the month, any month, Monday to Friday, so the job runs at 9:30 AM on weekdays.

FieldValuesExampleMeaning
Minute0-59*/15Every 15 minutes
Hour0-239-179 AM through 5 PM
Day of month1-311,15The 1st and the 15th
Month1-12 or JAN-DEC*/3January, April, July and October
Day of week0-6 or SUN-SAT1-5Monday to Friday

Four characters do most of the work. * means any value, , makes a list, - makes a range and / sets a step. A step counts from the start of its range, so */20 in the minute field means 0, 20 and 40, and 5-59/20 means 5, 25 and 45.

Quartz, AWS, Spring and node-cron also accept characters for dates that standard cron cannot express:

  • ? means no specific value. Quartz and AWS need it in one of the two day fields.
  • L means last. L in the day of month field is the last day of the month. 6L in the Quartz day of week field is the last Friday.
  • W means the nearest weekday. 15W runs on the Monday to Friday day closest to the 15th, inside the same month.
  • # picks one week of the month. 6#3 in Quartz is the third Friday.

Which cron are you writing for?

The same expression can be valid in one system, invalid in another and mean something different in a third. For example, 0 9 * * 1 runs on Monday in Linux crontab, but the same weekday number means Sunday in Quartz. Choose the platform above, and the tool applies that platform's rules. When you switch, it rewrites the expression if the new platform can express the same schedule. I checked these rules against each project's docs or source code in November 2026.

PlatformFieldsWeekday numbersTime zoneBoth day fields set
Linux crontab (cronie)50-7, and 0 and 7 are SundayServer time, or a CRON_TZ lineEither field matches, unless one starts with *
GitHub Actions50-6UTC, or a timezone keyThe docs do not say
Kubernetes CronJob50-6Controller time, or .spec.timeZoneEither field matches, unless one is a plain *
Vercel Cron Jobs5, no names0-6Always UTCNot allowed: one must be *
AWS EventBridge6, year last1-7, and 1 is SundayUTC, or any zone on SchedulerNot allowed: one must be ?
Quartz6 or 7, seconds first1-7, and 1 is SundaySet on the triggerNot supported: one must be ?
Spring @Scheduled6, seconds first0-7, and 0 and 7 are SundayThe zone attributeBoth must match (from the source code)
node-cron5 or 6, seconds first0-7, and 0 and 7 are SundayThe timezone optionBoth must match

Limits differ too. GitHub Actions runs a schedule at most once every 5 minutes, and runs can be late when GitHub is busy. Vercel Hobby projects can run once a day, and a job set for 1:00 can start at any time up to 1:59. The tool warns you about these limits when your schedule goes past them.

Why cron ran your job on the wrong day

In Linux crontab, the two day fields do not work like the other fields. If you set both, the job runs when either one matches. The crontab manual gives 30 4 1,15 * 5 as an example: 4:30 AM on the 1st, on the 15th and on every Friday.

This catches people who try to write "the first Monday of the month". 0 9 1-7 * 1 runs on the first seven days of the month and on every Monday. Standard cron has no symbol for this, so set the date range and test the weekday in the command:

0 9 1-7 * * [ "$(date +\%u)" = 1 ] && /path/to/job

The backslash matters, because crontab treats an unescaped % in a command as a new line. On Quartz, AWS, Spring and node-cron, use # instead, and write the third Friday as 6#3 in Quartz and AWS, or 5#3 in Spring and node-cron.

A step can change the rule without any warning. cronie treats any field that starts with * as unrestricted, so 0 0 */2 * 1 needs both fields to match: odd dates that are Mondays. robfig/cron, the library Kubernetes uses, treats */2 as restricted, so the same line runs on odd dates and on every Monday. If a schedule moves between systems, test it again.

Time zones and clock changes

A cron expression has no time zone of its own. The system that runs it decides, and many cloud schedulers default to UTC, so 0 9 * * * can run at 4 AM or 2 PM on your clock. Set the time zone above to see the real run times, with your local time beside them.

Clock changes create two more cases. When clocks go forward, some times do not exist. When clocks go back, some times happen twice. Each system handles these differently:

  • cronie: the manual says that a missing time never matches and a repeated time runs twice.
  • GitHub Actions: the docs say that a run in the missing hour moves forward, so 2:30 AM becomes 3:00 AM.
  • AWS EventBridge Scheduler: the docs say that it skips the missing run and runs a repeated time once.
  • Kubernetes: robfig/cron does not run jobs that fall in the missing hour.

This tool checks the next 12 months and tells you if a clock change affects your schedule in the chosen zone. To avoid the problem, schedule important jobs in UTC, or outside the hours when clocks change, which are usually between midnight and 3 AM.

How the next run times are calculated

The tool runs in your browser, with no server call. It parses the expression under your platform's rules, then walks forward through the dates and converts each match to a real moment with your browser's time zone data. I compared its run times for about 3,900 random five-field schedules, in seven time zones, against the open source cron-parser library, with no differences. That test left out schedules with */n in one day field and a restriction in the other, because cron versions disagree on them. Unit tests cover those schedules and the special Quartz characters. The tool does not run your scheduler, so test a schedule on the real system before you depend on it.

Before you trust a scheduled job

A correct expression does not make a job reliable. These problems cause more failures than the syntax does:

  • Runs that overlap. If a job can take longer than its interval, two copies can run at once. On Linux, flock -n /tmp/job.lock /path/to/job skips a run while the last one is still working. In Kubernetes, set concurrencyPolicy: Forbid.
  • A small environment. Cron does not load your login shell, so PATH and other variables can differ. Use full paths and set the variables that the job needs.
  • Failures that nobody sees. Send the output to a log with >> /var/log/job.log 2>&1, and add an alert for a job that does not finish on time.

If a scheduled job carries real work, such as reports, syncs or AI pipelines, see how I build automations. For a job in n8n or code, the n8n vs custom Python guide compares the two, and the automation ROI calculator shows if the work pays for itself.

By Muhammad Saif · Updated

Questions, answered.

What does * * * * * mean in cron?

It means every minute. The five fields are minute, hour, day of month, month and day of week, and a * matches any value. Replace a * to narrow the schedule: 0 * * * * runs at the start of every hour, and 0 9 * * * runs once a day at 9:00.

How do I run a cron job every 5 minutes?

Use */5 * * * *. It runs at minute 0, 5, 10 and so on, every hour. To limit it to office hours on weekdays, use */5 9-17 * * 1-5, which last runs at 5:55 PM. GitHub Actions accepts this schedule, but 5 minutes is its shortest interval.

Why did my cron job run on the wrong day?

The usual cause is setting both day fields. In Linux crontab, 0 9 1-7 * 1 runs on the first seven days of the month and on every Monday, not on the first Monday. Keep one day field as * and test the other condition in the command. node-cron and Spring differ, because they need both fields to match.

What time zone does a cron job use?

It depends on the system. Linux crontab uses the server's time zone, and cronie also reads a CRON_TZ line. GitHub Actions uses UTC unless you add a timezone key, Kubernetes uses the controller's time zone unless you set .spec.timeZone, Vercel always uses UTC, and AWS EventBridge Scheduler defaults to UTC but accepts any IANA zone.

What is the difference between 5, 6 and 7 field cron expressions?

Standard cron has five fields, starting with the minute. Spring has six, with seconds first. Quartz has six or seven, with seconds first and an optional year last. AWS EventBridge has six, with minutes first and a required year last. node-cron has five, with an optional seconds field first. Pick the platform in the tool to apply its layout.

What do the ?, L, W and # characters mean?

They are extensions that standard cron does not have. ? means no specific value in a day field, L means last (L is the last day of the month), W means the nearest weekday (15W), and # picks one week of the month (6#3 is the third Friday in Quartz and AWS). Linux crontab, GitHub Actions, Kubernetes and Vercel do not list L, W or #.

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

Quartz, AWS, Spring and node-cron accept L in the day of month field. Standard cron has no symbol for it, so run on the 28th through the 31st and test the date in the command. With GNU date, 0 0 28-31 * * [ "$(date -d tomorrow +\%d)" = 01 ] && /path/to/job runs only when tomorrow is the 1st.

Is my cron expression sent to a server?

No. The parser, the explanation and the run times all work in your browser, and nothing is logged or saved. A share link keeps the schedule in the part of the URL after the #, which browsers do not send to the server.