Cron Expression Generator

Build a cron schedule from dropdowns, or paste an expression you inherited and read it back in plain English — in five-field Unix, six or seven-field Quartz and Spring, AWS EventBridge or Azure NCRONTAB form. The format is detected from what you paste, and the next five run times are computed for that dialect, because a seconds field shifts the meaning of every value after it.

Field order: minute hour day-of-month month day-of-week. Edit the boxes directly or use the presets — the expression above stays in sync, and you can paste over it to go the other way.

Enter an expression to explain it.
The plain-English description will appear here.
Next 5 runs (your local time)
Upcoming execution times will appear here.

Unix, 5 fields:

Quartz / Spring, 6-7 fields:

AWS EventBridge / CloudWatch:

Azure Functions NCRONTAB:

How a cron expression works

Cron is the scheduler that has run periodic jobs on Unix systems since the 1970s, and its expression syntax outlived the daemon itself. Kubernetes CronJobs, GitHub Actions, systemd timers' calendar syntax, AWS EventBridge, Azure Functions, Vercel, Netlify, Airflow, Spring and half the job queues in existence all take a cron string. Learning the fields once pays off across every one of them — provided you also know which dialect the system in front of you speaks, because they disagree about how many fields there are, what the first one means, and which number is Sunday.

Start with the classic. A standard Unix expression is five space-separated fields, always in this order:

PositionFieldRangeNotes
1Minute0–59
2Hour0–2324-hour clock; midnight is 0
3Day of month1–31Months with fewer days simply skip
4Month1–12JANDEC also accepted
5Day of week0–70 and 7 are both Sunday; SUNSAT accepted

Every field takes the same four constructs. An asterisk means every value. A list uses commas: 0,15,30,45. A range uses a hyphen: 1-5. A step uses a slash: */15 means every fifteenth value across the whole field, and 9-17/2 means every second value within that range. Combining them is legal, so 0,30 9-17 * * 1-5 is a perfectly ordinary expression: on the hour and half hour, between 9am and 5pm, Monday to Friday.

One subtlety about steps: they divide the field's numeric range, not elapsed time. */7 in the minute field fires at 0, 7, 14, 21, 28, 35, 42, 49 and 56 — and then the hour rolls over and it fires again at 0, only four minutes later. Steps only behave like a true interval when they divide evenly into the field size, which is why 5, 10, 15, 20 and 30 are so much more common than 7 or 45.

Five fields, six fields, seven fields

The single most expensive misunderstanding in cron is that "cron syntax" is not one syntax. The classic Vixie cron that ships with Linux, and everything modelled on it, uses five fields starting at minute. Quartz — the Java scheduler behind Spring's @Scheduled(cron = ...) and a long tail of enterprise products — prepends a seconds field, giving six, and optionally appends a year field, giving seven. Azure Functions uses NCRONTAB, which is also six fields with seconds first. AWS EventBridge is six fields too — but the extra field is a year at the end, not seconds at the front.

That last point is why counting fields is not enough to identify a dialect, and why the tool above names the format it assumed instead of quietly picking one. Two six-field expressions can mean completely different things:

DialectFieldsOrderDay-of-week?, L, W, #
Unix / Linux (Vixie, cronie)5 min hour dom mon dow 0–7, 0 and 7 both SundayNo
Quartz / Spring6 or 7 sec min hour dom mon dow [year] 1–7, 1 is SundayYes
AWS EventBridge / CloudWatch6 min hour dom mon dow year 1–7, 1 is SundayYes
Azure Functions (NCRONTAB)6 sec min hour day mon dow 0–6, 0 is SundayNo

The consequence of guessing wrong is quiet and nasty. Paste a six-field Quartz expression into a five-field parser and, if it does not simply reject it, every value shifts one position left. 0 30 9 * * ? — meant as "09:30:00 every day" — becomes minute 0, hour 30, day 9, month *, weekday ?: either an error or, in a permissive parser, something entirely unlike the intention. Going the other way is just as bad: a five-field expression fed to Quartz is read as seconds-minute-hour-dom-month, so 0 9 * * 1-5 becomes a job that fires every second.

The day-of-week numbering is a second, subtler trap, and it survives even when the field count is right. 1-5 means Monday to Friday in Unix cron and in Azure NCRONTAB, but Sunday to Thursday in Quartz and AWS, because those two count Sunday as 1. A weekday report copied from a crontab into an EventBridge rule will therefore run on Sunday and skip Friday, and nothing will error. Writing the names — MON-FRI — is unambiguous in every dialect and is worth the extra three characters. The tool above resolves the field to actual day names in the breakdown so you can see which days you really selected.

