Pular para o conteúdo principal

Cron Expression Syntax

A cron expression is five fields — minute, hour, day of month, month, day of week — each holding *, a number, a range like 1-5, a step like */15, or a list like 1,3,5. All fields intersect, except the classic trap: restrict both day-of-month and day-of-week and cron ORs them, not ANDs. Timing follows the server's clock.

Só local
Só quer a ferramenta? Abra Testar expressão cronVê o que a expressão significa e quando ela disparaAbrir ferramenta

O guia abaixo está disponível apenas em inglês.

Cron Expression Syntax explained

Cron has been the Unix scheduler since the 1970s, and its grammar has barely moved: five small fields that together answer “when does this run”. You will meet the syntax in crontabs, CI systems, Kubernetes CronJobs, database maintenance plans and half the schedulers on the internet — a syntax old enough to vote, still winning because it fits in a line.

The grammar is simple; the edges are not. Every field except one intersects with the others, and the one exception — day of month versus day of week — does the opposite of what intuition demands. Sunday answers to both 0 and 7. The friendly @daily shortcuts are extensions, not part of the standard. And there is no timezone field anywhere in the syntax, so every expression silently inherits whatever clock its host uses.

This guide walks the five fields, the operators, the day-field trap, the shortcuts, and the timezone reality, ending with how to test an expression before you bet a production job on it. The tester linked here runs entirely in your browser — the expression never leaves the tab.

The fastest way to make these rules stick is to break them on purpose and watch a parser react, so keep the Cron Expression tester open in the next tab: it describes what your expression means and lists the next run times, computed locally.

Modern schedules usually live in deployment files rather than crontabs, so when your expression ships inside a pipeline definition, the YAML basics guide covers the indentation, quoting and typing rules that file obeys.

The five fields

A standard cron expression is exactly five fields separated by spaces, in a fixed order: minute, hour, day of month, month, day of week. A job fires when every field matches the current time — the fields are ANDed together. Write 30 4 * * * and the job runs at 04:30 every day; every field must agree for the moment to count.

Each field accepts numbers within its range, and the month and day-of-week fields also accept names — JAN-DEC and SUN-SAT — which some schedulers accept case-insensitively. The ranges are worth memorising because two of them start from an unexpected place: months count from 1, and days of week count from 0. Everything else about cron syntax is operators layered onto these fields.

FieldPositionAllowed valuesNames accepted
Minute1st0-59—
Hour2nd0-23—
Day of month3rd1-31—
Month4th1-12JAN-DEC
Day of week5th0-7 (0 and 7 are both Sunday)SUN-SAT

Ranges, steps and lists

Three operators do most of the work in real expressions. A range, 1-5, matches every value between its endpoints inclusive. A step, written after a slash, matches every Nth value of the field or range that precedes it — */15 in the minutes field means 0, 15, 30, 45, and 9-17/2 in the hours field means every second hour from 9 to 17. A list, 1,3,5, matches exactly the values named, and the operators compose: 1-5,10 and 0-30/5 are both legal.

Two habits keep steps honest. First, */N always includes the field's minimum — so */15 hits minute 0, which is why fleets of */15 jobs all wake at the top of the hour together. Second, a step over an explicit range is often clearer than a list: 0-59/10 reads as “every ten minutes” in a way that 0,10,20,30,40,50 does not.

ExpressionReads as
0 9 * * 1-509:00, Monday to Friday
*/15 * * * *Every 15 minutes
0 0 1 * *Midnight on the 1st of every month
30 4 1,15 * SUN04:30 on the 1st, the 15th, and every Sunday
0 22 * * 1-522:00 on weekdays

An expression, tested

30 9 * * 1-5

What the tester says

At 09:30, on Monday through Friday.
Next runs (UTC):
  Mon 2026-09-21 09:30
  Tue 2026-09-22 09:30

The day-of-month versus day-of-week trap

Every pair of fields intersects — except day of month and day of week, which cron ORs whenever both are restricted. The classic surprise: 0 0 13 * 5 does not mean “Friday the 13th”. It means midnight on the 13th of the month, or midnight on any Friday — a job that runs twice some weeks and on dates you never intended. If either day field is *, the normal AND behaviour returns and only the other one matters.

The OR rule is not a bug; it is written into POSIX and Vixie cron's documentation, and it survives because it lets the two day fields serve different masters — a monthly job on the 1st can coexist with a weekly job on Sunday in one field pair. But it defeats intuition, so the defensive habits are: never restrict both unless you have counted the ORs, and when you truly need “Friday the 13th”, schedule daily and test the date in the command itself.

Sunday is 0 and 7, and the shortcuts are extensions

