How-to GuidePublished 10 min read

How to Write EventBridge cron Expressions: What ? Means, Weekday Numbers, Japan Time and Converting from crontab

EventBridge cron expressions, used on AWS to run Lambda functions or ECS tasks on a schedule, look like Linux crontab but differ in the number of fields, the use of ?, the weekday numbers and the time zone. Write “0 9 * * 1-5” the crontab way and you get an error; copy the weekday numbers across and everything shifts by a day; and times are read in UTC, so you’re 9 hours off from Japan time. This article covers the six fields of an EventBridge cron expression and the ? rule, the weekday numbers, how to use L, W and #, how rate expressions differ, how Japan time works in rules versus Scheduler, and a conversion table for crontab, Kubernetes, Spring and systemd.

An EventBridge cron expression is “six fields” wrapped in cron( )

EventBridge schedules are written with either cron( ) or rate( ). Inside cron( ) go six values separated by spaces: crontab’s five fields plus a “year”. The year field can’t be omitted, so if any year will do, still put * at the end.

PositionFieldAllowed valuesAllowed symbols
1stMinute0–59, - * /
2ndHour0–23, - * /
3rdDay of month1–31, - * ? / L W
4thMonth1–12 or JAN–DEC, - * /
5thDay of week1–7 or SUN–SAT, - * ? L #
6thYear1970–2199, - * /

For example, “weekdays (Mon–Fri) at 0:00 (UTC)” is “cron(0 0 ? * MON-FRI *)”. Compared with crontab’s “0 0 * * 1-5”, the day of month has become ?, the weekday is an English abbreviation, and there’s an extra * for the year at the end. Those three are the places you always need to change when moving from crontab.

The ? rule: one of day of month or weekday must be ?

The most common source of errors in EventBridge is ?. It means “not specified in this field (the other field decides)”, and goes in exactly one of the day of month (3rd) or day of week (5th). You can’t make both *, and you can’t make both ?.

What you wantCorrectCommon mistake
Every day 9:00cron(0 9 * * ? *)cron(0 9 * * * *) (both *)
Weekdays 9:00cron(0 9 ? * MON-FRI *)cron(0 9 * * MON-FRI *) (day of month is *)
1st of every month 9:00cron(0 9 1 * ? *)cron(0 9 1 * * *) (weekday is *)
Every Monday 9:00cron(0 9 ? * MON *)cron(0 9 ? * ? *) (both ?)

The rule of thumb is simple: “if the weekday decides, make the day of month ?; if the date decides, make the weekday ?; for every day, make the day of month * and the weekday ?”. Because of this, neither crontab’s “either day of month or weekday (OR)” nor Spring’s “both (AND)” can be written in one rule. Something like “the 1st of every month and every Friday” has to be split into a date rule and a weekday rule.

Weekday numbers start with 1 for Sunday: one off from crontab

In crontab, 0 is Sunday and 1 is Monday, but in EventBridge 1 is Sunday, 2 is Monday…7 is Saturday. Copy the numbers over and every weekday moves one day earlier.

DaycrontabEventBridgeEnglish abbreviation
Sunday0 (7 also allowed)1SUN
Monday12MON
Tuesday23TUE
Wednesday34WED
Thursday45THU
Friday56FRI
Saturday67SAT
Weekdays (Mon–Fri)1-52-6MON-FRI

Put crontab’s “1-5” into EventBridge unchanged and you get Sunday to Thursday (SUN-THU): the Monday job runs on Sunday and nothing runs on Friday, a confusing bug. In EventBridge, writing English abbreviations such as MON-FRI means you never have to think about the off-by-one, so it’s the recommended style.

Month-end, weekdays and nth weekdays with L, W and #

EventBridge cron expressions can directly express things crontab can’t, such as “month-end” or “the nth weekday”. They come up often in daily and monthly batches, so it’s handy to know the common forms.

SyntaxMeaningExampleRun dates from October 2026
L in day of monthLast day of the monthcron(0 0 L * ? *)10/31, 11/30, 12/31, 1/31, 2/28
L-3 in day of month3 days before the last daycron(0 9 L-3 * ? *)10/28, 11/27, 12/28
15W in day of monthWeekday nearest the 15thcron(0 9 15W * ? *)10/15 (Thu), 11/16 (Mon), 12/15 (Tue)
LW in day of monthLast weekday of the monthcron(0 18 LW * ? *)10/30 (Fri), 11/30 (Mon), 12/31 (Thu)
3#2 in day of weekSecond Tuesday (3 is Tuesday)cron(0 10 ? * 3#2 *)10/13, 11/10, 12/8
6L in day of weekLast Friday (6 is Friday)cron(0 18 ? * 6L *)10/30, 11/27, 12/25