AWS EventBridge and CloudWatch cron

EventBridge (and the CloudWatch Events rules it grew out of) takes six fields wrapped in cron(...): minute hour day-of-month month day-of-week year. The wrapper is part of the schedule expression, not decoration — ScheduleExpression must be cron(0 12 * * ? *), not 0 12 * * ? *.

The one rule that catches nearly everyone: you cannot specify both day-of-month and day-of-week. Exactly one of them must be a question mark. cron(0 12 * * ? *) is a valid noon-daily schedule; cron(0 12 * * * *) is rejected by the API with a validation error, because both day fields are specified. It reads like a typo — the asterisk that works everywhere else in cron is the thing that is wrong here — which is exactly why it burns so much time. The parser above enforces the rule and explains it rather than silently accepting the expression.

Three more EventBridge specifics worth knowing. Schedules are evaluated in UTC by default, so a "9am" rule is 9am UTC unless you attach a time zone (EventBridge Scheduler supports one; classic rules do not). The finest granularity is one minute — there is no seconds field, so */30 * * * * *-style sub-minute scheduling is not available and a rate expression will not help either. And delivery is best-effort: a rule can fire slightly late, and in rare cases more than once, so the target should be idempotent — see idempotency for what that actually requires. If the target is an HTTP endpoint, exercise it first in the API tester so you are debugging the schedule and the endpoint separately rather than together.

