How to use
Paste a 5-field cron expression into the **Parser** tab and the tool validates each field — minute (0–59), hour (0–23), day-of-month (1–31), month (1–12 or `JAN-DEC`), day-of-week (0–6 or `SUN-SAT`, Sunday is 0) — and prints the next N upcoming fire times. Supported syntax: `*` (any), ranges `a-b`, lists `a,b,c`, steps `*/n` and `a-b/n`, and case-insensitive month/weekday names. When both day-of-month and day-of-week are restricted, this parser uses **AND** (POSIX behavior) rather than Vixie cron's OR — see the FAQ for the trade-off.
The **Builder** tab lets you compose an expression from a frequency picker instead. Pick *every minute / hour / day / week / month / year*, then refine — e.g. "every 15 minutes, weekdays only, 09:00–17:00" generates `*/15 9-17 * * 1-5`. The next-run preview uses the host browser's local time zone, which usually matches what `/etc/crontab` would do on a server in the same zone. If your production cron runs in UTC, mentally subtract or add the offset before deploying. The parser is self-contained — no network requests, no third-party cron library — so the expression and your machine's wall clock never leave the page.
Examples
Top-of-the-hour maintenance hook
Input
expression: 0 * * * *
starting: Mon 2026-05-18 14:00 local time
preview: next 5 runs
Output
Mon 2026-05-18 15:00
Mon 2026-05-18 16:00
Mon 2026-05-18 17:00
Mon 2026-05-18 18:00
Mon 2026-05-18 19:00
`0 * * * *` is the canonical "every hour on the hour" expression — minute is fixed at 0, every other field is `*`. A common mistake is writing `* * * * *` for "every hour", which actually fires every minute (60× more often). When you really want hourly, anchor the minute field. For sub-hour cadences use `*/15` or `0,15,30,45`; the two are equivalent for whole-hour-aligned schedules but `0,15,30,45` survives correctly across HUP/restart timing on Vixie cron, while `*/15` is read once at crontab parse time.
Weekday business-hours polling
Input
expression: */15 9-17 * * 1-5
interpret: every 15 minutes, 09:00–17:45, Monday–Friday
preview: next 4 runs after Fri 2026-05-15 17:45
Output
Mon 2026-05-18 09:00
Mon 2026-05-18 09:15
Mon 2026-05-18 09:30
Mon 2026-05-18 09:45
# Note: 17:00–17:45 inclusive on Friday, then the cron sleeps the entire
# weekend and resumes Monday 09:00. The 4× /hour cadence × 9 active hours
# × 5 weekdays = 180 fires per week.
A typical business-hours health check. The hour range `9-17` is *inclusive on both ends*, so 17:45 still fires. Day-of-week `1-5` means Monday through Friday (Sunday=0, Saturday=6). The combination of `*/15 9-17` produces 4 fires per hour × 9 hours = 36 fires per weekday, 180 per week. If you also need a final fire at 18:00 sharp before the system idles for the night, add it as an OR: switch to `0,15,30,45 9-17 * * 1-5` and add a separate line for `0 18 * * 1-5`. A single expression cannot OR distinct hour patterns.
Month-end report with DOM/DOW gotcha
Input
expression: 0 9 28-31 * 1
interpret: POSIX (AND) — only when the 28th-31st is also a Monday
Vixie (OR) — every day 28-31 OR every MondayOutput
POSIX behavior (this parser):
Mon 2026-12-28 09:00 # 28th is a Monday
Mon 2027-03-29 09:00 # next 28-31 + Monday match
...
Vixie crond behavior (most Linux distros):
Every day 28-31 of any month
PLUS every Monday
≈ 4 + 4 = 8 fires per month on average
This is the most-cited cron pitfall. POSIX § says day-of-month and day-of-week are ANDed when both are restricted (intersection); Vixie cron — which ships on most Linux distros via `cronie` or `cron` — ORs them (union). The same expression can fire 5× more often on a real production box than your local test. Use the parser to spot-check expressions where you restrict both fields. If you actually want monthly month-end runs, write `0 9 28-31 * *` with day-of-week left wide open, and add a date check inside the job itself: `[ "$(date -d tomorrow +%d)" = "01" ] && run.sh` only fires on the *true* last day.
FAQ
Why are there 5 fields here when I see 6 or 7 fields elsewhere?
Standard Unix cron — what `/etc/crontab` parses on Linux, BSD, and macOS — uses 5 fields: minute, hour, day-of-month, month, day-of-week. The 6-field variant adds a leading **seconds** field and is what Quartz Scheduler (Java) and many AWS / Spring schedulers accept. The 7-field variant adds a trailing **year** field and is Quartz-specific. The leading-seconds form is also used by some Go libraries like robfig/cron when configured with the `WithSeconds` option. If you paste a 6-field expression here, the parser will report "too many fields" — drop the seconds (or the year) and parse just the minute-onward middle five. Standard `crond` cannot fire faster than once per minute regardless of dialect.
Does cron support `@daily`, `@reboot`, and other shortcuts?
Vixie cron supports seven nicknames: `@yearly` / `@annually` (= `0 0 1 1 *`), `@monthly` (= `0 0 1 * *`), `@weekly` (= `0 0 * * 0`), `@daily` / `@midnight` (= `0 0 * * *`), `@hourly` (= `0 * * * *`), and `@reboot` (run once at system boot). The nicknames are a Vixie extension, not in POSIX, so they may not work on minimal busybox-cron used in some Docker base images. This parser does **not** accept nicknames in the parse box — paste the expanded 5-field form. `@reboot` has no equivalent expression because it is event-triggered (boot), not time-triggered; for similar "run once at startup" semantics, systemd timers with `OnBootSec` are the modern replacement.
What time zone does this parser use? My production cron runs in UTC.
The next-run preview uses your browser's local time zone (read via `Intl.DateTimeFormat().resolvedOptions().timeZone`). If your server runs in UTC and your browser is in JST or KST, the preview will be 9 hours later than what actually happens on the server. Most modern crons honor a `CRON_TZ=...` variable or `TZ=...` in the environment, set per-line or globally in `/etc/crontab`. On systemd timers, the `OnCalendar=` directive accepts an explicit zone like `OnCalendar=Mon..Fri 09:00 Asia/Tokyo`. Daylight saving makes the question worse: a job scheduled for 02:30 local time in a DST-observing zone will skip on the spring-forward day and run twice on the fall-back day under naive cron, which is why running production jobs in UTC is the standard recommendation.
How is `*/5` different from `0/5` or `0,5,10,15,...`?
In the minute field, all three forms are equivalent on POSIX/Vixie cron: they fire at minute 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55. The differences appear with non-zero starting points and non-standard ranges. `5/15` (Quartz) means "starting at 5, every 15" → 5, 20, 35, 50, which `*/15` does *not* produce. `1-30/5` means "every 5 minutes within 1–30" → 1, 6, 11, 16, 21, 26. POSIX/Vixie cron supports the `a-b/n` form but not the `a/n` (start-and-step) form — that's a Quartz extension. When in doubt, expand to an explicit list `0,5,10,...` for maximum portability. The list form is also what you want if you need to skip minutes — `0,3,6,9` fires 4 times per hour at irregular gaps.
Why does my Linux cron fire on different days than the parser predicts?
Almost certainly the DOM/DOW disagreement covered in example 3. This parser follows POSIX § (AND); the cron daemon shipping with most Linux distributions follows the Vixie convention (OR). To verify on your server, run `man 5 crontab` and search for "day-of-week" — the manpage will spell out the local behavior. Other possible causes: time zone mismatch between your editing environment and the server (see the UTC FAQ above), `cron` not running (`systemctl status cron`), the user's `MAILTO=` failing silently because mail isn't configured, or the user's `PATH` missing `/usr/local/bin` so the script's shebang resolves but its first command fails. Add `* * * * * date >> /tmp/cron.log` to a test crontab to confirm cron is firing at all before debugging expression semantics.
When should I use cron versus systemd timers or Kubernetes CronJob?
Use **cron** for shell-script-style automation on a single Linux host where you control `/etc/crontab` and uptime: log rotation, backups, simple polling, hobby projects. Use **systemd timers** when you already manage the service with systemd and want unified logging in `journalctl`, dependency ordering (run *after* `network-online.target`), randomized delays to avoid thundering-herd, or persistence across boots for missed runs (`Persistent=true`). Use **Kubernetes CronJob** when the workload runs in a cluster — you get pod-level resource isolation, automatic restart/retry, and observability through your normal k8s tooling, at the cost of significant cold-start latency (10–30 s for a tiny job). For cloud-native one-offs without a cluster, **AWS EventBridge Scheduler**, **Cloud Scheduler**, and **Azure Logic Apps** all accept cron-style expressions and free you from running any compute when the job is idle.
Related concepts
Cron originated at AT&T Bell Labs as part of 7th-Edition Unix (1979), reportedly written by Brian Kernighan. The original was a daemon that scanned `/usr/lib/crontab` once a minute and exec'd any matching entries. **Paul Vixie's rewrite** in 1987 introduced the per-user crontab, the named day/month aliases, the `@daily` / `@reboot` nicknames, and crucially the **OR semantics for DOM + DOW** that diverged from the POSIX standard published the following year. Vixie cron became the de facto standard on Linux and BSD; modern derivatives include `cronie` (Red Hat / Fedora), `bcron` (Debian), and `dcron` (Alpine, Slackware). POSIX-strict implementations exist mainly in commercial Unix and embedded busybox-cron. The DOM/DOW divergence is the single most cited cron pitfall and predates POSIX itself by ~ 7 years.
The **format itself has dialects** beyond field count. `JAN-DEC` and `SUN-SAT` aliases are Vixie extensions and not POSIX. Quartz Scheduler adds the seconds field, the year field, the special characters `L` (last), `W` (weekday), `#` (nth weekday of month), and `?` (no specific value, for DOM/DOW). AWS EventBridge follows Quartz semantics but adds `?` requirements. Spring `@Scheduled` follows yet another variant. When you copy a cron expression between systems, verify field count, the DOM/DOW semantic, and whether named aliases are supported — a working expression in one system can silently misbehave or refuse to parse in another.
Three **modern alternatives** have eaten cron's lunch in different domains. **systemd timers** replace cron on systemd-managed Linux servers (most distributions since 2015) — they integrate with the unit system, log to journald, support randomization and persistence, and accept the more readable `OnCalendar=Mon..Fri 09:00` syntax. **Kubernetes CronJob** brings cron semantics to containerized workloads with pod isolation and retry policies. **Cloud-managed schedulers** (AWS EventBridge Scheduler, Google Cloud Scheduler, Azure Logic Apps Scheduler) accept cron expressions but run on managed infrastructure, eliminating the always-on host. For one-off task scheduling within an application, libraries like Java's Quartz, Python's APScheduler, Go's robfig/cron, and Node's node-cron embed cron semantics directly. Despite these alternatives, plain crontab remains alive on every Unix-like machine — its small surface area and `/etc/crontab` simplicity mean it will outlive most of its replacements.