Cron expression explainer

Explains any cron expression — Vixie, Quartz, EventBridge or Jenkins — lists the next runs in any time zone, and flags the traps.

Input

Read as Vixie cron.

What it means

At 00:00 on day-of-month 13 or on Friday

Field by field

FieldWrittenMatches
Minute00
Hour00
Day of month1313
Month*every value
Day of week5Friday

What to watch for

  • Both day fields are restricted, so cron runs this when either one matches — the day of the month or the day of the week, not both together. That is why 13 with Friday means the 13th and every Friday, not Friday the 13th.

Next runs

    What this tool does

    It reads a cron expression back to you in words, lists the next times it will actually fire in whichever time zone the job runs in, and names the things the expression implies but does not say. That last part is the point: a cron expression is never wrong in a way that produces an error. It runs on the wrong days, quietly, until somebody notices.

    Four dialects are understood rather than one. The five-field form that crontab takes, Quartz with its seconds field and its calendar rules, AWS EventBridge with a year on the end, and Jenkins with its hash. Pasting one into a tool that only knows another is how most people first discover there is more than one.

    The fields, and how many of them there are

    The classic expression has five fields, separated by spaces, in a fixed order: minute, hour, day of month, month, day of week. Each is either a star meaning "every", a number, a list separated by commas, a range with a dash, or a step with a slash. Months and days may be written as three-letter names, which is almost always clearer than the digits.

    • A star means every value the field can take.
    • A range such as 9-17 means every value from 9 through 17 inclusive.
    • A step such as */15 means every 15th value starting from the bottom of the field; 9-17/2 steps inside a range, and 5/15 steps from 5 to the top.
    • A list such as 0,30 means exactly those values.
    • A degenerate range such as 13-13 is simply 13 — some libraries get this wrong and treat it as a star.

    A sixth field is where the dialects part company. Quartz puts it at the front and reads it as seconds; EventBridge puts it at the end and reads it as a year. Six bare fields are therefore genuinely ambiguous, and this tool says which way it read them rather than picking silently. Wrapping the expression in cron(...) — EventBridge’s own syntax — settles it.

    The macros are shorthand: @hourly, @daily, @midnight, @weekly, @monthly, @yearly and @annually. They expand to ordinary expressions, which the tool shows. @reboot is the odd one out and is not a schedule at all.

    The two day fields, and the "or" that looks like an "and"

    A date can be picked in two different ways — by the day of the month and by the day of the week — and cron has both fields. When both of them are restricted, the job runs when either one matches. Not both.

    So 0 0 13 * 5 is not Friday the 13th. It is the 13th of every month, and every Friday: about 64 days a year instead of one or two. The expression looks like a conjunction and behaves like a disjunction, nothing reports an error, and the job simply runs more often than intended.

    The rule has a second half that is stranger, and it is the one almost nobody knows. Cron does not test whether a day field is restricted; it tests whether the field’s first character is a star. So a step like */14 counts as a star even though it restricts the field to the 1st, 15th and 29th — and its presence flips the two day fields from "or" back to "and". Two expressions that look equally restricted then behave completely differently, and this tool reports which rule is in force.

    Quartz and EventBridge avoid the whole question by refusing to let both fields say something: exactly one of them has to be a question mark, meaning "no specific value, the other field decides".

    Steps that do not divide their field

    A step is written as though it were an interval, and inside one cycle of the field it is. Across the boundary it usually is not.

    */7 in the hour field means hours 0, 7, 14 and 21. After 21 the field runs out, so the next run is at 0 the following day — three hours later, not seven. Every cycle ends with one short gap, and the same happens with */7 in minutes (a four-minute gap after 56) and any other step that does not divide its range evenly. */15 in minutes and */6 in hours are safe; most of the numbers people reach for are not.

    This matters when the step is chosen to space work out. A job on */7 hours does not run every seven hours, and a rate limit sized for seven will be breached once a day.

    Days of the week are numbered differently in different places

    Vixie cron numbers the days 0 to 7 with Sunday at both ends, so 0 and 7 are the same day and 1 is Monday. Quartz and EventBridge number them 1 to 7 with Sunday at 1, so there 1 is Sunday and 2 is Monday.

    An expression copied from a Quartz configuration into a crontab therefore runs a day early, and nothing anywhere complains, because both spellings are valid numbers in both systems. Three-letter names — MON, FRI — mean the same day everywhere and are the simple defence.

    Daylight saving: the two mornings a year

    A cron daemon does not schedule instants. It looks at the local wall clock each minute and asks whether the expression matches. Twice a year that clock is not a well-behaved sequence.

    When it jumps forward, an hour of readings never happens. A job set for 02:30 has no 02:30 that day. When it goes back, an hour of readings happens twice, and a job set for 01:30 has two of them.

    Vixie cron distinguishes two kinds of job here, and the distinction is worth knowing because it decides which of your jobs are affected. A job pinned to a wall-clock time — a fixed hour and a fixed minute — is run exactly once either way: after a forward jump it runs immediately, and after a backward one cron takes care not to repeat it. A job with a star in the hour or the minute is an interval job, and it simply follows the clock: it loses an hour of runs in spring and repeats an hour of them in autumn.

    So an hourly backup really does run twice on one night a year, and a nightly job at 02:30 really does run at an unusual moment on another. This tool checks the next twelve months of the chosen zone against your expression and says which of the two applies, with the date.

    The reliable way out is to schedule the job in UTC, or outside 01:00–03:00 local, where no transition lands.

    Which clock the job is actually on

    A crontab runs on the machine’s local zone, or on whatever CRON_TZ or TZ says at the top of the file. That is rarely the zone of the person reading the expression, which is why the time zone here is a setting rather than your browser’s.

    A Kubernetes CronJob is different again: it reads its schedule in UTC unless the manifest sets a timeZone field, whatever the node’s own clock says. A schedule written for local business hours and deployed without that field runs at the wrong time of day everywhere outside UTC — and does not change with the seasons, which is either a bug or a relief depending on the job.

    Jenkins and the H

    Jenkins adds a token no other dialect has: H, which looks like randomness and is not. Jenkins hashes the job’s name into a fixed value inside the field’s range, so the job runs at the same time every day — its own time — while a hundred jobs written H * * * * spread themselves evenly across the hour instead of all starting on the minute.

    Because the value comes from the job name, and the job name is not part of the expression, no tool can compute the exact times. This one resolves every H to the bottom of its range and says so: the shape of the schedule — how often, on which days — is exact, and only the offset inside each period is a stand-in.

    Jenkins also redefines the macros. Its @daily is @midnight, which is not midnight but a hashed minute somewhere in the first three hours of the day.

    What cron does not do

    • It does not catch up. If the machine was asleep or the daemon was down when a run was due, that run does not happen later — it is simply missed. anacron exists for exactly this and is a different program.
    • It does not stop overlapping runs. If a job takes longer than the interval, the next one starts anyway, and after a while there are several. A lock file is the usual answer.
    • It has no notion of seconds in the classic form. The finest resolution is one minute; anything faster needs Quartz, systemd, or a loop inside the job.
    • @reboot is not a schedule. It runs once when the daemon starts, so it has no next run, and on a machine that never reboots it never runs at all.
    • A rate expression in EventBridge counts from when the rule was created, and that moment is not in the expression, so no tool can say when it fires next.

    Frequently asked questions

    How do I run a job on the first Monday of the month?
    Not with a plain cron expression — that needs an "and" between the two day fields, and cron gives you an "or". The standard workaround is to schedule 0 0 1-7 * * and have the command test the weekday itself. Quartz and EventBridge can express it directly, as 0 0 12 ? * MON#1.
    Why does 0 0 13 * 5 run so often?
    Because it means the 13th or every Friday, not Friday the 13th. When both day fields are restricted and neither begins with a star, cron runs the job when either matches. There is no way to write an "and" in the classic dialect.
    What is the difference between */5 and 0-59/5?
    Nothing, for the minute field: both give 0, 5, 10 and so on. The difference shows up in the two day fields, where cron decides between "and" and "or" by looking at whether the field begins with a star — so */5 and 0-59/5 select the same days but combine with the other day field differently.
    Does my job run twice when the clocks go back?
    If it has a star in the hour or the minute, yes: it follows the clock, and that hour happens twice. If it has a fixed hour and minute, no — Vixie cron runs such a job once. The findings panel says which case yours is, with the date it applies to.
    Which time zone does a cron expression use?
    The machine’s, unless CRON_TZ or TZ is set in the crontab. A Kubernetes CronJob uses UTC unless the manifest sets timeZone. Since the zone is not part of the expression, this tool asks for it rather than assuming your browser’s.
    Can I schedule something every 90 minutes?
    Not as one expression, because each field cycles independently and 90 minutes does not fit into an hour. The usual spelling is two expressions, 0 0,3,6,9,12,15,18,21 * * * and 30 1,4,7,10,13,16,19,22 * * *, which together give a run every 90 minutes.
    Is anything I paste sent to a server?
    No. Parsing, explaining and the whole schedule calculation run in your browser; nothing is uploaded or logged, and it works with no network connection.