EventBridge expressionMeaning
cron(0 12 * * ? *)Every day at 12:00 UTC
cron(0/15 * * * ? *)Every 15 minutes
cron(0 8 ? * MON-FRI *)Weekdays at 08:00 UTC
cron(0 0 L * ? *)Midnight on the last day of every month
cron(0 9 ? * 2#1 *)09:00 on the first Monday of the month (2 is Monday here)
cron(0 0 1 1 ? 2027)Once, at the start of 1 January 2027

Azure Functions timer triggers (NCRONTAB)

An Azure Functions timer trigger takes an NCRONTAB expression: six fields, seconds first — second minute hour day month day-of-week. It is the same shape as Quartz, which is why the two are so often confused, but it is Azure-flavoured: there is no ?, no L, no W and no #, and day-of-week is 0–6 with 0 meaning Sunday rather than Quartz's 1–7.

The mistake that reliably makes it into production is reading the leading field as minutes. 0 */5 * * * * is every five minutes at zero seconds past the minute — the */5 is in the minute field, second from the left. The expression that really does run every five seconds is */5 * * * * *, with the step in the first field. Getting these the wrong way round means a function billed 12 times more often than you planned, and the only symptom is the invoice. Load either into the tool above with the Azure format selected and read the next five run times; the gap between them settles the question in a second.

Two operational notes. Timer triggers default to UTC; the app setting WEBSITE_TIME_ZONE changes that, and daylight saving in the chosen zone then applies with all the usual consequences. And because a function app can scale to zero or restart mid-schedule, timer triggers use a storage-backed schedule monitor to avoid double-firing across instances — but "avoid" is not "guarantee", so the same idempotency advice applies as everywhere else in this page.

NCRONTAB expressionMeaning
*/5 * * * * *Every five seconds
0 */5 * * * *Every five minutes
0 0 * * * *Once an hour, on the hour
0 30 2 * * *Every day at 02:30
0 0 9 * * 1-5Weekdays at 09:00 (0–6 numbering, so 1–5 really is Mon–Fri)
0 0 0 1 * *Midnight on the first of every month

Quartz and Spring: ?, L, W and #

Quartz is the scheduler behind Spring's @Scheduled(cron = "..."), and Spring's own cron support follows the same six-field shape. It adds four characters that standard cron has never had, and the tool above parses and explains all of them.

TokenFieldMeaning
?day-of-month or day-of-weekNo specific value. Required in exactly one of the two, because specifying both would be ambiguous.
Lday-of-monthThe last day of the month — 31 in July, 30 in September, 28 or 29 in February.
L-3day-of-monthThree days before the last day of the month.
LWday-of-monthThe last weekday (Mon–Fri) of the month.
15Wday-of-monthThe weekday nearest the 15th, without crossing into an adjacent month.
FRIL / 6Lday-of-weekThe last Friday of the month.
FRI#3 / 6#3day-of-weekThe third Friday of the month.

These are the answer to the scheduling problems standard cron cannot express at all: month-end billing runs (0 0 22 L * ?), payroll on the last working day (0 0 6 LW * ?), and the "first Monday of the month" meeting reminder (0 0 9 ? * MON#1) that otherwise needs a guard inside the job. Note that W deliberately does not jump across a month boundary: if the 1st falls on a Saturday, 1W fires on Monday the 3rd, not on the Friday before.

They are also the fastest way to break a schedule when a system moves. L, W and # do not exist in Unix cron or in Azure NCRONTAB, and a parser that does not understand them will either error or — worse — mis-read them. The tool above refuses them when a non-Quartz format is selected, with a message naming the dialect, rather than guessing. A refusal you can see beats a wrong answer you cannot.

Common cron expressions

These are all five-field Unix expressions — what a Linux crontab, a Kubernetes CronJob or a GitHub Actions schedule expects. The Quartz, AWS and Azure equivalents are in their own sections above, and the preset buttons under the tool load a working example of each.

ExpressionMeaning
* * * * *Every minute
*/5 * * * *Every five minutes
*/15 * * * *Every quarter of an hour
0 * * * *Hourly, on the hour
0 */2 * * *Every two hours
0 0 * * *Every day at midnight
30 2 * * *Every day at 02:30 — the classic backup window
0 9 * * 1-5Weekdays at 9am
0 0 * * 0Weekly, Sunday at midnight
0 0 1 * *Monthly, on the first at midnight
0 0 1 1 *Yearly, at the start of 1 January
0 0 1,15 * *On the 1st and 15th of every month
0 0 1 */3 *Quarterly, on the first of January, April, July and October
0 22 * * 1-5Weeknights at 10pm
*/10 9-17 * * 1-5Every ten minutes during business hours on weekdays

Many crons also accept the shorthand macros @yearly, @monthly, @weekly, @daily, @hourly and @reboot. They are readable, but they are not universally supported and @reboot in particular has no equivalent anywhere else, so an explicit expression travels better between systems.

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

Every other pair of fields in cron is combined with AND. These two are not. When both the day-of-month and day-of-week fields are restricted — neither is an asterisk — Unix cron runs the job when either matches. So 0 0 1 * 1 does not mean "the first of the month, if it is a Monday". It means "the first of the month, and also every Monday", which fires roughly five times as often as intended. Azure NCRONTAB behaves the same way.

Quartz and AWS EventBridge sidestep the ambiguity rather than defining it away: they require ? in exactly one of the two fields, so only one day rule can ever be active and the OR case simply cannot arise. That is the whole reason ? exists, and it is why the tool above shows the OR warning for Unix and Azure expressions but never for Quartz or AWS ones.

When only one of the two is restricted and the other is *, every dialect behaves the obvious way. That is why 0 9 * * 1-5 is safe and idiomatic, and why "the first Monday of the month" cannot be expressed in standard cron at all. The usual Unix workaround is 0 9 1-7 * 1 combined with a guard in the job itself — the schedule fires on the first seven days and on every Monday, and the script exits immediately unless both conditions hold. Quartz and AWS users can write 0 0 9 ? * MON#1 instead and be done. The next-runs list in the tool above makes all of this visible immediately: put both fields in and count how many dates come back.

Time zones, drift and other operational realities

Cron has no time zone of its own. It uses the local zone of whatever machine, container or runner is executing it, which means the same crontab produces different behaviour in different deployments. If the zone observes daylight saving, two things happen every year: on the spring transition an hour of local time does not exist, so a job scheduled inside it is skipped entirely, and on the autumn transition an hour repeats, so a job inside it may fire twice. Both are real incidents that happen to real reporting jobs. Running schedulers in UTC removes the entire class of problem, at the cost of the schedule no longer lining up with anyone's working day. If you need a local-time schedule, use a scheduler that accepts an explicit IANA zone — Kubernetes CronJob supports timeZone, and most managed schedulers do too. To reason about what a stored UTC timestamp means in local terms, the epoch converter does the arithmetic.

A few more things worth knowing before a schedule goes to production. Cron guarantees no overlap protection: if a job scheduled every five minutes takes six, you will accumulate concurrent copies until something falls over. Wrap long jobs in a lock — flock is one line and solves it. Cron also runs with a minimal environment, so PATH is short and your shell profile is not sourced; absolute paths and explicit environment variables avoid the classic "works when I run it, fails from cron" report. Output is another one: anything a job writes to stdout or stderr is mailed to the owner by default, which on a machine with no mail transport means it is silently discarded, so redirect to a log file you actually read.

Finally, schedule the load rather than the convenience. Every job written 0 0 * * * across a fleet fires at the same instant, and if those jobs call an API you will produce a spike that trips rate limiting and returns 429 Too Many Requests. Spreading jobs across the hour, or adding a short random delay at the start of the script, costs nothing and avoids a thundering herd. If a scheduled job talks to an HTTP endpoint, it is worth exercising that request by hand in the API tester first, checking the response headers with the headers reference, and confirming what the endpoint returns under failure — a cron job that ignores a 503 and reports success is worse than one that never ran.

Frequently asked questions

What is the difference between a 5-field, 6-field and 7-field cron expression?

Five fields is standard Unix and Linux cron: minute, hour, day-of-month, month, day-of-week. Six fields usually means Quartz, Spring or Azure NCRONTAB, which prepend a seconds field so the order becomes second, minute, hour, day-of-month, month, day-of-week. Seven fields is Quartz with a trailing year. AWS EventBridge is the odd one out: it also uses six fields, but the extra field is a trailing year rather than a leading seconds field, so its order is minute, hour, day-of-month, month, day-of-week, year. Counting fields is not enough to identify the dialect, which is why this tool names the format it assumed.

How do I write an AWS EventBridge or CloudWatch cron expression?

Six fields — minute, hour, day-of-month, month, day-of-week, year — wrapped as cron(...) in the schedule expression. The rule that catches almost everyone is that you cannot specify both day-of-month and day-of-week: exactly one of them must be a question mark. cron(0 12 * * ? *) is a valid noon-daily schedule; cron(0 12 * * * *) is rejected by the API. AWS day-of-week is 1 to 7 with 1 meaning Sunday, not 0 to 6, and all EventBridge schedules are evaluated in UTC unless you set a time zone on the schedule.

What does the question mark mean in a cron expression?

In Quartz and in AWS EventBridge, ? means no specific value and is only legal in the day-of-month and day-of-week fields. It exists because those two fields overlap: specifying both would be ambiguous, so the dialect requires you to leave exactly one of them unspecified. Functionally ? behaves like an asterisk when it comes to matching, but the two are not interchangeable because the parser enforces the exactly-one rule. Standard Unix cron has no question mark at all.

Does an Azure Functions timer trigger use seconds first?

Yes. Azure Functions uses NCRONTAB, which is six fields with seconds leading: second, minute, hour, day, month, day-of-week. So 0 */5 * * * * is every five minutes at zero seconds past the minute, not every five seconds — the five belongs to the minute field. Writing */5 * * * * * instead does mean every five seconds. Azure day-of-week is 0 to 6 with 0 meaning Sunday, and NCRONTAB does not support the Quartz question mark.

How do I write every 30 seconds in cron?

You cannot in standard five-field cron, where one minute is the finest granularity; the usual workarounds are two crontab entries with a sleep 30 in one of them, or a systemd timer. In a dialect with a seconds field you can: */30 * * * * * is every thirty seconds in Quartz and in Azure NCRONTAB. AWS EventBridge cron has no seconds field either, so the minimum there is one minute.

What do L, W and # mean in a Quartz cron expression?

L in day-of-month means the last day of the month, and L-3 means three days before it. LW means the last weekday of the month. 15W means the weekday nearest the 15th, without crossing into another month. In day-of-week, 6L means the last Friday of the month and 6#3 means the third Friday. None of these exist in standard Unix cron or in Azure NCRONTAB, and pasting one into a parser that does not understand it is how a monthly job quietly becomes a daily one.

How do day-of-month and day-of-week interact?

In Unix cron and in Azure NCRONTAB, if both fields are restricted the job runs when either one matches, not both. So 0 0 1 * 1 runs at midnight on the first of the month and also on every Monday. Quartz and AWS sidestep the ambiguity entirely by requiring a question mark in one of the two fields, so only one day rule is ever active. If only one field is restricted and the other is an asterisk, every dialect behaves the intuitive way.

Which time zone does cron use, and does the preview handle daylight saving?

Standard cron uses the local time zone of the machine or container running it. AWS EventBridge defaults to UTC. The preview on this page computes in your browser's local time zone, so transitions in that zone are reflected, but a production scheduler uses its own host's zone and may differ. If a zone observes daylight saving, a job scheduled inside the skipped spring hour does not run and a job inside the repeated autumn hour may run twice; running schedulers in UTC removes the whole class of problem.

Why did my scheduled job not run at all?

Common causes: the expression has the wrong number of fields for that scheduler, a six-field expression was read as five so every value shifted one position, the crontab file has no trailing newline, an unescaped % (which cron treats as a newline in the command), a command that is not on cron's minimal PATH, or a schedule that silently never matches — day 31 in a month with 30, or 0 0 31 2 * which can never fire. This tool reports an expression that has no run in the next five years rather than leaving the preview blank.

Is anything I type here sent to a server?

No. Format detection, parsing, the plain-English description and the next-run calculation all happen in this page, in your browser. Nothing is uploaded and nothing is stored.

Related tools and reference