Cron expressions look intimidating mostly because the five fields carry no labels in the string itself, `0 9 * * 1-5` gives no visual hint about what each of the five values means. Once you memorize the fixed field order, though, reading any cron expression becomes a mechanical process rather than a guessing game.
The five fields, in order
A standard cron string always has five space-separated fields in this exact order: minute (0-59), hour (0-23), day-of-month (1-31), month (1-12), and day-of-week (0-6, where 0 is Sunday, though some systems also accept 7 for Sunday). An asterisk `*` in any field means "every value," so `* * * * *` (all asterisks) means "run every single minute."
Beyond a single number or asterisk, each field accepts several useful modifiers: a comma-separated list like `1,15` means "on the 1st and 15th," a range like `9-17` means "every hour from 9 through 17," and a step value like `*/15` in the minute field means "every 15 minutes" (0, 15, 30, 45). These can combine, `*/15 9-17 * * 1-5` means every 15 minutes, during the hours 9 through 17, on weekdays, a common pattern for a business-hours polling job.
Worked examples
`0 0 * * *` means minute 0, hour 0, every day, every month, every day-of-week, which is midnight every day. `0 9 * * 1-5` means minute 0, hour 9, every day-of-month, every month, weekdays only, 9 AM on weekdays. `0 0 1 * *` means minute 0, hour 0, day-of-month 1, every month, every day-of-week, midnight on the first of every month. `*/5 * * * *` means every 5 minutes, all day, every day, one of the most common patterns for frequent background jobs.
Reading unfamiliar cron strings by hand is error-prone precisely because a small misread, mistaking the day-of-month field for the day-of-week field, is easy to do and produces a job that silently runs on the wrong schedule. Pasting a string into the Cron Expression Explainer converts it to a plain-English sentence instantly, and the Cron Expression Generator works the other direction, building the correct syntax from a description so you don't have to hand-assemble the fields.
The day-of-month / day-of-week trap
One genuinely confusing cron behavior: if both the day-of-month and day-of-week fields are restricted (not `*`), most cron implementations treat them as an OR, the job runs if either condition matches, not only when both match simultaneously. A schedule like `0 0 15 * 1` doesn't mean "the 15th, if it's also a Monday," it means "midnight on the 15th of every month, OR every Monday," which surprises people who assume the fields combine with AND logic the way the other three fields do.

