Quartz.NETQuartz.NET
Home
Features
Blog
Discussions
NuGet
GitHub
Home
Features
Blog
Discussions
NuGet
GitHub
  • Getting Started

    • Overview
    • Quartz 4 Quick Start
    • Tutorial
      • Using Quartz
      • Jobs And Triggers
      • More About Jobs & JobDetails
      • Job Data
      • More About Triggers
      • Querying Jobs and Triggers
      • Simple Triggers
      • Cron Triggers
      • RecurrenceTrigger
      • Time and TimeProvider
      • Trigger and Job Listeners
      • Scheduler Listeners
      • Job Execution Middleware
      • Job Stores
      • Configuration, Resource Usage and Building a Scheduler
      • Building a Scheduler Without a Host
      • Clustering
      • Execution Groups
      • Node Affinity (Preferred Node)
      • Testing
      • Compile-Time Checks
      • Declaring Jobs with Attributes
      • Delegate Jobs
    • Configuration Reference
    • JSON Configuration
    • Cron Expression Reference
    • Multi-Tenancy
    • Comparison
    • Frequently Asked Questions
    • Best Practices
    • Before You Go Live
    • Operating a Cluster
    • Log Events
    • Tenancy Patterns
    • Database Schema
    • Database Schema Changes
    • Migration Guide
    • Troubleshooting
    • API Documentation
  • How To's
    • One-Off Job
    • Rescheduling Jobs
    • Backfill
    • Retrying Failed Jobs
    • Pausing with a Reason
    • Job Continuations
    • Overlap Policy
    • Progress and Execution Logs
    • Job Outcomes
    • Multiple Triggers
    • Job Template
    • Running Quartz under Aspire
    • Quartz.NET with Wolverine
    • Coming from Hangfire
    • Coming from TickerQ
    • Embedding Quartz in a Library
    • Running under an External Leader Election
    • Publishing Trimmed and Native AOT
    • Extending Quartz: what is open, what is closed, and how to ask
    • A Job Store of Your Own
    • A Driver Delegate for a New Database
    • Persisting a Custom Trigger Type
    • A Lock Handler of Your Own
  • Packages

    • Quartz Core Additions

      • Jobs
      • Serialization (System.Text.Json)
      • JSON Serialization
      • Plugins
    • Integrations

      • Aspire Integration
      • ASP.NET Core Integration
      • HTTP API
      • HTTP Client
      • Dashboard
      • Dashboard Agent
      • Hosted Services Integration
      • Microsoft DI Integration
      • Multiple Schedulers with Microsoft DI
      • Observability
      • Redis Lock Handler
      • TimeZoneConverter Integration
      • Weasel Schema Management
    • 3rd Party Plugins for Quartz
  • Quartz 3.x

    • Getting Started

      • Quartz 3 Quick Start
      • Tutorial
        • Using Quartz
        • Library Overview
        • Jobs And Triggers
        • More About Jobs
        • More About Triggers
        • Execution Groups
        • Node Affinity (Preferred Node)
        • Simple Triggers
        • Cron Triggers
        • RecurrenceTrigger
        • Trigger and Job Listeners
        • Scheduler Listeners
        • Job Stores
        • Tuning the Scheduler
        • Configuration, Resource Usage and SchedulerFactory
        • Advanced (Enterprise) Features
      • Configuration Reference
      • JSON Configuration
      • Multi-Tenancy
      • Frequently Asked Questions
      • Best Practices
      • Tenancy Patterns
      • Troubleshooting
      • API Documentation
      • Database Schema
      • Database Schema Changes
      • Migration Guide
      • Miscellaneous Features
    • How To's

      • One-Off Job
      • Multiple Triggers
      • Job Template
      • Using the CronTrigger
      • Rescheduling Jobs
    • Packages

      • Quartz Core Additions

        • Dashboard
        • Jobs
        • Serialization (System.Text.Json)
        • Serialization (Newtonsoft Json.NET)
        • Plugins
      • Integrations

        • ASP.NET Core Integration
        • Hosted Services Integration
        • Microsoft DI Integration
        • Multiple Schedulers with Microsoft DI
        • OpenTelemetry Integration
        • OpenTracing Integration
        • Redis Lock Handler
        • TimeZoneConverter Integration
      • 3rd Party Plugins for Quartz
  • Old Releases

    • Quartz 2.x
      • Quartz 2 Quick Start
      • Tutorial
        • Lesson 1: Using Quartz
        • Lesson 2: Jobs And Triggers
        • Lesson 3: More About Jobs & JobDetails
        • Lesson 4: More About Triggers
        • Lesson 5: SimpleTrigger
        • Lesson 6: CronTrigger
        • Lesson 7: TriggerListeners and JobListeners
        • Lesson 8: SchedulerListeners
        • Lesson 9: JobStores
        • Lesson 10: Configuration, Resource Usage and SchedulerFactory
        • Lesson 11: Advanced (Enterprise) Features
        • Lesson 12: Miscellaneous Features of Quartz
        • CronTrigger Tutorial
      • Configuration Reference
      • Migration Guide
      • API Documentation
    • Quartz 1.x
      • Tutorial
        • Lesson 1: Using Quartz
        • Lesson 2: Jobs And Triggers
        • Lesson 3: More About Jobs & JobDetails
        • Lesson 4: More About Triggers
        • Lesson 5: SimpleTrigger
        • Lesson 6: CronTrigger
        • Lesson 7: TriggerListeners and JobListeners
        • Lesson 8: SchedulerListeners
        • Lesson 9: JobStores
        • Lesson 10: Configuration, Resource Usage and SchedulerFactory
        • Lesson 11: Advanced (Enterprise) Features
        • Lesson 12: Miscellaneous Features of Quartz
      • API Documentation
  • License

