RecurrenceTrigger schedules with an iCalendar RFC 5545 recurrence rule (RRULE). Use it for patterns that CronTrigger and SimpleTrigger cannot express: "every 2nd Monday of the month", "every other week on Monday, Wednesday and Friday", "the last weekday of March each year". It takes a standard RRULE string and computes fire times lazily, without materializing all occurrences.
RRULE Basics
An RRULE is semicolon-separated key-value pairs. FREQ, the base frequency, is required; the other properties refine it:
| Property | Description | Example |
|---|---|---|
FREQ | Base frequency (required) | YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, SECONDLY |
INTERVAL | How often the recurrence repeats (default: 1) | INTERVAL=2 (every other) |
COUNT | Maximum number of times the trigger will fire | COUNT=10 |
UNTIL | End date/time for the recurrence | UNTIL=20251231T235959Z |
BYDAY | Days of the week, optionally with ordinal | BYDAY=MO,WE,FR or BYDAY=2MO (2nd Monday) |
BYMONTHDAY | Days of the month (1-31 or -1 to -31) | BYMONTHDAY=15 or BYMONTHDAY=-1 (last day) |
BYMONTH | Months of the year (1-12) | BYMONTH=1,6,12 |
BYSETPOS | Position within the expanded set | BYSETPOS=-1 (last occurrence) |
BYHOUR | Hours (0-23) | BYHOUR=9,17 |
BYMINUTE | Minutes (0-59) | BYMINUTE=0,30 |
BYSECOND | Seconds (0-59) | BYSECOND=0 |
BYWEEKNO | Week numbers (1-53 or -53 to -1) | BYWEEKNO=1,26 |
BYYEARDAY | Day of year (1-366 or -366 to -1) | BYYEARDAY=1,100,200 |
WKST | Week start day (default: MO) | WKST=SU |
Tips
COUNT and UNTIL are mutually exclusive - you cannot use both in the same RRULE.
Warning
COUNT counts actual firings (TimesTriggered), not recurrence occurrences, which differs from strict RFC 5545 counting:
- a misfired occurrence that is skipped (e.g. by the
DoNothingmisfire policy) does not count; - an immediate fire caused by the misfire policy (e.g.
FireAndProceed) does count.
Examples
Every 2nd Monday of the month at 9:00 AM:
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("monthlyTrigger", "group1")
.WithRecurrenceSchedule("FREQ=MONTHLY;BYDAY=2MO")
.StartAt(DateBuilder.Create().InYear(2025).InMonthOnDay(1, 1).AtHourMinuteAndSecond(9, 0, 0).Build())
.Build();
Every other week on Monday, Wednesday, and Friday:
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("weeklyTrigger", "group1")
.WithRecurrenceSchedule("FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,WE,FR")
.StartNow()
.Build();
Last weekday of March each year:
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("yearlyTrigger", "group1")
.WithRecurrenceSchedule("FREQ=YEARLY;BYMONTH=3;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1")
.StartNow()
.Build();
Every day, but only on weekdays (skip weekends):
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("weekdayTrigger", "group1")
.WithRecurrenceSchedule("FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR")
.StartNow()
.Build();
Last day of every month:
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("lastDayTrigger", "group1")
.WithRecurrenceSchedule("FREQ=MONTHLY;BYMONTHDAY=-1")
.StartNow()
.Build();
Every 3 months on the 1st and 15th, limited to 10 occurrences:
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("quarterlyTrigger", "group1")
.WithRecurrenceSchedule("FREQ=MONTHLY;INTERVAL=3;BYMONTHDAY=1,15;COUNT=10")
.StartNow()
.Build();
Time Zone Support
Recurrence calculations use the system's local time zone by default. Set another with InTimeZone:
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger1", "group1")
.WithRecurrenceSchedule("FREQ=MONTHLY;BYDAY=2MO", b => b
.InTimeZone(TimeZones.FindById("Eastern Standard Time")))
.StartNow()
.Build();
DI / Hosted Service Configuration
Under AddQuartz(), use WithRecurrenceSchedule on the trigger:
services.AddQuartz(q =>
{
q.AddJob<MyJob>(j => j.WithIdentity("myJob"));
q.AddTrigger(t => t
.ForJob("myJob")
.WithIdentity("myTrigger")
.WithRecurrenceSchedule("FREQ=MONTHLY;BYDAY=2MO")
.StartNow());
});
RecurrenceTrigger Misfire Instructions
The RecurrenceTriggerMisfireInstruction enum has two trigger-specific instructions, with the same semantics as CronTrigger's, plus IgnoreMisfires, which every family has:
RecurrenceTriggerMisfireInstruction.FireAndProceedRecurrenceTriggerMisfireInstruction.DoNothingRecurrenceTriggerMisfireInstruction.IgnoreMisfires
SmartPolicy, the default, resolves to FireAndProceed.
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger1", "group1")
.WithRecurrenceSchedule("FREQ=WEEKLY;BYDAY=MO", b => b
.WithMisfireInstruction(RecurrenceTriggerMisfireInstruction.DoNothing))
.Build();
When to Use RecurrenceTrigger vs Other Triggers
| Scenario | Recommended Trigger |
|---|---|
| Fixed interval (every 10 seconds) | SimpleTrigger |
| Cron-expressible pattern (every weekday at 9am) | CronTrigger |
| Nth day-of-week in month (2nd Monday) | RecurrenceTrigger |
| Last weekday of a month | RecurrenceTrigger |
| Every other week on specific days | RecurrenceTrigger |
| Complex yearly patterns with BYSETPOS | RecurrenceTrigger |
| Calendar interval (every 5 months) | CalendarIntervalTrigger |
Persistence
| Stored in | Value |
|---|---|
| table | QRTZ_SIMPROP_TRIGGERS (existing; no schema change) |
| RRULE string | STR_PROP_1 column, max 512 characters |
| trigger type discriminator | RECUR |