# and L combine with weekday numbers, so the “1 is Sunday” offset matters here too. Write the second Tuesday as “2#2” with crontab habits and you get the second Monday. W runs on “the nearest weekday within the same month if the given day falls on a weekend”, and never crosses into another month. November 15, 2026 is a Sunday, so 15W runs on Monday the 16th.

How rate expressions differ: “every” counts from when you start

For a simple interval you can use rate( ) instead of cron. Write a number and a unit (minute, hour or day), as in rate(5 minutes), rate(1 hour) or rate(7 days).

  • Use the singular for 1 and the plural for 2 or more. rate(1 hours) and rate(5 minute) are errors
  • The interval counts from when the rule was created (or enabled), so you can’t align runs to times like “minute 0 of every hour”
  • To run at exactly minutes 0, 15, 30 and 45 of every hour, use cron(0/15 * * * ? *), not rate(15 minutes)
  • There are no week or month units, so weekly and monthly jobs are written with cron

In EventBridge cron expressions, it’s common to write the start of a step as a number, like “0/15” (it means the same as “*/15”). A good split is cron for work that’s easier to handle later if times line up, such as log aggregation, and rate for work where only the interval matters, such as health checks.

Running on Japan time: rules use UTC, Scheduler lets you choose a time zone

EventBridge offers two ways to create schedules: the long-standing “EventBridge rules” and “EventBridge Scheduler”, added in 2022. The cron syntax is the same, but they handle time zones differently.

EventBridge rulesEventBridge Scheduler
Time zone of the cron expressionFixed to UTCSet with ScheduleExpressionTimezone (e.g. Asia/Tokyo)
Weekdays 9:00 Japan timecron(0 0 ? * MON-FRI *)cron(0 9 ? * MON-FRI *) + time zone Asia/Tokyo
One-time runNot possibleat(2026-12-01T09:00:00)
Regions with daylight saving timeYou have to adjust the time yourselfAdjusted automatically for the time zone

To match Japan time with a rule, write the UTC time 9 hours earlier, just as with GitHub Actions. Japan time 0:00–8:59 falls on the previous day in UTC, so move the weekday back one as well. For example, weekdays at 8:00 Japan time is 23:00 UTC Sunday–Thursday, so “cron(0 23 ? * SUN-THU *)”. The detailed steps are explained in running GitHub Actions cron on Japan time. If you’re creating something new, Scheduler, which lets you set the time zone, is safer because there’s no offset arithmetic to get wrong.

Conversion table for crontab, Kubernetes, Spring and systemd

Here is the same schedule written for each of the common systems. Times are in each system’s configured time zone (EventBridge rules and GitHub Actions use UTC).

