Build Cron Lists and Ranges: Commas and Hyphens
Paste a comma or hyphen into a cron field in the parser above and it shows you exactly which values match, before you commit the expression to a live schedule. The comma (,) creates discrete values that fire independently, while the hyphen (-) creates a continuous range between two endpoints, inclusive.1 Both operators work in every field and combine with each other and with the step operator, giving you fine-grained control over when a job runs without writing multiple crontab lines.
Choosing between them comes down to whether your target values are contiguous. Ranges suit contiguous spans: weekdays (1-5), business hours (9-17), or the first half of the year (1-6). Lists suit non-contiguous values: specific hours (0,6,12,18), alternate months (1,4,7,10), or arbitrary days (1,15). Mixing them in the same field is valid too, and running 1,15-20,25 through the parser confirms it fires on the 1st, 15th through 20th, and 25th exactly as expected.
List operator (,): discrete values
A comma-separated list in any field fires at each listed value independently. 0,30 * * * * fires at minute 0 and minute 30 of every hour, twice per hour. 0 0 * * 1,3,5 fires at midnight on Monday, Wednesday, and Friday. Lists can contain as many values as needed, in any order. The list MON,WED,FRI is equivalent to 1,3,5 in the day-of-week field on systems that support named days.2 Choosing a list over a range is the right call whenever the target values are not contiguous, because a range cannot skip intermediate values.
Keeping lists readable
Use one value per semantic target when a list grows long. A list such as 1,4,7,10 immediately reads as quarterly months, while a longer comma chain is easier to audit if the values are ordered and grouped by purpose. When a list exceeds five or six values, consider whether a range or step could express the same set more compactly; the goal is to make the schedule scannable at a glance, not to enumerate every trigger explicitly.
Order inside a list is cosmetic, so write values ascending even though cron treats 0,30 and 30,0 identically, because a sorted list is far easier to scan during a review. The same scannability argument favors a named day list such as MON,WED,FRI over a bare numeric one when the platform supports it, since the intent reads directly from the field. Reserve a step only when the values truly are uniform, because a step that happens to match today can drift from your intent the moment the start point changes.
Range operator (-): continuous spans
A hyphen between two values creates an inclusive range. 1-5 in the day-of-week field covers Monday through Friday. 9-17 in the hour field covers 9 AM through 5 PM. Building on this, 0 9-17 * * 1-5 fires at minute 0 of every hour from 9 AM to 5 PM on weekdays. Ranges require the start value to be less than or equal to the end value: 5-1 produces a parse error on most implementations.1
The inclusive nature of ranges is easy to forget when you think of the endpoints as boundaries that exclude the next value. In practice, both endpoints are valid trigger points: 9-17 fires at both 9 AM and 5 PM, not just at the hours between them. When you need to exclude the upper boundary for readability or to avoid overlap with another range, use a list instead of a hyphen.
Combining list, range, and step operators
All three operators can appear in the same field. A field like 1,10-20,30 first creates a discrete value of 1, adds the continuous range 10 through 20, and appends the discrete value 30, giving the cron daemon a single unified set of 13 valid matches for that field position. This composability is what makes cron expressions compact: a single field can encode a complex trigger set that would otherwise require multiple separate job lines.
Auditing list and range combinations
0,30 9-17 * * 1-5 fires at minutes 0 and 30 of every hour between 9 AM and 5 PM on weekdays. 1,15-20,25 in the day-of-month field fires on the 1st, the 15th through 20th, and the 25th. You can also combine a range with a step: 1-5,10-15 fires on the values 1, 2, 3, 4, 5, 10, 11, 12, 13, 14, and 15, a concatenated multi-range list. When auditing these combinations, expand each operator into its literal value set and count the total matches to confirm the frequency aligns with your intent.
When to use a list instead of a range
When the values you need to target are non-contiguous, a list is clearer than approximating the pattern with ranges and exclusions. A job that should fire on Tuesday and Friday only needs 2,5 in the day-of-week field. For hours 0, 6, 12, and 18, the list 0,6,12,18 is immediately readable; the step */6 achieves the same result but requires the reader to know it starts at 0.
Non-uniform quarterly month distributions
For quarterly jobs targeting specific months, use a list to name each month explicitly: 1,4,7,10 targets January, April, July, and October. The step */3 starting from month 1 happens to produce the same set in this case, but for a distribution starting in February (2,5,8,11), the step */3 starting from 1 gives 1, 4, 7, 10, the wrong months.3 Write 2,5,8,11 explicitly to guarantee the correct targets.
List and range portability across platforms
In all standard cron implementations (Linux crontab, GitHub Actions, Kubernetes CronJob, Cloudflare Workers, AWS EventBridge), both the list and range operators work in all five fields. An expression using 1-5 or 1,3,5 in the day-of-week field is portable across these platforms without modification.4 This portability makes lists and ranges the safest choice when you need a schedule to behave identically across development, staging, and production environments.
One behavioral difference to know: most implementations require the start of a range to be less than or equal to the end.5 If you write 5-1 expecting a wrap-around (Saturday through Monday), most parsers produce an error rather than interpreting it as wrapping. To express a schedule that covers Saturday through Monday, use a list: 0,1,6 in the day-of-week field covers Sunday, Monday, and Saturday without relying on wrap-around behavior, and you can turn a cron day-of-week list into real dates to confirm a workaround actually covers the days you intended.
When to use this
Reach for the list operator in the parser above when a job needs to fire at multiple specific, non-contiguous times, and the range operator for contiguous spans like weekdays or business hours. Combine both and check the result whenever the schedule needs a subset of values a single range or step cannot express.
Examples
Run twice a day at 6 AM and 6 PM
A comma-separated list in the hour field fires at both values.
Run on weekdays AND the 1st and 15th
List in day-of-month combined with range in day-of-week. Note: cron uses OR, so this fires on all weekdays AND on the 1st and 15th regardless of weekday.
- 1.
The Open Group, "crontab — schedule periodic background work," opengroup.org, 2018. https://pubs.opengroup.org/onlinepubs/9699919799/utilities/crontab.html
- 2.
Linux man7, "crontab(5) — Linux manual page," man7.org, accessed June 2026. https://www.man7.org/linux/man-pages/man5/crontab.5.html
- 3.
Rob Pike, "cron package — github.com/robfig/cron/v3," pkg.go.dev, accessed June 2026. https://pkg.go.dev/github.com/robfig/cron/v3
- 4.
GitHub, "Events that trigger workflows," docs.github.com, accessed June 2026. https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows
- 5.
Rob Ford, "robfig/cron: Range parsing and validation," github.com, accessed June 2026. https://github.com/robfig/cron/blob/v3/parser.go