Reading the five fields
Read an expression left to right: minute (0-59), hour (0-23), day of month (1-31), month (1-12) and day of week (0-7, where 0 and 7 both mean Sunday). A * matches every value in its field. A comma makes a list like 1,15, a hyphen makes a range like 9-17, and a slash makes a step like */5.
So */5 * * * * runs every fifth minute of every hour. That's 288 runs a day, at minutes 0, 5, 10, 15 and on. Pin the minute and hour and it narrows to once a day: 30 2 * * * runs at 02:30. Add ranges and 0 9-17 * * 1-5 runs on the hour from 9 AM to 5 PM, Monday to Friday.
Why a step can leave an uneven gap
A step counts inside its field and starts over when the field wraps. It doesn't carry on from the last run. In the minute field */7 matches minutes 0, 7, 14, 21 and on up to minute 56. Then the hour rolls over and the job fires at minute 0 again, only 4 minutes after the last run. So */7 isn't really every 7 minutes.
A step that divides the field evenly has no such gap. */5 and */15 keep a steady beat. */15 fires 4 times an hour. The hour field works the same way: */2 matches hours 0, 2, 4, 6 and on.
When both day fields are set
If the day of month and the day of week are both restricted (neither one is a *), standard cron runs the job when either one matches, not when both do. So 0 9 1-7 * 1 isn't the first Monday of the month. It's every day from the 1st to the 7th, plus every Monday. The usual fix is to restrict one day field and check the other in the script. The first Monday of the month guide shows how.
If one of the two fields is a *, only the other one counts, so 0 9 * * 1 is simply every Monday.
Time zones and other dialects
Classic cron reads the schedule in the time zone of the machine that runs it, unless the crontab sets CRON_TZ. A server on UTC that runs 0 0 * * * fires at 4 PM the day before for someone on US Pacific standard time (UTC-8). Daylight saving time adds two traps. A job set for an hour that gets skipped doesn't run, and a job set for an hour that happens twice can run twice. The cron time zone guide covers why jobs run at the wrong time.
Other schedulers bend the syntax. AWS EventBridge takes six fields, with a year at the end, and wants a ? in one of the two day fields. A Kubernetes CronJob keeps five fields and reads them in the controller manager's time zone unless you set .spec.timeZone. When a page or tool says cron, check which dialect it means before you paste an expression.