Spring Cron Expression Generator & Explainer

Also works for Quartz and Unix crontab — and converts between all three.

  • Explains, validates & converts
  • Runs in your browser

This Spring cron expression generator builds, validates and explains cron expressions for Spring’s @Scheduled annotation — the six-field format with seconds first — as well as Quartz and classic Unix crontab. Paste an expression or pick a common schedule, and you get a plain-English explanation, the next ten run times in any time zone, the equivalent expression in the other two dialects, and copy-ready @Scheduled, application.yml, Quartz and crontab code.

Checked against the real libraries: the run times were compared with Spring Framework 7.0.9 and Quartz 2.3.2 on 1,672 test expressions — 1,670 match exactly. The other two come from quirks in the libraries themselves (a Spring L-n bug and Quartz’s month-end W handling), and the generator warns you when you use either.

The Spring cron format: six fields, seconds first

A Spring cron expression has exactly six space-separated fields. This is the single biggest source of confusion, because most online cron tools (and Linux crontab) use five fields with no seconds.

#FieldAllowed valuesSpecial characters
1Second0–59* , - /
2Minute0–59* , - /
3Hour0–23* , - /
4Day of month1–31* , - / ? L W
5Month1–12 or JAN–DEC* , - /
6Day of week0–7 or MON–SUN (0 and 7 = Sunday, 1 = Monday)* , - / ? L #

So 0 0 9 * * MON-FRI reads: second 0, minute 0, hour 9, any day of the month, any month, Monday to Friday — nine o’clock every weekday. Since Spring Framework 5.3 (Spring Boot 2.4), the parser behind @Scheduled is CronExpression, which added Quartz-style L, W and # plus macros such as @daily and @hourly. Everything on this page applies to Spring Boot 3 and Spring Boot 4.

Fixing “Cron expression must consist of 6 fields”

If your application fails to start with

java.lang.IllegalArgumentException: Cron expression must consist of 6 fields (found 5 in "*/5 * * * *")

you have pasted a five-field Unix expression — usually copied from crontab.guru or a Linux server — into @Scheduled. Add a seconds field at the front: */5 * * * * becomes 0 */5 * * * *. The generator above detects this automatically and offers the fixed expression in one click. The reverse mistake is just as common: * * * * * * in Spring means every second, not every minute. For every minute use 0 * * * * *.

Spring vs Quartz vs Unix cron: the differences that break jobs

