Every Hour During Business Hours Cron Expression
Runs hourly from 9 AM to 5 PM, Monday through Friday.
0 9-17 * * 1-5 in POSIX cron
Expression
0 9-17 * * 1-5Try it in the tester →Field Breakdown
How It Works
Hour range `9-17` combined with weekday filter — fires at 9, 10, 11, 12, 13, 14, 15, 16, and 17 hours on Mon–Fri (9 runs per business day, 45 per week).
Example Run Times
- Mon 09:00:00
- Mon 10:00:00
- Mon 11:00:00
- Mon 12:00:00
- Mon 13:00:00
Frequently Asked Questions
Does `9-17` include 17:00?
Yes — cron ranges are inclusive on both ends, so 9, 10, …, 17 all match.
How do I extend to 8 AM – 6 PM?
Change the hour range to `8-18`: `0 8-18 * * 1-5`.
0 9-17 * * 1-5 in Quartz / Spring
Hourly, but only while the office is awake: this fires at the top of each hour from 09:00 through 17:00, Monday to Friday. That's nine runs a day and 45 a week — under a third of what an unrestricted hourly trigger would produce.
Quartz / Spring expression
0 0 9-17 ? * 2-6Try it in the tester →At second 0, at minute 0, at hours 9, 10, 11, 12, 13, 14, 15, 16, 17 on Mon, Tue, Wed, Thu, Fri
Unix / POSIX equivalent: 0 9-17 * * 1-5
@Scheduled parser uses 0–7 with Monday = 1 — numeric day values mean different days in the two. The day names (MON, FRI, MON-FRI) are identical in both and safer to copy between them.Field Breakdown
Using It with Quartz / Spring
Restricting an hourly sweep to staffed hours fits work whose value depends on humans being present: refreshing a support-queue dashboard, syncing CRM records sales reps are actively editing, or polling a partner system that itself only updates during the business day. Spring consumes it as @Scheduled(cron = "0 0 9-17 ? * 2-6"), Quartz as a standard CronTrigger string. The off-hours silence is a feature twice over — it spares downstream systems roughly 123 nightly and weekend calls a week (75 weeknight, 48 weekend), and it makes any alert that does fire at 03:00 unambiguous evidence of a different, misconfigured job.
Quartz-Specific Notes
- →Ranges in Quartz are inclusive at both ends, so 9-17 yields nine distinct hours: 9, 10, 11, 12, 13, 14, 15, 16, 17. The last run of the day starts at 17:00, not 17:59.
- →Both constrained fields here are real constraints, so the mandatory ? takes the only remaining day slot — day-of-month.
- →If a single execution can exceed an hour during a busy afternoon, mark the job @DisallowConcurrentExecution; Quartz will otherwise launch the 14:00 run while the 13:00 one is still going.
Frequently Asked Questions
My office closes at 6 — how do I include a 18:00 run?
Widen the range by one: 0 0 9-18 ? * 2-6 gives ten runs ending at 18:00. The range bound is the last hour that fires, not the hour the schedule stops.
Can I make it half-hourly within the same window?
Put a minute list inside the same constraints: 0 0,30 9-17 ? * 2-6 fires at :00 and :30 of each business hour — 18 runs a day instead of 9.
Do the 9 and 17 follow the server clock or my office clock?
The trigger's timezone, which defaults to the JVM's. For an office schedule, set it explicitly — zone = "America/Denver" on @Scheduled or a TimeZone on the Quartz trigger — so a server migration can't quietly shift your business hours.
0 9-17 * * 1-5 in AWS EventBridge
Nine fires per working day — at the top of each hour from 09:00 through 17:00 UTC, Monday to Friday — totalling 45 a week and roughly 2,349 a year. This is the shape for polling that only earns its keep while humans are at their desks.
AWS EventBridge expression
cron(0 9-17 ? * 2-6 *)Try it in the tester →At minute 0, at hours 9, 10, 11, 12, 13, 14, 15, 16, 17 UTC on Mon, Tue, Wed, Thu, Fri
Unix / POSIX equivalent: 0 9-17 * * 1-5
Field Breakdown
Using It with AWS EventBridge
Wire it to the staffed-hours machinery: a Lambda that syncs the support-ticket queue into Slack so the on-duty engineer sees fresh counts each hour, an hourly CRM-to-warehouse delta pull feeding the sales dashboard people actually watch during the day, or an office-systems check that verifies badge readers and meeting-room displays are responding. The whole point versus a 24/7 hourly rule is the roughly 73 percent of invocations it never makes (123 of 168 weekly) — overnight and weekend runs that would poll an unchanging source for an audience that is asleep.
AWS-Specific Notes
- →Cron ranges are inclusive at both ends: 9-17 produces nine fires (09:00 through 17:00), not eight — and nothing covers 17:01 onward, so consumers expecting freshness until 18:00 need the range extended to 9-18.
- →Both window edges are UTC; for a New York office 9-17 UTC means 4 AM to noon local in winter, so the practical fix is either shifting to 13-21 or, better, an EventBridge Scheduler schedule carrying the office timezone.
- →Every one of the nine daily events is byte-identical except its time field — targets that care which hour fired must parse that timestamp.
Frequently Asked Questions
How do I tighten this to every 30 minutes within the same window?
Put a minute list in the first field: cron(0,30 9-17 ? * 2-6 *) doubles the cadence to 18 fires per business day while leaving the hour and weekday gates untouched.
Why am I getting nine runs a day when I expected eight?
9-17 includes both endpoints, so hours 9, 10, 11, 12, 13, 14, 15, 16, and 17 all match. If the final run should be 16:00, the range you want is 9-16.
0 9-17 * * 1-5 in GitHub Actions
An hourly heartbeat confined to the working day: the 9-17 hour range fires at 09:00 through 17:00 UTC inclusive — nine runs per weekday, 45 per week — and the 1-5 day filter silences weekends entirely. It is the standard shape for automation that only matters while people are at their desks.
GitHub Actions expression
0 9-17 * * 1-5Try it in the tester →At minute 0, at hours 9, 10, 11, 12, 13, 14, 15, 16, 17 UTC on Mon, Tue, Wed, Thu, Fri
Unix / POSIX equivalent: 0 9-17 * * 1-5
Field Breakdown
Using It with GitHub Actions
Written into workflow YAML as on: schedule: - cron: "0 9-17 * * 1-5" on the default branch, business-hours polling covers things like syncing a support ticket queue into GitHub issues while agents are working, refreshing an internal dashboard each hour of the day, or nudging stale review requests during the window when a nudge can actually be acted on. The UTC window aligns with European offices; a New York 9-to-5 would instead be 0 13-21 * * 1-5 under daylight saving or 0 14-22 * * 1-5 in winter. Consider a concurrency: group too — at hourly density, one slow run can still be going when the next begins.
GitHub-Specific Notes
- →Cron ranges include both endpoints, so 9-17 yields nine ticks, not eight — the 17:00 run fires; for a window ending before 5 PM, write 9-16.
- →All 45 weekly ticks land on minute zero, the slot GitHub's own documentation suggests avoiding because so many workflows pile onto it; 26 9-17 * * 1-5 keeps every hour covered from a much quieter minute.
- →The 9-17 UTC window barely grazes a US Pacific workday — 17:00 UTC is only 9 or 10 AM in California — so this exact line cannot serve both European and West Coast office hours.
Frequently Asked Questions
Why do I get nine runs a day when I expected eight?
Because 9-17 is inclusive at both ends: hours 9, 10, 11, 12, 13, 14, 15, 16, and 17 all match. Trim the range to 9-16 if 17:00 should not fire.
How do I cover 9 AM to 5 PM US Eastern instead of UTC?
Shift the range by the UTC offset: 0 13-21 * * 1-5 while EDT is in effect, 0 14-22 * * 1-5 during EST. There is no setting that applies the shift automatically, so the file needs a touch at each DST changeover for exact alignment.
Can two consecutive hourly runs overlap?
Yes — ticks are independent, and a run that drags past an hour (or starts late) can coexist with the next one. A concurrency group keyed on the workflow name, with or without cancel-in-progress, serializes them.