Day of week accepts both 0 and 7 for Sunday in Vixie cron, the implementation most systems descend from, and 7 is folded to 0 on the way in. It is a courtesy for ranges — SUN-SAT can be written 0-6 or 1-7 — but it is also a portability seam: some other implementations reject 7 outright, and a few quirks of parsing make 7 risky in lists. Writing 0 for Sunday is the portable choice.

The @-shortcuts — @reboot, @yearly, @annually, @monthly, @weekly, @daily, @midnight and @hourly — are likewise Vixie extensions, not part of the POSIX grammar. They are nevertheless supported nearly everywhere that claims cron compatibility, which makes them fine in practice and still worth expanding mentally: @daily is 0 0 * * *, midnight on every day, and @reboot means once when the daemon starts, which may be sooner or later than you expect after a reschedule.

ShortcutExpands toMeaning
@reboot—Once, when the cron daemon starts
@yearly / @annually0 0 1 1 *Midnight, January 1st
@monthly0 0 1 * *Midnight on the 1st of each month
@weekly0 0 * * 0Midnight every Sunday
@daily / @midnight0 0 * * *Midnight every day
@hourly0 * * * *Top of every hour

The timezone field that isn't there

Cron has no timezone field. An expression is evaluated against the daemon's clock — the server's local system time — so the same crontab means different times on machines in different zones, and moving a job between a Tokyo host and a Frankfurt one silently moves its schedule. Containers make this sharper: most run in UTC regardless of where their operators sit, so “when it runs” and “when I meant” drift apart by an offset nobody announced.

Daylight saving adds its own hazards in any local-time zone: a 02:30 job does not exist on the spring-forward night, and a 01:30 job runs twice on the fall-back night. Some implementations accept non-portable patches — a TZ or CRON_TZ line in the crontab, or a timezone in a scheduler's own config — but support varies and portability ends where those lines begin. The honest practice is to know which clock your expression runs on; this site's tester computes everything in UTC and says so next to every result.

Testing an expression before shipping it

Some expressions are legal and unsatisfiable — 0 0 31 2 * asks for February 31st, and no naive search loop over dates ever finds it, which is why a real tester bounds its search by years and reports a never-firing schedule instead of hanging. Other expressions are legal and merely not what you meant; the 13th-or-Friday trap produces schedules that look right for days before surprising you.

The workflow that catches both: write the expression, have it described back to you in words, and look at the next few concrete run times. A description says what a parser thinks you wrote; concrete timestamps show where it will actually fire. The tester on this site does both — five-field standard syntax plus six- and seven-field Quartz-style flavours — entirely in your browser, so an expression containing internal job names or hostnames never leaves the tab.

Frequently asked questions

Does the cron tester upload my expressions or job names?

No. Parsing, description and next-run calculation all happen in JavaScript inside your browser tab; the expression never leaves your machine. Confirm it in DevTools: open the Network panel, evaluate an expression, and watch the request list stay empty. The site's Content-Security-Policy also forbids outbound connections from page scripts.

Why doesn't 0 0 13 * 5 mean Friday the 13th?

Because cron ORs the day fields when both are restricted: this schedule fires at midnight on the 13th of the month or on any Friday, whichever comes first — not on Friday the 13th. It is defined behaviour, in POSIX and Vixie cron alike. To catch the actual coincidence, schedule the job daily and test the date in the command it runs.

Is 7 a valid day-of-week value?

In Vixie cron and its descendants, yes — both 0 and 7 mean Sunday, with 7 folded to 0 on the way in. But it is the least portable corner of the syntax: some implementations reject 7 outright, and it behaves oddly in lists and ranges there. Writing 0 for Sunday costs nothing and avoids the disagreement entirely.

What timezone does a cron expression run in?

Whatever clock the daemon uses — the server's local system time, since cron has no timezone field. Containers usually mean UTC. Some implementations accept a TZ or CRON_TZ line in the crontab, but that is non-portable, and local-time zones add daylight-saving quirks where 02:30 jobs skip a night and 01:30 jobs run twice. Know the clock before trusting the schedule.

Are @daily, @weekly and @reboot standard cron?

They are Vixie cron extensions rather than POSIX, but support is close to universal in things that call themselves cron. Worth knowing what they expand to: @daily is 0 0 * * *, @weekly is 0 0 * * 0, and @reboot fires when the daemon starts — which, after a machine reschedules or a container restarts, is whenever that happens, not a fixed time.

Why won't my job run on the last day of the month?

Cron has no “last day” syntax — day-of-month stops at 31, and 31 matches only in seven months of the year. The standard workaround is a small shell test: schedule for midnight daily and run only if tomorrow is the 1st. Schedulers with richer calendars (systemd timers, Quartz with its L flag) express it natively; POSIX cron deliberately does not.