The three dialects look alike, which is exactly why copying an expression between them silently changes the schedule.

  1. Seconds. Spring and Quartz start with a seconds field; crontab has none and cannot run more often than once a minute.
  2. Day-of-week numbers. In Spring, 1 is Monday and both 0 and 7 are Sunday. In Quartz, 1 is Sunday and 7 is Saturday. So 5L is the last Friday in Spring but the last Thursday in Quartz. Use names (MON-FRI, FRI#2) whenever you can.
  3. The question mark. Quartz requires ? in exactly one of the two day fields, so 0 0 12 * * * is rejected by Quartz with “Support for specifying both a day-of-week AND a day-of-month parameter is not implemented”. Spring accepts ? as a synonym for *.
  4. Both day fields set. In Spring, 0 0 0 13 * FRI only fires on a 13th that is also a Friday — both fields must match. In Unix cron, 0 0 13 * 5 fires on every 13th and every Friday. Quartz doesn’t allow it at all.
  5. L, W and #. Spring and Quartz support “last day” (L), “nearest weekday” (15W, LW) and “nth weekday” (MON#1). Standard crontab supports none of them.
  6. Year. Quartz has an optional seventh year field; Spring and crontab don’t.

The “Same schedule in Spring, Quartz and crontab” panel converts between the dialects and tells you when an exact conversion is impossible — for example, a Unix “either day” rule becomes two Spring expressions.

Common Spring cron expression examples

Click any expression to load it into the generator and see its next run times.

Spring @ScheduledMeaningQuartzcrontab
0 * * * * *Every minute0 * * * * ?* * * * *
*/30 * * * * *Every 30 seconds*/30 * * * * ?— (no seconds)
0 */5 * * * *Every 5 minutes0 */5 * * * ?*/5 * * * *
0 */15 * * * *Every 15 minutes0 */15 * * * ?*/15 * * * *
0 0 * * * *Every hour, on the hour0 0 * * * ?0 * * * *
0 0 */2 * * *Every 2 hours, on the hour0 0 */2 * * ?0 */2 * * *
0 0 0 * * *Every day at midnight0 0 0 * * ?0 0 * * *
0 0 9 * * *Every day at 09:000 0 9 * * ?0 9 * * *
0 0 9 * * MON-FRIAt 09:00, Monday through Friday0 0 9 ? * MON-FRI0 9 * * MON-FRI
0 */15 9-17 * * MON-FRIEvery 15 minutes, 09:00–17:59, Monday through Friday0 */15 9-17 ? * MON-FRI*/15 9-17 * * MON-FRI
0 0 8 * * MONAt 08:00, every Monday0 0 8 ? * MON0 8 * * MON
0 0 0 1 * *At midnight on the 1st of every month0 0 0 1 * ?0 0 1 * *
0 0 0 L * *At midnight on the last day of the month0 0 0 L * ?— (no L)
0 0 18 * * 5LAt 18:00 on the last Friday of the month0 0 18 ? * 6L— (no L)
0 0 10 * * TUE#2At 10:00 on the second Tuesday of the month0 0 10 ? * 3#2— (no #)
0 0 0 1 1 *At midnight on 1 January0 0 0 1 1 ?0 0 1 1 *

A note on steps: */15 in the minutes field means minutes 0, 15, 30 and 45. A step that doesn’t divide evenly behaves unexpectedly — */45 fires at minute 0 and minute 45, so the gaps are 45 then 15 minutes. For a true “every 45 minutes”, use @Scheduled(fixedRate = 45, timeUnit = TimeUnit.MINUTES) instead of cron.

Time zones and externalising the schedule

Without a zone attribute, @Scheduled uses the server’s default time zone — which is usually UTC in containers and something else on developer laptops. Pin it explicitly:

@Scheduled(cron = "0 0 9 * * MON-FRI", zone = "Asia/Kolkata")
public void sendDailyReport() { ... }

Better still, keep the expression in configuration so it can change without a rebuild: @Scheduled(cron = "${app.jobs.report.cron}") with the value in application.yml. Setting it to "-" disables the job. Remember that scheduling only runs when a configuration class is annotated with @EnableScheduling.

Daylight saving time gotchas

Jobs scheduled between 01:00 and 03:00 local time are the ones that misbehave. On the night clocks spring forward, a time such as 02:30 does not exist, so that day’s run is skipped. On the night clocks fall back, 01:30 happens twice. The next-run table flags both cases for the time zone you select. The safest fix for business-critical jobs is to run them in UTC or outside the 01:00–03:00 window.

Frequently asked questions

What is the cron format for Spring @Scheduled?

Six fields separated by spaces: second, minute, hour, day of month, month and day of week. For example, 0 30 8 * * * runs at 08:30:00 every day. Spring also accepts the macros @hourly, @daily, @midnight, @weekly, @monthly, @yearly and @annually.

How do I run a Spring scheduled job every 5 minutes?

Use @Scheduled(cron = "0 */5 * * * *"), which fires at minute 0, 5, 10 and so on of every hour. If you don’t need clock alignment, @Scheduled(fixedRate = 5, timeUnit = TimeUnit.MINUTES) is simpler.

Why does Spring say “Cron expression must consist of 6 fields”?

You used a five-field Unix cron expression. Spring needs a seconds field first, so add 0 to the front: 0 9 * * 1-5 becomes 0 0 9 * * 1-5.

What is the difference between Spring and Quartz cron expressions?

Both use six fields with seconds first, but Quartz counts day-of-week from Sunday = 1 (Spring uses Monday = 1), requires ? in one of the day fields, and allows an optional year field. An expression can be valid in both and still mean different days, so convert rather than copy.

Does Spring support L, W and # in cron expressions?

Yes, since Spring Framework 5.3. L means the last day of the month (or the last given weekday, e.g. 5L = last Friday), W the nearest weekday (15W, LW), and # the nth weekday of the month (MON#1 = first Monday).

How do I set the time zone for a Spring cron job?

Add the zone attribute, for example @Scheduled(cron = "0 0 9 * * *", zone = "Europe/London"). Without it Spring uses the JVM’s default time zone.

How accurate are the run times?

They were checked against the real Spring Framework 7.0.9 and Quartz 2.3.2 libraries on 1,672 test expressions, including daylight-saving changes in six time zones: 1,670 match exactly. The two exceptions are library quirks the tool warns about. It also models real daylight-saving behaviour — when clocks go back, Spring runs a repeated time twice and Quartz once.

Is my expression sent anywhere?

No. The generator is plain JavaScript running in your browser; nothing you type is uploaded or stored.


More Java developer tools: Browse all free tools — including the application.properties ↔ YAML converter (handy for moving a cron value into application.yml) and the Spring Boot Migration Analyzer.