Cron Expression Parser
Parse and explain cron expressions with the day-of-month/day-of-week rule actually implemented correctly, UTC-aware next-run times, optional seconds precision, and a plain-English breakdown of every field. Runs entirely in your browser.
| Field | Value | Meaning | Valid |
|---|---|---|---|
| Minute | 0 | 0 | β |
| Hour | 9 | 9 | β |
| Day of Month | * | every | β |
| Month | * | every | β |
| Day of Week | 1-5 | Monday, Tuesday, Wednesday, Thursday, Friday | β |
Reading a Schedule Before Trusting It
- Never assume a restricted day-of-month AND day-of-week means "both must match." Standard cron treats that combination as "either matches" β the field breakdown flags this explicitly whenever it's active.
- Check UTC before deploying anything to a cloud scheduler. Kubernetes CronJobs, most CI schedules, and many managed cron services run in UTC regardless of server location β a schedule that looks right in local time can fire hours off once deployed.
- Use */N for evenly-spaced intervals, not a literal step from "now." */15 in the minute field always lands on :00, :15, :30, :45 β never on a 15-minute cadence starting from whenever the job happens to deploy.
- Trust the Next Runs list over mental math for anything with a step, range, or list. Combinations of commas, dashes and slashes are easy to misread by eye and cheap to verify directly.
Features
The One Rule That Breaks Most Simple Cron Parsers
Five fields, each with a well-defined range, looks like it should be simple to validate and explain β and four of the five fields are exactly that simple. Day-of-month and day-of-week are the exception, because they don't behave independently the way the other three fields do. When only one of them is restricted, it behaves exactly as expected: 0 0 1 * * runs only on the 1st, 0 9 * * 1-5 runs only on weekdays. The moment both are restricted at once β 0 0 1 * 1, say β the spec switches to "either matches," not "both must match," which has no equivalent in how any of the other fields behave and is genuinely counter-intuitive the first time it's encountered. A parser that doesn't implement this specific interaction correctly will silently produce wrong next-run times for exactly the expressions where getting it wrong matters most β anything trying to combine a monthly date with a weekday condition.
The practical failure mode is subtle enough to survive casual testing. A schedule meant to mean "the 1st of the month, but only if it's also a weekday" β a plausible-sounding intent β actually has no direct expression in standard cron at all, since the OR rule means 0 0 1 * 1-5 runs on every weekday AND the 1st, not the intersection of the two. Getting that specific intersection requires either checking the day-of-month inside the job itself once it starts, or building the schedule from a small set of explicit dates instead of relying on the day fields to encode an AND relationship they were never designed to express.
Local Time Is a Convenience; UTC Is the Actual Contract
A cron expression by itself carries no timezone information at all β 0 9 * * 1-5 says "9 AM," not "9 AM where." What that 9 actually means depends entirely on what timezone the system evaluating the expression is configured to use, and for a large share of modern infrastructure, that answer is UTC by default: Kubernetes CronJobs, GitHub Actions scheduled workflows, and most managed cloud cron services all evaluate schedules in UTC regardless of where the underlying servers physically run. Testing a schedule against local time and then deploying it somewhere that actually runs in UTC is a specific, common way for a job to silently fire at the wrong hour β not broken, just running exactly as scheduled against a timezone nobody accounted for. Toggling UTC here recomputes the entire next-run sequence against UTC calendar math, not just relabeling the same local times, so it reflects what would genuinely happen once deployed.
The gap between the two is easy to underestimate until it lands on a specific hour that matters. A job scheduled for 0 6 * * * intending "6 AM before the workday starts" behaves very differently depending on which timezone actually evaluates it β 6 AM UTC lands well before dawn across most of the Americas and well into the afternoon across parts of Asia, neither of which is likely what was intended by "before the workday." Confirming the deployment target's actual evaluation timezone before finalizing an hour-sensitive schedule, rather than assuming it matches whatever timezone the schedule was written in, is a five-second check that avoids a genuinely confusing class of "it ran, but at the wrong time" bug report.
Seconds Aren't Standard, But They're Increasingly Common
The original Vixie cron specification β the version most Unix systems and the POSIX crontab format still follow β has exactly five fields and no concept of seconds at all; the finest granularity it supports is once per minute. A number of modern scheduling libraries extend that with an optional leading seconds field, six fields total, specifically to support sub-minute triggers without needing a separate timer mechanism layered on top. Because this is an extension rather than something every cron-reading system recognizes, an expression written with seconds for one scheduler won't necessarily parse correctly if pasted into a strictly five-field-only system β worth confirming which convention a specific target actually expects before relying on the extra field.
From a Parsed Expression to a Deployed Job
A typical path: start from whichever preset is closest to the intended schedule, adjust the specific field that needs to differ, and read the plain-English description back to confirm it says what was actually intended β a quick sanity check that catches an inverted range or a misplaced comma before it reaches a real system. For anything combining a monthly date with a weekday restriction, checking the OR-quirk note specifically is worth the extra second, since that's the exact case most likely to produce a schedule that runs more often than expected. Once the next-run sequence looks right in the correct timezone, the expression is ready to paste directly into a crontab file, a CI configuration, or a scheduled job definition β pairing it with the Unix Timestamp Converter is a fast way to cross-check a specific run time against a timestamp appearing elsewhere in a log or API response.
It's also worth documenting the finished expression somewhere a future reader won't have to re-derive it from scratch β a short comment next to the crontab line, or a note in whatever config file defines the schedule, explaining the intent in plain language rather than leaving only the raw five-or-six-field string. Cron syntax is compact specifically because it wasn't designed to be self-explanatory, and a schedule that made perfect sense while being built rarely stays obvious six months later without that extra sentence of context.