Skip to content
Y
YAS.SH
Developers

Cron Expressions Demystified: A Field-by-Field Guide

Every cron field explained with examples, the five rules that trip everyone up (day-of-month vs day-of-week, timezones, DST), and a parser that computes the next runs.

yas-team6 min readcronschedulingdevops
Cron Expressions Demystified: A Field-by-Field Guide
Featured imageCron Expressions Demystified: A Field-by-Field Guide

Cron is the oldest automation tool still in daily use — and the one most people configure by copy-paste. The syntax is small enough to learn in ten minutes and weird enough to cause a 3 AM incident when you get it wrong.

The Cron Parser computes the next run times for any expression, which makes every rule in this article testable in seconds instead of learnable through outages.

The five fields

* * * * *
│ │ │ │ │
│ │ │ │ └── day of week (0–7, 0 and 7 = Sunday)
│ │ │ └──── month (1–12, or jan–dec)
│ │ └────── day of month (1–31)
│ └──────── hour (0–23)
└────────── minute (0–59)

The order is the thing everyone gets wrong at least once: minute comes first. 30 2 * * * is 02:30, not 30:02.

The building blocks

Syntax Meaning Example
* Every value * * * * * — every minute
5 Exact value 0 9 * * * — 09:00 daily
a-b Range 9-17 — 9 through 17
*/n Step */15 * * * * — every 15 minutes
a-b/n Range with step 9-17/2 — 9, 11, 13, 15, 17
a,b,c List 0,30 * * * * — at :00 and :30
jan–dec, sun–sat Names 0 9 * jan mon * — January Mondays (some implementations)

Rule 1: the classic gotcha — two day fields

Both day of month and day of week have * defaults, and when both are restricted, either matching counts. So:

0 0 1 * 1

runs every Monday AND on the 1st of every month — not "the first Monday". This is the single most common cron surprise. If you want "the first Monday", you need the # extension (0 0 * * 1#1 in extended syntaxes) or a wrapper script that checks the date.

Rule 2: timezone is the server's, always

Cron has no timezone field in the classic 5-field form. A server running UTC runs your "8 AM" job at 8 AM UTC — which is 10 AM in Cairo in summer and 11 AM in winter. Fix: run servers in UTC and schedule in UTC, or use CRON_TZ where supported.

Rule 3: DST is a lie detector

When clocks jump forward, an hour simply doesn't exist; when they jump back, it happens twice. Most cron implementations handle this by running at the (possibly shifted) wall-clock time — but "once daily at 02:30" can become zero or two runs. For anything that must be exactly-once, prefer UTC (no DST).

Rule 4: no backfill

If the server was down at 02:30, the 02:30 job doesn't run at 03:00. Cron is fire-and-forget. Mission-critical jobs need catch-up logic or an external scheduler.

Rule 5: minute granularity is the floor

Cron's smallest unit is one minute. Sub-minute scheduling (every 30 seconds) is not cron's job — that's a systemd timer with OnUnitActiveSec=30s or a proper queue.

Test before you trust

The parser is one POST away:

curl -X POST https://yas.sh/api/v1/tools/cron-parser -d '{"input":"*/15 * * * *","count":5}'
{ "expression": "*/15 * * * *", "valid": true,
  "next": ["2026-08-08T12:15:00.000Z", "2026-08-08T12:30:00.000Z", "..."] }

Run your expression, read the next five times, and ask: is this what I meant? If it is, schedule it. If not, the parser just saved you a 3 AM pager call — which is exactly what a ten-minute investment in cron syntax is worth.

Frequently asked questions

Why does my cron job run at the wrong time?

Cron runs in the server's local timezone. If the server is UTC and you think in local time, everything shifts. Use CRON_TZ if your system supports it, or compute the UTC offset yourself.

What happens if a job is missed during a restart?

Nothing — cron doesn't backfill. Missed runs are simply lost. For jobs that must run exactly once, use a scheduler with catch-up or an idempotent worker.

Is 0 0 1 * * the same as monthly?

Almost — it's midnight on the 1st of every month. But a monthly task on a 31-day month never runs on the 30th/31st if you write it as the last day. cron has no "last day" field (that's what L is for in some implementations).

Was this helpful? Share
🍪 Cookies & privacy. yas.sh uses only essential cookies to keep you signed in and remember your preferences. We do not run third-party trackers. See our cookie policy and privacy policy.
Settings