What Is a Cron Expression? Explainer, Syntax & Next Run Times
What is a cron expression?
A cron expression is a short line of five fields — minute, hour, day of month, month and day of week — that tells a scheduler when to run a job. Each field holds a number, a list, a range or * for “every”, so 0 9 * * 1-5 means 09:00 on every weekday. Cron runs the command whenever the current time matches all five fields.
┌───────────── minute (0–59) │ ┌─────────── hour (0–23) │ │ ┌───────── day of month (1–31) │ │ │ ┌─────── month (1–12) │ │ │ │ ┌───── day of week (0–6, Sunday = 0) │ │ │ │ │ * * * * * command to run */5 9-17 * * 1-5 → every 5 min, 09:00–17:55, Mon–Fri
| Expression | Runs |
|---|---|
* * * * * | every minute |
*/5 * * * * | every 5 minutes |
0 * * * * | every hour, on the hour |
0 0 * * * | every day at midnight |
0 9 * * 1-5 | 09:00 on weekdays |
0 0 1 * * | first day of every month |
* any value · , list (1,15) · - range (1-5) · / step (*/10). Some platforms add a seconds or year field — see the common schedules or paste your own below.
Paste a cron expression and read it back in plain English, with the next 12 run times in
whatever timezone you actually care about. The catch nobody warns you about is that
“cron” is not one language. 0 0 * * 1 means Monday on
Linux and Sunday in Quartz. A five-field string is a syntax error to Spring. ?
is mandatory on AWS and meaningless on a server. So this tool parses against the platform you
pick — and then shows you what the same string would do on the other seven.
Guide — read any cron expression in five steps
Unix crontab, Kubernetes, Quartz, Spring, AWS EventBridge, Azure, Jenkins or GitHub Actions. Field count and day numbering differ, so this decides how the string is read.
It is read back in plain English as you type, and a syntax error names the field it came from. No expression yet? Type it in words or use the builder.
Choose the zone the scheduler runs in. The next 12 runs show in that zone with the UTC instant alongside.
The OR rule between the two day fields, uneven steps such as */7, months without the chosen day, and runs that daylight saving skips or repeats.
See what the same string does on the other seven schedulers, copy the converted version, or grab a ready-made crontab line with locking and logging.
Which panel answers which question
- verdict
- the expression in one plain-English sentence
- fields
- minute, hour, day, month, weekday labelled
- examples
- common schedules, one click to load
- next runs
- the next 12, in your zone and UTC
- week grid
- the coming week, hour by hour
- clock changes
- the next DST jumps and what they do
- cross-check
- the same string on all eight schedulers
- convert
- rewritten for each, incl. systemd
- snippets
- lines with
flock, logging andTZ= - audit
- paste a crontab, every line explained
- overlap
- can the next run start on top of the last?
Common cron schedules — the ones people look up
Every line here is a working expression in standard five-field cron. Click use to load one into the parser above and see its next runs in your timezone; the URL updates, so the result is shareable.
* * * * * | Every minute | |
*/2 * * * * | Every 2 minutes | |
*/3 * * * * | Every 3 minutes | |
*/5 * * * * | Every 5 minutes | |
*/10 * * * * | Every 10 minutes | |
*/15 * * * * | Every 15 minutes (quarter past, half past, quarter to, on the hour) | |
*/20 * * * * | Every 20 minutes | |
*/30 * * * * | Every 30 minutes | |
0,30 * * * * | Twice an hour — on the hour and at half past | |
5 * * * * | At five past every hour | |
*/5 9-17 * * 1-5 | Every 5 minutes during office hours, weekdays only |
0 * * * * | Every hour, on the hour | |
0 */2 * * * | Every 2 hours | |
0 */3 * * * | Every 3 hours | |
0 */4 * * * | Every 4 hours | |
0 */6 * * * | Every 6 hours — 00:00, 06:00, 12:00, 18:00 | |
0 */8 * * * | Every 8 hours | |
0 */12 * * * | Every 12 hours | |
0 9-17 * * * | Every hour between 09:00 and 17:00 | |
0 9-17/2 * * 1-5 | Every 2 hours during the working day, weekdays | |
30 */6 * * * | At half past, every 6 hours |
0 0 * * * | Every day at midnight | |
0 1 * * * | Every day at 01:00 | |
0 2 * * * | Every day at 02:00 — the usual slot for backups | |
30 2 * * * | Every day at 02:30 | |
0 3 * * * | Every day at 03:00 | |
0 6 * * * | Every day at 06:00 | |
0 8 * * * | Every day at 08:00 | |
0 9 * * * | Every day at 09:00 | |
0 12 * * * | Every day at noon | |
0 17 * * * | Every day at 17:00 | |
0 18 * * * | Every day at 18:00 | |
0 22 * * * | Every day at 22:00 | |
0 0,12 * * * | Twice a day — midnight and noon | |
15 14 * * * | Every day at 14:15 |
0 9 * * 1-5 | Weekdays at 09:00 | |
0 18 * * 1-5 | Weekdays at 18:00 | |
*/30 9-17 * * 1-5 | Every half hour during office hours, weekdays | |
0 0 * * 0 | Every Sunday at midnight | |
0 0 * * 1 | Every Monday at midnight | |
0 9 * * 1 | Every Monday at 09:00 — the weekly report slot | |
0 9 * * 2 | Every Tuesday at 09:00 | |
0 9 * * 3 | Every Wednesday at 09:00 | |
0 9 * * 4 | Every Thursday at 09:00 | |
0 17 * * 5 | Every Friday at 17:00 | |
0 0 * * 6 | Every Saturday at midnight | |
0 0 * * 6,0 | Weekends at midnight | |
0 3 * * 0 | Sunday at 03:00 — the weekly maintenance window |
0 0 1 * * | The 1st of every month at midnight | |
0 6 1 * * | The 1st of every month at 06:00 | |
0 0 1,15 * * | The 1st and the 15th | |
0 0 15 * * | The 15th of every month | |
0 4 8-14 * 0 | The first Sunday of the month — the standard Unix idiom | |
0 4 1-7 * 1 | The first Monday of the month (same idiom) | |
0 0 L * * | The last day of the month — Quartz and AWS only | |
0 0 1 */3 * | The first day of every quarter | |
0 0 1 1,4,7,10 * | Quarterly, written out — January, April, July, October | |
0 0 1 */6 * | Twice a year | |
0 0 1 1 * | Once a year, on 1 January | |
0 0 25 12 * | Once a year, on 25 December |
@hourly | Shorthand for 0 * * * * | |
@daily | Shorthand for 0 0 * * * (also @midnight) | |
@weekly | Shorthand for 0 0 * * 0 | |
@monthly | Shorthand for 0 0 1 * * | |
@yearly | Shorthand for 0 0 1 1 * (also @annually) | |
@reboot | Once, when the machine boots — not a schedule at all | |
0 0 29 2 * | 29 February — runs only in leap years | |
0 0 31 * * | The 31st — which seven months of the year actually have | |
0 0 13 * 5 | The OR trap: every 13th AND every Friday, not Friday the 13th |
When a cron job silently does not run
Cron almost never tells you that something failed. It has no log of its own beyond a line saying it started the command, it mails output to an address that usually goes nowhere, and a schedule that can never match is not an error. These are the causes, in the order they actually happen:
| Symptom | Usual cause | Fix |
|---|---|---|
| Works in the shell, not in cron | Minimal PATH and no profile | Absolute paths, or PATH= at the top of the crontab |
| Nothing in any log | Output was mailed and dropped | >> /var/log/job.log 2>&1 |
| Runs an hour early or late twice a year | Daylight saving | Run in UTC, or avoid 01:00–03:00 local |
| Runs far more often than intended | Both day fields set — the OR rule | Leave one of them * |
| Command stops at a percent sign | % is a newline in crontabs | Escape it as \% |
| Skipped every time the machine sleeps | Cron does not catch up | systemd timer with Persistent=true, or anacron |
| Several copies running at once | A run overran its interval | flock -n, or concurrencyPolicy: Forbid |
| Nothing at all, on a fresh file | Missing trailing newline | End the crontab with a blank line |
The audit box on this page checks a pasted crontab for most of these — the unescaped %, the missing
redirect, relative command paths with no PATH, duplicate lines and midnight pile-ups — and shows the next run
time for every line so you can see at a glance which ones are dead.
cron, systemd timers, and which one to reach for
A timer unit is two files where cron is one line, and it buys three things cron cannot do at any price:
- Catch-up.
Persistent=trueruns a job that was missed while the machine was off, as soon as it comes back. Cron simply skips it. - Jitter.
RandomizedDelaySec=spreads a fleet, so five hundred machines do not hit the same endpoint at exactly 03:00. - Verification.
systemd-analyze calendar 'Mon..Fri *-*-* 09:00:00'prints the next elapse before you enable anything.
Cron still wins on portability — every Unix has it, containers ship it, and a single line in a file is hard to beat for
something trivial. The conversion panel above turns whatever you have typed into an OnCalendar= line, with one
honest caveat: cron treats day-of-month and day-of-week as an or, systemd treats them as an and, so a
schedule that uses both fields fires on fewer days as a timer than it does as a crontab line.
The five fields, and the sixth that catches people out
A classic crontab line is five fields separated by spaces, and they are always in this order:
| Position | Field | Range | Notes |
|---|---|---|---|
| 1 | minute | 0–59 | |
| 2 | hour | 0–23 | 24-hour clock, no AM/PM |
| 3 | day of month | 1–31 | see the OR trap below |
| 4 | month | 1–12 or JAN–DEC | |
| 5 | day of week | 0–7 or SUN–SAT | 0 and 7 are both Sunday |
Four characters do all the work. * means every value. , lists them (1,15). - is a range (9-17). / is a step, so */15 is every fifteenth. That is the entire language — everything else is a platform extension.
The sixth field is where it goes wrong. Quartz, Spring and Azure Functions put seconds first, so their expressions have six fields and everything shifts one place right. AWS EventBridge also uses six, but its extra field is a year on the end, not seconds at the front. Paste a five-field line into any of them and you get either a rejection or, worse, a schedule that runs sixty times more often than you meant. That is why this page asks which scheduler you are targeting before it tells you anything.
Day-of-month and day-of-week are an OR, not an AND
This is the single most expensive misunderstanding in cron, and it is genuinely counter-intuitive. In standard Vixie cron — Linux, Kubernetes, Jenkins, GitHub Actions — when both the day-of-month and day-of-week fields are restricted, the job runs when either one matches.
So 0 0 13 * 5 is not “midnight on Friday the 13th”. It is midnight on every 13th, and also midnight every Friday — about 60 runs a year instead of one or two. People discover this when a monthly report starts arriving weekly.
The rule only applies when both fields are restricted. If either is *, the other simply wins, which is why 0 0 * * 5 (every Friday) and 0 0 13 * * (every 13th) both behave exactly as you would expect.
There is no way to express a true AND in standard cron. The usual workaround is to schedule the broader of the two and test the date inside the job:
0 0 13 * * [ "$(date +\%u)" = "5" ] && /path/to/job
Quartz and AWS EventBridge dodge the problem entirely by making you write ? in whichever day field you are not using, so only one of them is ever active. This tool warns you the moment both fields are set on a dialect where the OR applies.
Timezones and daylight saving break more cron jobs than syntax errors do
Cron schedules wall-clock time, not elapsed time. It looks at the clock on the wall, and if the clock says the job is due, it runs. Twice a year that clock does something strange, and two predictable failures follow.
Spring forward: the run that never happens
When clocks jump from 02:00 to 03:00, the hour in between does not exist. A job scheduled for 30 2 * * * is simply skipped that day — no error, no log line, no alert. If that job was your nightly backup, you have a hole in your backups exactly once a year, and nothing tells you.
Fall back: the run that happens twice
When clocks go from 03:00 back to 02:00, the 02:00–03:00 hour is replayed. That same job runs twice, roughly an hour apart. If it is not idempotent — a billing run, an email send, a counter increment — you have just done it to every customer twice.
The run list on this page flags both cases explicitly, marking skipped runs and repeated hours against whichever timezone you select. The practical fix is to avoid scheduling anything between 01:00 and 03:00 local time, or to run in UTC, which has no daylight saving at all. Running in UTC means your “9am job” drifts an hour twice a year relative to office hours — usually the lesser evil, and always the more predictable one.
Where the timezone comes from also varies. A Linux crontab uses the server timezone, or a TZ= line at the top of the file. A Kubernetes CronJob is UTC unless you set spec.timeZone, which only exists from 1.27. AWS EventBridge cron() rules are UTC with no option at all. GitHub Actions is UTC, always.
The same string means different things on different platforms
Eight schedulers, eight sets of rules. These are the differences that actually change what runs when:
| Platform | Fields | Sunday is | Seconds | ? | L / W / # |
|---|---|---|---|---|---|
| Unix / crontab | 5 | 0 or 7 | no | no | no |
| Kubernetes CronJob | 5 | 0 or 7 | no | no | no |
| Quartz | 6 or 7 | 1 | yes, first | required | yes |
| Spring @Scheduled | 6 | 0 or 7 | yes, first | allowed | no |
| AWS EventBridge | 6 | 1 | no (year last) | required | yes |
| Azure NCRONTAB | 6 | 0 | yes, first | no | no |
| Jenkins | 5 | 0 or 7 | no | no | no (has H) |
| GitHub Actions | 5 | 0 or 7 | no | no | no |
The day numbering is the dangerous one, because a Quartz expression copied from a Linux crontab is still valid — it just runs a day early, every time, for ever. In Quartz and AWS, Sunday is 1 and Saturday is 7. Everywhere else Sunday is 0. So 1 means Monday on your server and Sunday in your Java scheduler, and nothing will ever tell you.
The comparison panel above the examples shows, for whatever you have typed, which of the eight accept it and which of those give it a different meaning. If you are migrating a schedule between platforms, that panel is the whole reason this page exists.
Steps, ranges and the ones that quietly misfire
*/n reads as “every n”, and mostly behaves — but only when n divides the field range evenly. */15 in the minute field gives you 0, 15, 30, 45 and then wraps cleanly. */7 gives you 0, 7, 14, 21, 28, 35, 42, 49, 56 — and then the next run is 0, which is four minutes later, not seven. Every hour has one short gap. The same applies to */7 in hours, */45 in minutes, and anything else that does not divide its range.
A step can also start somewhere other than zero: 5/10 means “from 5, every 10” — 5, 15, 25, 35, 45, 55. And a range can carry a step, so 0-30/5 is every five minutes for the first half hour only.
Days that do not exist
0 0 31 * * looks monthly but runs seven times a year, because April, June, September and November have no 31st. 0 0 30 2 * parses perfectly and can never run. If you want the last day of the month on Linux there is no L — the idiom is to run on the 1st and subtract a day, or to check inside the job with [ "$(date -d tomorrow +\%d)" = "01" ]. On Quartz and AWS you can simply write L.
This page flags all of these as you type, along with the frequency: if an expression fires 1,440 times a day it will tell you, because a job that runs every minute and sometimes takes 70 seconds will happily start overlapping copies of itself until the box falls over. flock, a lock file, or concurrencyPolicy: Forbid on Kubernetes.
How to read and write a cron expression
A cron expression is five fields — minute, hour, day of month, month, day of week — that tell a scheduler when to run a job. The explainer above reads any expression back in plain English; the sections below cover the questions that come up once you start writing them yourself.
Cron, a cron job and a crontab
Cron is the background service (daemon) on Linux and macOS that runs scheduled commands. A cron job is one scheduled command. The crontab (cron table) is the file that lists your cron jobs, one per line: a cron expression followed by the command.
Edit it with crontab -e and list it with crontab -l.
How many fields: five, six or seven
Standard Unix cron and Kubernetes use five: minute, hour, day of month, month, day of week. Quartz and Spring add seconds at the start (six), Quartz allows an optional year at the end (seven), and AWS EventBridge uses six fields with a year and needs ? in one of the day fields.
How to read a cron expression
Read it left to right as minute, hour, day of month, month, day of week. 30 4 * * 1 is: minute 30, hour 4, any day of the month, any month, day-of-week 1 — so 04:30 every Monday.
The trick is that the fields you leave as * are the ones that set the frequency. A * in the day-of-month and month fields means “daily”; putting a number in the day-of-week field narrows it to weekly. Paste any expression into the box above and it will read it out for you.
What * * * * * means
Every minute of every hour of every day — 1,440 runs a day, 10,080 a week. It is the most frequent schedule plain cron can express, since the smallest unit is one minute.
If a run can ever take longer than sixty seconds, cron starts the next one anyway and they overlap. Wrap the command in flock -n /tmp/job.lock, or set concurrencyPolicy: Forbid on a Kubernetes CronJob.
How to run a cron job every 5 minutes
*/5 * * * *. The */5 in the minute field means “every fifth minute” — 0, 5, 10, 15 and so on. The four *s after it mean every hour, every day, every month, every weekday.
The same pattern gives you the rest: */10, */15, */30. Only use divisors of 60, or the wrap-around gap will be shorter than the interval — see the warning this tool shows for */7.
How to run a job at a specific time every day
Put the minute and hour in the first two fields and leave the rest as *. 30 3 * * * is 03:30 daily. The hour is a 24-hour clock, so 6pm is 18, not 6.
For several times a day, list them: 0 9,13,17 * * * runs at 09:00, 13:00 and 17:00. For a window, use a range: 0 9-17 * * * is hourly through office hours.
*/5 versus 5
5 means exactly the value 5 — minute 5, once an hour. */5 means every fifth value — minutes 0, 5, 10, 15… twelve times an hour. One character, twelve times the load.
There is also 5/10, which is “start at 5, then every 10”, giving 5, 15, 25, 35, 45, 55.
Is Sunday 0 or 1 in cron?
It depends on the scheduler, and this is the difference most likely to cost you a day.
- Unix, Kubernetes, Jenkins, GitHub Actions, Spring: Sunday is
0. Monday is 1, Saturday is 6, and7is accepted as Sunday too. - Quartz and AWS EventBridge: Sunday is
1. Monday is 2, Saturday is 7. There is no 0. - Azure NCRONTAB: Sunday is
0; Microsoft documents the range as 0–6.
So 0 0 * * 1 is Monday on a server and Sunday in Quartz. Using the three-letter names — MON, FRI — sidesteps the whole problem, and every dialect here accepts them.
First Monday of the month
On Quartz or AWS EventBridge there is direct syntax: 2#1 in the day-of-week field means “the first Monday” (remember Sunday is 1 there, so Monday is 2). The full Quartz expression is 0 0 9 ? * 2#1.
On Linux there is no #, so the idiom is to combine a day-of-month range with a weekday: 0 9 1-7 * 1 — but remember the OR rule makes that “days 1–7 or any Monday”, which is wrong. The correct version tests inside the job: 0 9 1-7 * * [ "$(date +\%u)" = "1" ] && /path/to/job.
Last day of the month
In Quartz and AWS EventBridge, use L in the day-of-month field. In plain Unix cron there is no such thing, and the idiom is to run every day late in the month and let the job decide: 0 23 28-31 * * [ "$(date -d tomorrow +\%d)" = "01" ] && /path/to/job. The same trick handles “last Friday” and similar rules that cron cannot express — schedule the superset, test the exact condition inside the job.
Which timezone cron uses
Whatever the machine or service is configured for, and it differs by platform:
- Linux crontab: the server's local timezone. You can override it per file with a
TZ=Europe/Brusselsline at the top. - Kubernetes CronJob: UTC, unless you set
spec.timeZone— stable since Kubernetes 1.27. - AWS EventBridge
cron(): always UTC, with no option. Use EventBridge Scheduler instead if you need a real timezone. - GitHub Actions: always UTC.
- Quartz / Spring: the JVM default unless you set one explicitly.
Pick a zone in the selector above and the run list is recalculated in it, with the UTC instant shown alongside so you can check what the server will actually see.
Should cron jobs run in UTC?
Usually yes, for anything machine-facing. UTC has no daylight saving, so every day is exactly 24 hours and every schedule is exactly as frequent as it looks. Logs from different regions line up. Nothing runs twice, and nothing is skipped.
The cost is that a UTC job drifts an hour relative to local office hours twice a year. For a nightly batch nobody notices; for “email the team at 9am” it matters, and that is the case where a real timezone earns its complexity.
AWS EventBridge cron expressions
When an expression that works on Linux fails in EventBridge, there are three likely reasons. Field count: EventBridge wants six fields, with a year at the end — 0 12 * * ? *, not 0 12 * * *. The question mark: exactly one of day-of-month and day-of-week must be ?; * * in both is rejected outright. Day numbering: Sunday is 1, not 0.
Also worth knowing: EventBridge cron() rules are always UTC, and the minimum resolution is one minute. Select AWS EventBridge above and this page will validate against exactly those rules.
The question mark ?
It means “no specific value” and it exists only to resolve the day-of-month versus day-of-week ambiguity. In Quartz and AWS EventBridge you must put ? in whichever of the two day fields you are not using, so only one is ever active — which is how those platforms avoid the OR trap that catches people on Linux.
Standard Unix cron does not understand ? at all. Paste one into a crontab and the line is rejected.
Kubernetes CronJob schedules
Kubernetes uses the crontab syntax — five fields, Vixie syntax, and the @daily-style macros all work. The differences are operational rather than syntactic:
- No
@reboot— a cluster has no single boot moment. - UTC by default, with
spec.timeZonestable since 1.27. startingDeadlineSecondsdecides how late a missed run may still start. If more than 100 scheduled runs were missed, the controller does not start the job and logs an error instead.concurrencyPolicyis how you stop overlapping runs — set it toForbidrather than reaching for a lock file.
GitHub Actions schedules that run late
Scheduled runs are best-effort, and GitHub documents this. Scheduled workflows go into a shared queue; at times of high load, the top of the hour especially, they start late and some queued runs are dropped. Scheduling at an odd minute such as 17 * * * * helps.
Two more rules catch people: the shortest interval is once every five minutes, and in public repositories scheduled workflows are disabled automatically after 60 days without repository activity. If a job must happen on time, trigger it from something that guarantees delivery and use Actions only as the runner.
@daily, @hourly and @reboot
They are shorthand macros:
| Macro | Equivalent | Meaning |
|---|---|---|
| @yearly / @annually | 0 0 1 1 * | midnight, 1 January |
| @monthly | 0 0 1 * * | midnight on the 1st |
| @weekly | 0 0 * * 0 | midnight on Sunday |
| @daily / @midnight | 0 0 * * * | every midnight |
| @hourly | 0 * * * * | every hour, on the hour |
| @reboot | — | once, at startup |
@reboot is the odd one out: it has no schedule at all, and it is not supported on Kubernetes, AWS, Azure or GitHub Actions. Nor are the other macros, on AWS and GitHub.
L, W and #
Quartz extensions, also supported by AWS EventBridge, and unavailable everywhere else.
Lin day-of-month is the last day of the month — 31 January, 28 or 29 February, 30 April.L-3is three days before that.Lafter a weekday is the last of that weekday:6Lis the last Friday in Quartz numbering.Wis the nearest weekday to a date, without leaving the month:15Won a Sunday the 15th moves to Monday the 16th.#picks the nth weekday:6#3is the third Friday.
None of these exist in standard cron. Select Quartz or AWS above to use them and see the real dates they resolve to.
Every 30 seconds, or every second
Standard cron cannot do it: one minute is the floor, because there is no seconds field. Schedulers that do have one are Quartz, Spring @Scheduled and Azure NCRONTAB, all of which put seconds first: */30 * * * * * is every thirty seconds in Spring.
On a plain server the usual workaround is a one-minute job that loops internally with sleep, or a systemd timer with OnUnitActiveSec, which handles sub-minute intervals properly and logs its runs.
For a job every 30 seconds there are three honest options: schedule it every minute and have the script sleep 30 and run again; use a systemd timer with OnUnitActiveSec=30s; or use a scheduler that has a seconds field, which means Quartz, Spring or Azure Functions. The sleep trick is fine for something trivial and terrible for anything that can overrun, because now two copies can overlap inside the same minute.
Works in the shell, not in cron
Your shell is not cron's shell. An interactive login reads /etc/profile, ~/.bashrc and ~/.profile; cron reads none of them. You get /bin/sh, a PATH of roughly /usr/bin:/bin, no aliases, no virtualenv, no nvm, no SSH agent and no HOME-relative assumptions. The fix is to make the job self-contained: absolute paths to every binary, explicit environment variables in the crontab or sourced at the top of the script, and a cd into the working directory rather than relying on where cron starts you. Test it the way cron will run it: env -i /bin/sh -c '/path/to/job.sh'.
Missed runs are not caught up
Cron looks at the clock, and if the machine was asleep when the minute passed, that run simply never happened. This is what anacron was invented for on laptops and desktops, and what systemd timers do with Persistent=true, which runs the job as soon as the machine comes back. If a missed run matters — a backup, an invoice batch — do not use plain cron on a machine that is not always on.
Stopping two runs from overlapping
Cron will happily start a second copy while the first is still going, and that is how a five-minute job on a five-minute schedule turns into forty copies competing for the same database. On Linux, wrap the command in flock -n /tmp/myjob.lock, which simply does nothing if the lock is held. On Kubernetes set concurrencyPolicy: Forbid on the CronJob. In any language, a lock file or an advisory database lock at the top of the job does the same thing. The overlap check above compares how long you say the job takes against the shortest gap in the schedule.
What % means in a crontab
It is the single most surprising character in the file. In a command, an unescaped % is turned into a newline, and everything after the first one is fed to the job on standard input. That is why date +%Y-%m-%d in a crontab is cut off at the first %: date only receives +, and the rest goes to its standard input. Escape every one of them as \%, or put the command in a script file where % behaves normally — which is the better habit anyway.
Testing a cron expression without waiting
Read the next run times above: they are computed with the same day-matching rules the scheduler uses, in the zone you select, with daylight-saving transitions applied. For the command itself, run it the way cron will: env -i /bin/sh -c 'your command here'. On systemd, systemd-analyze calendar 'Mon..Fri 09:00' prints the next elapse. On Kubernetes, kubectl create job --from=cronjob/myjob manual-run-1 runs it immediately without touching the schedule.
How accurate the run times are
The run times are computed in your browser with the same day-matching rules the scheduler uses — including the day-of-month / day-of-week OR rule, steps that do not divide evenly, leap years and month ends — in the timezone you pick, with daylight-saving transitions applied.
What the page cannot know is whether your server is up, whether the previous run is still going, or whether the machine’s clock is right. It tells you when the schedule fires, not whether the job succeeds.
Sharing an expression
The link button copies a URL carrying the expression, the platform and the timezone, so whoever opens it sees exactly what you saw rather than their own defaults. The address bar updates as you type, so a plain bookmark works too.
Privacy
Nothing you type is sent anywhere. The parser, the schedule calculation and the timezone conversions all run in the page, on your device. There is no API call and no logging of what you type; the only analytics on the site are privacy-first page counts. You can open the network tab and watch nothing happen, or disconnect entirely — the page keeps working.