The five fields, and what may be written in them
The order is minute, hour, day of month, month, day of week, and each field holds a number from its own range: 0-59, 0-23, 1-31, 1-12, 0-7. Anything else in a field is one of four shapes. A range is `1-5` or `MON-FRI`, where names are a courtesy in the month and weekday fields and mean exactly what the numbers do. A list is `1,15,30`. A step is written with a slash and means "every n": `*/15` in the minute field is 0, 15, 30 and 45.
The shape people misread most is a step that follows a number rather than a star. `3/10` does not mean "the 3rd, and then every tenth" - it means "from 3 to the end of the range, every 10", so it selects 3, 13, 23, 33, 43 and 53. The field table on this page prints the values a field actually selected, which is the quickest way to see whether a step did what was intended.
Ranges do not wrap around. `50-10` in the minute field is refused rather than read as an hour spanning midnight, because implementations do not agree on what that would mean - write it as two parts, `50-59,0-10`. Names are case-insensitive, and Sunday may be written `0` or `7` wherever a dialect counts from zero, which is a small mercy and the source of a large confusion.
Five fields or six, and why the sixth one is dangerous
Standard cron has five fields. Everything else is an extension, and the extensions disagree about where the extra field goes. This page reads three shapes. The five-field form is the one in a Linux or macOS crontab and in GitHub Actions schedules. The six-field form taken by `node-cron`, Go’s `robfig/cron` and Spring’s `@Scheduled` puts **seconds first**. Quartz, the Java scheduler, also puts seconds first but adds an optional **year at the end**, so a Quartz expression has six fields or seven.
The trap is that a six-field expression can be valid in more than one of these readings. `0 0 12 * * ?` is Quartz for "noon every day" and is rejected outright by a reading that expects seconds first, because `?` is not a number - a refusal rather than a silent difference, which is the good outcome. The difference is silent for expressions that avoid the Quartz-only symbols: `0 0 12 * * 1` is noon on Sunday to Quartz and noon on Monday to everything that counts Sunday as zero.
Two further shapes are worth knowing even though this page does not read them. AWS EventBridge and CloudWatch put the **minute first and the year last** - `cron(0 12 * * ? *)` - so the same six tokens can mean a completely different schedule there. Jenkins replaces `H` with a number derived from the job name before cron ever sees the expression, and systemd timers use `OnCalendar`, which is not cron syntax at all. Each of those is named in the refusals rather than being read as something it is not.
Day of month and day of week are joined with or
This is the trap that catches people for years. When *both* day fields name particular days, Vixie cron runs the job when **either** matches, not when both do. `0 0 1 * 0` is not "the 1st of the month, when it happens to be a Sunday": it is midnight on the 1st, and also midnight every Sunday. To get "the 1st only when it is a Sunday" the test has to move into the command itself.
A field counts as naming days when it was not written with a leading `*`. That is why `*` and `0-6` are not interchangeable even though both cover every day: `0 0 * * 0-6` fires every day and names a weekday, while `0 0 * * *` names nothing. Quartz solves the same ambiguity by refusing to accept both, requiring a `?` in one of the two fields - and joining what is left with "and" rather than "or".
Quartz also has the day syntax the others lack. `L` in the day-of-month field is the last day of the month, `LW` the last weekday of the month, and `15W` the weekday nearest the 15th - which stays inside the month, so if the 1st is a Saturday then `1W` fires on Monday the 3rd. In the day-of-week field `6#3` is the third Friday and `6L` is the last Friday. Vixie cron has none of these, which is why "the last day of the month" usually becomes a shell test on `date`.
Run times, time zones, and the two hours a year that misbehave
A cron expression names a wall clock, not an instant, so its run times only mean something in a zone. This page reads the expression in the zone you choose and prints both: the wall clock with its offset (`Wed 2026-09-16 09:00`, `UTC+02:00`), and the same moment written absolutely in UTC, which is the form a log search wants. The offset shown is the one in force at that moment rather than today, so a summer job shows the summer offset.
Daylight saving is where a wall clock stops being a total order. When the clocks go forward, the hour that would have been 02:00-03:00 never happens, so a job scheduled at 02:30 has no 02:30 that day. When they go back, that same hour happens twice and so does 02:30. Real implementations disagree about what to do, so this page does not guess: a reading the zone skipped is reported as skipped, and one it repeated is used at its first occurrence, which is when a job on a real machine would run first.
The search walks the calendar rather than a ticking clock, which is why a schedule as rare as 29 February is answered properly: it tests days, not minutes, and reports the runs it found as `2028-02-29`, `2032-02-29` and so on. It looks thirty years ahead, says so when it stops, and never lists a wall-clock time that does not exist in the zone you asked about.