Simple Triggers

Use a SimpleTrigger to run a job once at a specific moment, or at a moment and then repeatedly at an interval: for example, at exactly 11:23:54 AM on January 13, 2005, and then five more times, every ten seconds.

PropertyValues
start timea DateTimeOffset
end time (EndTimeUtc)a DateTimeOffset; overrides the repeat count
repeat countzero, a positive integer, or SimpleTriggerImpl.RepeatIndefinitely (-1), which RepeatForever() sets
repeat intervala positive TimeSpan; TimeSpan.Zero only with a repeat count of zero
  • A trigger that repeats needs a positive interval: scheduling one with a zero interval throws SchedulerException ("Repeat Interval cannot be zero."). To run several firings at once, schedule several one-shot triggers.
  • Start and end times carry an offset, so they are unambiguous. Compute one from DateTimeOffset.UtcNow; in code that has a TimeProvider (a job, a test), read the clock from it, as DateBuilder.Create(timeProvider) does.
  • To fire every 10 seconds until a given moment, set the end time and a repeat count of RepeatIndefinitely instead of computing the number of repeats. A repeat count larger than the number of firings before the end time also works.

Build a SimpleTrigger with TriggerBuilder (the main properties) and its WithSimpleSchedule extension method (the SimpleTrigger-specific properties).

A specific moment in time, with no repeats:

// trigger builder creates simple trigger by default
ITrigger trigger = TriggerBuilder.Create()
    .WithIdentity("trigger1", "group1")
    .StartAt(myStartTime) // some Date
    .ForJob("job1", "group1") // identify job with name, group strings
    .Build();

The trigger family interfaces (ISimpleTrigger and friends) are read models: cast to one to inspect a trigger's schedule, never to change it. To change a schedule, rebuild the trigger with trigger.GetTriggerBuilder() and pass it to IScheduler.RescheduleJob.

A specific moment in time, then every ten seconds ten times:

ITrigger trigger = TriggerBuilder.Create()
    .WithIdentity("trigger3", "group1")
    .StartAt(myTimeToStartFiring) // if a start time is not given (if this line were omitted), "now" is implied
    .WithSimpleSchedule(x => x
        .WithInterval(TimeSpan.FromSeconds(10))
        .WithRepeatCount(10)) // note that 10 repeats will give a total of 11 firings
    .ForJob(myJob) // identify job with handle to its JobDetail itself
    .Build();

Once, five minutes in the future:

ITrigger trigger = TriggerBuilder.Create()
    .WithIdentity("trigger5", "group1")
    .StartAt(DateTimeOffset.UtcNow.AddMinutes(5))
    .ForJob(myJobKey) // identify job with its JobKey
    .Build();

Now, then every five minutes, until 22:00:

ITrigger trigger = TriggerBuilder.Create()
    .WithIdentity("trigger7", "group1")
    .WithSimpleSchedule(x => x
        .WithInterval(TimeSpan.FromMinutes(5))
        .RepeatForever())
    .EndAt(DateBuilder.Create().AtHourMinuteAndSecond(22, 0, 0).Build())
    .Build();

At the top of the next hour, then every 2 hours, forever:

ITrigger trigger = TriggerBuilder.Create()
    .WithIdentity("trigger8") // because group is not specified, "trigger8" will be in the default group
    .StartAt(DateBuilder.Create().AtMinute(0).AtSecond(0).Build().AddHours(1)) // the next even hour
    .WithSimpleSchedule(x => x
        .WithInterval(TimeSpan.FromHours(2))
        .RepeatForever())
    // note that in this example, 'ForJob(..)' is not called
    //  - which is valid if the trigger is passed to the scheduler along with the job
    .Build();

await scheduler.ScheduleJob(job, trigger);

TriggerBuilder and WithSimpleSchedule have more options than these examples show.

Measured from completion

MeasureIntervalFromCompletion() makes the interval a fixed delay: the next run starts one interval after the previous run completes, however long it took. The default is a fixed rate, measured from each run's scheduled time. From 4.7.

ITrigger trigger = TriggerBuilder.Create()
    .WithIdentity("poll-inbox", "group1")
    .WithSimpleSchedule(x => x
        .WithInterval(TimeSpan.FromSeconds(30))
        .RepeatForever()
        // Thirty seconds after each run ends, however long it took.
        .MeasureIntervalFromCompletion())
    .Build();
Fixed rate, the defaultMeasured from completion
A 300 ms job every second starts at0, 1, 2 s0, 1.3, 2.6 s
While a run is in progressThe next may start beside it, as the overlap policy saysThe trigger is held, on every node. The overlap policy is never asked
Repeat countCounts runsCounts runs
End timeBounds the runsBounds the runs: a completion whose next run would be past it ends the trigger
A failed run with a retry policyRetries, unless the next occurrence comes firstRetries at its retry time; the delay follows the last attempt
A vetoed run, or one the scheduler could not startKeeps its scheduleCounts as completed when the store hears of it
  • While a run is in progress, NextFireTimeUtc is provisional: the run's scheduled time plus the interval. The completion replaces it.
  • A calendar moves the next run on by whole intervals past what it excludes.
  • It holds this trigger only. The job's other triggers and TriggerJob still start beside it; use [DisallowConcurrentExecution] to hold the job.
  • The [SimpleTrigger] attribute, the JSON Simple schedule and XML's <simple> carry it as MeasureIntervalFromCompletion and <measure-interval-from-completion>.
  • A persistent store needs the 4.7 migration, add_trigger_fixed_delay_<db>.sql. A cluster measures from completion only once every node is 4.7 or later: an older node runs the trigger at a fixed rate.

A run missed while the scheduler was down has no backlog, because each run is measured from a completion:

Misfire instructionMeasured from completion
SmartPolicyFires once now, then measures from that run's completion
IgnoreMisfiresFires once now
FireNowFires once now; the missed run does not count
NowWithExistingCountFires once now; the missed run does not count
NowWithRemainingCountFires once now; the missed run counts against the repeat count
NextWithExistingCountSkips the missed run; the next is one interval from now
NextWithRemainingCountSkips the missed run, which counts; the next is one interval from now

Every instruction keeps to the end time and the repeat count.

SimpleTrigger Misfire Instructions

Misfires are explained in More About Triggers. SimpleTrigger's instructions are on the SimpleTriggerMisfireInstruction enum, whose API documentation describes each:

  • SimpleTriggerMisfireInstruction.IgnoreMisfires
  • SimpleTriggerMisfireInstruction.FireNow
  • SimpleTriggerMisfireInstruction.NowWithExistingCount
  • SimpleTriggerMisfireInstruction.NowWithRemainingCount
  • SimpleTriggerMisfireInstruction.NextWithRemainingCount
  • SimpleTriggerMisfireInstruction.NextWithExistingCount

SmartPolicy is available on every trigger and is the default. On a SimpleTrigger it resolves by repeat count:

Repeat countResolves to
0 — fires onceFireNow
RepeatIndefinitely — repeats foreverNextWithRemainingCount
a finite countNowWithExistingCount

FireNow on a repeating trigger is treated as NowWithRemainingCount, so the rest of the schedule is kept. The behaviour is in SimpleTriggerImpl.UpdateAfterMisfire.

Set the misfire instruction on the simple schedule (SimpleScheduleBuilder):

ITrigger trigger = TriggerBuilder.Create()
    .WithIdentity("trigger7", "group1")
    .WithSimpleSchedule(x => x
        .WithInterval(TimeSpan.FromMinutes(5))
        .RepeatForever()
        .WithMisfireInstruction(SimpleTriggerMisfireInstruction.NextWithExistingCount))
    .Build();
Help us by improving this page!
Last Updated: 10/19/26, 11:26 PM
Contributors: Marko Lahma, Claude Opus 5.5
Prev
Querying Jobs and Triggers
Next
Cron Triggers