Cronwise

Quartz Special Characters: L, W, and # Explained

Deep token-level guidance for Quartz's L, W, and # operators so you can build precise schedules without runtime surprises.

Open Quartz Generator

Why Quartz Special Characters Matter

Most cron mistakes start before deployment, when schedule intent and syntax diverge. Standard five-field cron covers minutes, hours, day-of-month, month, and day-of-week. Quartz extends this with a seconds field, a year field, and three special characters—L, W, and #—that unlock scheduling precision standard cron cannot match.

These characters behave differently depending on which field they appear in, and small misunderstandings cascade into schedules that fire on the wrong day or skip runs entirely. This article explains each character with practical examples, validation checks, and clear next actions you can take in Cronwise's Quartz Generator.

The L Character: Last Day and Last Weekday

L stands for "last" and is valid in two fields: day-of-month and day-of-week. Its meaning changes depending on where you place it.

L in Day-of-Month

When used alone, L means the last day of the month. The expression 0 0 18 L * ? fires at 6:00 PM on the last day of every month—whether that is the 28th, 29th, 30th, or 31st. You never need to hardcode month lengths.

You can combine L with a negative offset. L-3 means three days before the last day. In a 31-day month that resolves to the 28th; in February of a non-leap year it resolves to the 25th. This is useful for billing cut-off windows.

L in Day-of-Week

In the day-of-week field, you pair L with a weekday number: 6L means the last Friday of the month. The expression 0 0 9 ? * 6L fires at 9:00 AM on the last Friday of every month—useful for payroll processing or month-end reports that must land on a specific weekday.

The W Character: Nearest Weekday

W is valid only in the day-of-month field. It selects the nearest weekday (Monday through Friday) to the specified date. Write 15W to mean "the weekday closest to the 15th."

How W Resolves

If the 15th falls on a Saturday, the schedule shifts to Friday the 14th. If it falls on a Sunday, it shifts to Monday the 16th. The resolution never crosses month boundaries: if the 1st is a Saturday, 1W resolves to Monday the 3rd, not to the previous Friday.

Combining L and W

Quartz allows the combination LW, meaning "the last weekday of the month." This is ideal for end-of-month financial closes that must run on a business day. If the last day is Saturday, LW resolves to Friday. Note that W cannot be combined with a list or range—expressions like 1W,15W are invalid.

The # Character: Nth Weekday of the Month

# is valid only in the day-of-week field and specifies the Nth occurrence of a weekday within a month. The syntax is day#N, where day is the weekday number (1–7, Sunday through Saturday) and N is the occurrence (1–5).

Practical Examples

0 0 10 ? * 2#1 fires at 10:00 AM on the first Monday of every month. 0 0 14 ? * 6#3 fires at 2:00 PM on the third Friday. These patterns replace fragile workarounds that rely on day-of-month ranges and conditional logic.

When N Exceeds Actual Occurrences

If you specify 2#5 (the fifth Monday), the schedule does not fire in months with only four Mondays. Quartz treats this as a no-match, not an error—a silent skip that makes run-count monitoring essential.

The # character cannot appear in day-of-month and cannot be combined with lists, ranges, or increments. Each day-of-week field accepts at most one # specification.

Edge Behavior and Failure Modes

Subtle mistakes with these characters rarely trigger validation errors but silently deviate from intent.

Common Pitfalls

ExpressionExpectedActualFix
0 0 9 L * 6Last day if FridayConflict: both day fields setUse ? in one day field
0 0 9 1W * ? (1st is Saturday)Previous FridayMonday the 3rd (no cross-month)Verify with next-run preview
0 0 9 ? * 2#5Fifth Monday monthlyOnly months with five MondaysAdd run-count monitoring
0 0 9 LW * ? in FebruaryLast weekday26th, 27th, or 28th by yearCheck timezone-aware preview

The most dangerous error is specifying values in both day-of-month and day-of-week without using ? as a placeholder. Quartz requires exactly one day field to contain ?. Omitting it produces a parse error or undefined behavior depending on the Quartz version.

Decision Framework: Choosing the Right Character

Each special character solves a different scheduling problem. Selecting the right one depends on whether your schedule is anchored to a calendar date, a weekday, or a relative position within the month.

RequirementCharacterFieldExample
Run on the last day of every monthLDay-of-month0 0 18 L * ?
Run N days before month endL-NDay-of-month0 0 18 L-3 * ?
Run on the last specific weekdaynLDay-of-week0 0 9 ? * 6L
Run on the nearest weekday to a dateWDay-of-month0 0 9 15W * ?
Run on the last business day of monthLWDay-of-month0 0 17 LW * ?
Run on the Nth weekday of the month#Day-of-week0 0 10 ? * 2#1

When your requirement is purely date-based ("the 15th"), you do not need special characters at all. Use W only when business-day alignment is required. Use # only when a recurring weekday occurrence matters more than a fixed date. And use L when month-end semantics must adapt to variable month lengths automatically.

Production Hardening: Pre-Deploy Checks

Before any Quartz schedule reaches production, run through these verification steps to catch edge cases that static validation cannot surface.

Verification Checklist

CheckWhy It MattersPass Criteria
Parse without errorsConfirms syntactic validityNo parser exceptions
Inspect next 10 runsSurfaces month-boundary surprisesAll dates match intent
Verify timezone alignmentServer vs. business TZ mismatchesRuns match target timezone
Confirm ? placementBoth day fields active is undefinedOne day field has ?
Check for silent skips#5 may fire < 12 times/yearAnnual count documented

Cronwise's next-run preview returns the next 10 executions in your selected timezone, letting you verify these criteria without deploying a test job. If your team operates across time zones, review Cron Timezones Explained for Global Teams for additional guidance.

Putting It All Together

Quartz's L, W, and # characters give you scheduling power that standard cron cannot match. L adapts to variable month lengths, W aligns execution to business days, and # pins runs to specific weekday occurrences. Used correctly, they eliminate brittle workarounds and make schedule intent explicit in the expression itself.

The key decision rules are straightforward. Use L for month-end logic. Use W when you need the nearest weekday. Use # for the Nth weekday of a month. Always place ? in the day field you are not using. And always verify with a next-run preview before deploying.

Ready to build or validate your Quartz schedule? Open the Cronwise Quartz Generator to construct expressions visually, see plain-language explanations, and preview execution times in your target timezone. For more cron scheduling guides and deep dives, browse all cron articles on Cronwise.