Schedulecrontab / KubernetesEventBridgeSpring @Scheduledsystemd OnCalendar
Every day 3:3030 3 * * *cron(30 3 * * ? *)0 30 3 * * **-*-* 03:30:00
Weekdays 9:000 9 * * 1-5cron(0 9 ? * MON-FRI *)0 0 9 * * MON-FRIMon..Fri *-*-* 09:00:00
Every 15 minutes*/15 * * * *cron(0/15 * * * ? *)0 */15 * * * **-*-* *:00/15:00
1st of every month 0:000 0 1 * *cron(0 0 1 * ? *)0 0 0 1 * **-*-01 00:00:00
Last day of every month 23:00Not possible (28-31 + check)cron(0 23 L * ? *)0 0 23 L * **-*~01 23:00:00
Second Tuesday 10:00Not possible (8-14 + check)cron(0 10 ? * 3#2 *)0 0 10 ? * TUE#2Tue *-*-08..14 10:00:00
  • Kubernetes CronJob uses the same five fields as crontab. From 1.27 you can set Japan time with spec.timeZone: Asia/Tokyo; writing CRON_TZ= inside schedule isn’t supported
  • Spring has six fields, with seconds first. You can set a time zone with zone, as in @Scheduled(cron = "0 0 9 * * MON-FRI", zone = "Asia/Tokyo")
  • systemd timer’s OnCalendar takes the form “weekday year-month-day hour:minute:second”. ~01 means “the 1st day counting from the end of the month”, that is, the last day

In the cron expression checker, choose AWS EventBridge as the syntax and paste your expression: it flags a missing ? or shifted weekday numbers and shows the meaning in plain language with the upcoming run times. Enter a crontab expression and you can see it converted to EventBridge, Kubernetes, Spring and systemd syntax all at once. For ports that involve the day-of-month/weekday OR difference, also see cron’s day-of-month/weekday OR rule and how to write nth weekdays and month-end.

FAQ

What does ? mean in an EventBridge cron expression?
It means “not specified in this field”, and must go in exactly one of the day of month or day of week. If the weekday decides, make the day of month ?; if the date decides, make the weekday ?. For every day, make the day of month * and the weekday ?, as in cron(0 9 * * ? *). Both * or both ? is an error.
How do I run EventBridge at 9:00 Japan time?
EventBridge rules are read in UTC, so subtract 9 hours and write cron(0 0 * * ? *). With EventBridge Scheduler, write cron(0 9 * * ? *) and set the time zone (ScheduleExpressionTimezone) to Asia/Tokyo, and it runs at 9:00 Japan time.
Which number is Monday in EventBridge weekdays?
1 is Sunday, 2 is Monday…7 is Saturday. That’s one off from crontab (0 is Sunday, 1 is Monday), so weekdays are 2-6. To avoid mixing up the numbers, it’s best to use English abbreviations such as MON-FRI.
Should I use cron or rate to run EventBridge every 5 minutes?
If only the interval matters, rate(5 minutes) is simpler. But rate counts from when the rule was created, so runs won’t line up with minutes 0, 5, 10 of each hour. If you want aligned times, use cron(0/5 * * * ? *).
How do I convert crontab’s “0 9 * * 1-5” to EventBridge?
Make the day of month ?, the weekday MON-FRI (2-6 as numbers), and add a * for the year at the end: cron(0 9 ? * MON-FRI *). And because EventBridge rules are read in UTC, if you want 9:00 Japan time, set the hour to 0: cron(0 0 ? * MON-FRI *).

Tools in this article

Related articles

How-to Guide10 min read

Running GitHub Actions cron on Japan Time: UTC Conversion Table and Fixing Day Shifts and Delays

You wrote “0 9 * * 1-5” in your GitHub Actions schedule, but the workflow ran at 6 p.m. Japan time. That isn’t a typo: GitHub Actions always reads cron expressions in UTC (Coordinated Universal Time). This article covers how to convert a Japan Standard Time (JST) schedule into a UTC cron expression, with a ready-made table; why times between midnight and 8:59 a.m. shift the weekday and date back by one; how to split a schedule into two lines when one won’t do; and what to do when a schedule runs late or not at all.

Read article →
How-to Guide10 min read

How to Write crontab, with a Cheat Sheet: The Five Fields, Symbols, Common Examples and Commands

Recurring jobs like “back up every day at 3:00” or “run a monitoring script every 5 minutes” take a single line in crontab on Linux. But the five fields (minute, hour, day of month, month, day of week) and the symbols * , - / are easy to misread at first, and accidents are common: a job that runs 60 times an hour, or one meant to run every 7 minutes that ends up with a 4-minute gap. This article walks through the basics of crontab syntax and what each symbol means, a cheat sheet you can copy from, how to use crontab -e, -l and -r, and what to watch out for when writing the command part, mostly through examples.

Read article →
How It Works10 min read

cron’s Day-of-Month and Day-of-Week Are ORed: Writing Second Mondays, Month-End, Last Fridays and Every Other Week Correctly

You wrote “0 9 1-7 * 1” for “9:00 on the first Monday of every month”, and it ran every Monday plus every day from the 1st to the 7th. This very common cron mistake comes from a rule unique to cron: if you specify both the day of month and the day of week, the job runs on days that match either one. This article explains how the OR works and its exception, how to make crontab handle nth weekdays, month-end, last Fridays and every other week correctly, and how to use L, # and W in AWS EventBridge and Spring, with real run dates as examples.

Read article →