Releases: quartznet/quartznet
Release list
v3.18.2
Quartz.NET 3.18.2 is a maintenance release that stabilizes the dashboard introduced in 3.18.0, fixes several scheduling correctness bugs, and speeds up cron next-fire-time computation. It is a drop-in upgrade from earlier 3.18.x releases — no breaking changes and no database schema migrations.
Highlights
- Type-load failures no longer stall the whole scheduler — a trigger or job whose type failed to load could throw inside
AcquireNextTriggerand block acquisition of every other trigger. The faulting trigger is now isolated so the rest keep firing. (#3108) - "Reschedule next" misfire policies no longer fire immediately — after a misfire, the reschedule-next policies could fire once right away instead of waiting for the next scheduled time. (#3100)
- Named-scheduler JSON config is honored — schedules defined under the root
Quartzconfiguration section were not loaded for named schedulers. (#3113) - Dashboard fixes — authorization policy no longer leaks to host-app endpoints (#3067); static assets and a custom
DashboardPathwork under a fail-closedFallbackPolicy, and the Blazor circuit is allowed anonymous access under the same policy (#3098, #3120); trigger and calendar JSON deserialization is fixed (#3102); and JobDataMap and SimpleSchedule trigger details now display correctly (#3132). - MySQL
FORCE INDEXfix — schema-qualified table prefixes produced a malformedFORCE INDEXhint. (#3086) - Faster cron scheduling — cron next-fire-time computation gains a bitmask fast path and avoids needless
DateTimeOffsetchurn in the hot loop. (#3126, #3129) - Smaller enhancements — health-check tags can now be passed to
AddQuartzServer(#3112), and new public helpers make plugin configuration extensible (#3104).
What's Changed
- Fix dashboard authorization policy leaking to host app endpoints (#3066) by @lahma in #3067
- Fix malformed MySQL FORCE INDEX hint with schema-qualified table prefix by @lahma in #3086
- Fix dashboard FallbackPolicy asset blocking, honor custom DashboardPath, document Blazor integration (3.x) by @lahma in #3098
- Fix reschedule-next misfire policies firing immediately after misfire (#3096) by @lahma in #3100
- Fix dashboard trigger and calendar JSON deserialization (#3094) by @lahma in #3102
- Fix type-load exceptions in AcquireNextTrigger blocking all other triggers by @lahma with @Copilot in #3108
- Fix JSON schedule config not loaded for named schedulers using root Quartz section (#3106) by @lahma in #3113
- Allow passing health check tags to AddQuartzServer by @lahma in #3112
- Strip 3.x branch docs to NuGet package READMEs only (#3116) by @lahma in #3119
- Allow the dashboard Blazor circuit anonymous access under a fail-closed FallbackPolicy (#3117) by @lahma in #3120
- Add public plugin configuration extensibility helpers (3.x) by @lahma in #3104
- Speed up cron next-fire-time computation with a bitmask fast path by @lahma in #3126
- Reduce DateTimeOffset churn in the cron next-fire-time loop (3.x) by @lahma in #3129
- Fix dashboard JobDataMap and SimpleSchedule trigger display (3.x) by @lahma in #3132
Full Changelog: v3.18.1...v3.18.2
v3.18.1
Quartz.NET 3.18.1 is a maintenance release that addresses a timezone-related bug, security advisories on transitive dependencies, and an issue that prevented the new Redis distributed-lock package from publishing to nuget.org.
Highlights
- Fix
GetTimeBeforefor positive-offset timezones —CronExpression.GetTimeBeforecould throwArgumentOutOfRangeExceptionfor cron expressions evaluated against timezones with positive UTC offsets (e.g.Europe/Helsinki,Asia/Tokyo). Backported frommain. - Redis package renamed to
Quartz.Extensions.Redis— the originalQuartz.Redispackage id from 3.18.0 collided with an unrelated v1.0.0 already on nuget.org and could not be published. The 3.18.0 release was shipped withIsPackable=falseas a workaround; 3.18.1 picks the umbrella idQuartz.Extensions.Redis(matchingQuartz.Extensions.DependencyInjection/Quartz.Extensions.Hosting) and re-enables publication. There are no consumers to migrate — the original id was never on nuget.org. - Vulnerable transitive dependency bumps — addresses moderate advisories that were blocking restore on the 3.x CI pipeline.
What's Changed
- Fix GetTimeBefore throwing for positive-offset timezones (#3046) by @lahma in #3047
- Bump vulnerable packages to unblock 3.x CI by @lahma in #3054
- Rename Quartz.Redis package to Quartz.Extensions.Redis by @lahma in #3053
Full Changelog: v3.18.0...v3.18.1
v3.18.0
Quartz.NET 3.18.0 is a major feature release with 8 new capabilities, performance improvements, and important bug fixes.
New Features
RFC 5545 RRULE recurrence trigger
Quartz.NET now supports scheduling with iCalendar recurrence rules (RFC 5545 RRULE), enabling complex patterns that cannot be expressed with cron expressions — such as "2nd Monday of every month" or "last weekday of March each year."
A custom lightweight RRULE engine (~1.5K LOC) handles all frequencies (YEARLY through SECONDLY), all BY* rules (BYDAY, BYMONTHDAY, BYSETPOS, etc.), COUNT, UNTIL, INTERVAL, and WKST. No new external dependencies. No database schema changes — uses the existing SIMPROP_TRIGGERS table.
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("myTrigger")
.WithRecurrenceSchedule("FREQ=MONTHLY;BYDAY=2MO", b => b
.InTimeZone(TimeZoneInfo.FindSystemTimeZoneById("America/New_York")))
.StartNow()
.Build();| RRULE | Pattern |
|---|---|
FREQ=MONTHLY;BYDAY=2MO |
Every 2nd Monday of the month |
FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,WE,FR |
Every other week on Mon/Wed/Fri |
FREQ=YEARLY;BYMONTH=3;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1 |
Last weekday of March each year |
FREQ=MONTHLY;BYMONTHDAY=-1 |
Last day of every month |
FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR |
Every weekday |
Execution groups for per-node thread limits
Tag triggers with an execution group to limit how many threads a category of jobs can consume concurrently on each node. This prevents resource-intensive jobs from starving lightweight work.
services.AddQuartz(q =>
{
q.UseExecutionLimits(limits =>
{
limits.ForGroup("batch-jobs", maxConcurrent: 2);
limits.ForDefaultGroup(maxConcurrent: 10);
limits.ForOtherGroups(maxConcurrent: 5);
});
q.AddTrigger(t => t
.ForJob("heavyJob")
.WithExecutionGroup("batch-jobs")
.WithCronSchedule("0 0/5 * * * ?"));
});Also configurable via properties (quartz.executionLimit.batch-jobs = 2) and runtime API (scheduler.SetExecutionLimits(...)). Includes optional EXECUTION_GROUP column for ADO.NET job stores with graceful fallback when absent, and Dashboard integration.
Multiple named schedulers in Microsoft DI
Register multiple independent scheduler instances in a single DI container. Each named scheduler gets isolated options, jobs, triggers, listeners, and calendars.
services.AddQuartz("Scheduler1", q =>
{
q.AddJob<EmailJob>(j => j.WithIdentity("email"));
q.AddTrigger(t => t.ForJob("email").WithCronSchedule("0 0/5 * * * ?"));
});
services.AddQuartz("Scheduler2", q =>
{
q.UsePersistentStore(s => { /* ... */ });
q.AddJob<ReportJob>(j => j.WithIdentity("report"));
q.AddTrigger(t => t.ForJob("report").WithCronSchedule("0 0 * * * ?"));
});
services.AddQuartzHostedService(o => o.WaitForJobsToComplete = true);AddQuartzHostedService() automatically manages the lifecycle of all registered schedulers.
JSON configuration and scheduling data
Configure Quartz.NET using hierarchical JSON in appsettings.json instead of flat property keys. Supports declarative job and trigger definitions, all 4 trigger types, and named schedulers via a Schedulers section.
{
"Quartz": {
"Scheduler": { "InstanceName": "My Scheduler" },
"ThreadPool": { "MaxConcurrency": 10 },
"Schedule": {
"Jobs": [{ "Name": "myJob", "JobType": "MyApp.Jobs.MyJob, MyApp", "Durable": true }],
"Triggers": [{ "Name": "myTrigger", "JobName": "myJob", "Cron": { "Expression": "0/30 * * * * ?" } }]
}
}
}services.AddQuartz(Configuration.GetSection("Quartz"));Also includes a standalone JsonSchedulingDataProcessorPlugin for quartz_jobs.json file support with hot-reload, mirroring XMLSchedulingDataProcessorPlugin.
(#3012, #3015, #3017) — Closes #1755
Redis-based distributed lock handler (new Quartz.Extensions.Redis package)
Note: This package is published as
Quartz.Extensions.Redisstarting with 3.18.1. The package was authored as part of 3.18.0 but had to ship withIsPackable=falsebecause the originally-chosen idQuartz.Rediswas already taken on nuget.org by an unrelated v1.0.0. See #3053. To use this feature, installQuartz.Extensions.Redis3.18.1 or later.
New Quartz.Extensions.Redis NuGet package providing RedisSemaphore — an ISemaphore implementation using Redis SET NX PX distributed locks instead of database row locks. This eliminates DB row lock contention and deadlocks in clustered setups while keeping job/trigger data in the relational database.
Uses two-tier locking: local SemaphoreSlim prevents redundant Redis round-trips within the same process, and a Lua script ensures atomic check-and-delete on release for safety.
services.AddQuartz(q =>
{
q.UsePersistentStore(store =>
{
store.UseRedisLockHandler(redis =>
{
redis.RedisConfiguration = "redis-server:6379";
});
});
});Activity tracing for ADO.NET job store operations
28 IJobStore methods are now wrapped with System.Diagnostics.Activity spans, so database calls appear as children of named Quartz operations (e.g., Quartz.JobStore.AcquireNextTriggers) instead of orphaned root spans in your tracing system. Zero overhead when tracing is disabled.
[Quartz.JobStore.AcquireNextTriggers]
└── [SELECT ... FROM TRIGGERS]
└── [INSERT ... INTO FIRED_TRIGGERS]
[Quartz.JobStore.TriggersFired]
└── [UPDATE ... TRIGGERS SET STATE=...]
[Quartz.Job.Execute]
└── [user job work]
All new operations are automatically included in QuartzInstrumentationOptions.DefaultTracedOperations.
UpdateTriggerDetails — update trigger metadata without rescheduling
New UpdateTriggerDetails method updates Description, Priority, JobDataMap, CalendarName, and MisfireInstruction on an existing trigger without resetting fire times, trigger state, or misfire context. Available via IScheduler.UpdateTriggerDetails() extension method.
Factory-based AddQuartz() with IServiceProvider access
New AddQuartz overloads accepting Action<IServiceCollectionQuartzConfigurator, IServiceProvider> allow resolving DI services during Quartz configuration — useful for obtaining connection strings, feature flags, or other configuration from DI-registered services.
services.AddQuartz((q, sp) =>
{
var config = sp.GetRequiredService<DatabaseConfig>();
q.UsePersistentStore(s =>
{
s.UseSqlServer(sql => sql.ConnectionString = config.ConnectionString);
});
});Performance
Reduced DB round-trips in misfire recovery
Misfire recovery now uses a targeted UPDATE instead of routing each trigger through the full StoreTrigger path, reducing per-trigger DB round-trips from 7-12 down to 1-2 (~87% reduction). For a batch of 20 cron triggers, this drops from ~150 queries to ~20 queries — all under LockTriggerAccess. Calendar lookups are also cached across the batch.
Bug Fixes
- Fix scheduler signal loss causing triggers stuck in WAITING state —
SemaphoreSlim(0, 1)inQuartzSchedulerThreadcould silently drop scheduling signals viaSemaphoreFullException, causing triggers to stay stuck in WAITING state until the next idle loop timeout. (#3033) — Fixes #3028 - Fix SchedulerRepository preventing connections to multiple cluster nodes —
SchedulerRepositoryindexed by scheduler name only, preventing multiple remote proxies to different nodes in the same cluster from coexisting. Now supports instance-aware lookup. (#2991) — Fixes #388 - Implement
IsJobGroupPaused/IsTriggerGroupPausedin ADO.NET job store — These methods previously threwNotImplementedExceptionin the persistent job store. (#3030) - Fix Dashboard Live not working over HTTP — Dashboard live updates now work correctly over plain HTTP connections. (#3032)
- Fix dashboard plugins using static
ServiceProvider— Dashboard plugins no longer rely on a staticServiceProviderreference, fixing issues with multiple host instances. (#3035) — Fixes #3026
Deprecations
DirtyFlagMap.Get()— use the indexer (map[key]) instead (#2986)DirtyFlagMap.Put()/PutAll()— use the indexer or collection initializer instead (#2989)
What's Changed
v3.17.1
Highlights
Jenkins-style H (hash) token for cron expressions
Quartz.NET now supports the H (hash) token in cron expressions, inspired by Jenkins. The H token resolves to a deterministic value based on the trigger's identity, spreading job execution times to avoid the thundering herd problem when many triggers share the same schedule.
Supported forms: H, H(min-max), H/step, H(min-max)/step
// Runs at a consistent but spread-out minute each hour
trigger = TriggerBuilder.Create()
.WithIdentity("myTrigger", "myGroup")
.WithCronSchedule("0 H * * * ?")
.Build();
// Runs every 15 minutes, starting at a hash-determined offset
trigger = TriggerBuilder.Create()
.WithIdentity("myTrigger", "myGroup")
.WithCronSchedule("0 H/15 * * * ?")
.Build();The hash seed is automatically derived from the trigger's identity (name + group) when using TriggerBuilder, or can be provided explicitly. See the cron trigger documentation for full details.
Structured logging history plugins
New StructuredLoggingJobHistoryPlugin and StructuredLoggingTriggerHistoryPlugin provide first-class support for structured logging frameworks like Serilog and NLog. Unlike the existing logging plugins that use index-based format placeholders ({0}, {1}), these use named MEL-style message template parameters ({JobName}, {TriggerGroup}, etc.), making log output queryable in structured logging sinks and avoiding template cache memory leaks.
quartz.plugin.jobHistory.type = Quartz.Plugin.History.StructuredLoggingJobHistoryPlugin, Quartz.Plugins
quartz.plugin.triggerHistory.type = Quartz.Plugin.History.StructuredLoggingTriggerHistoryPlugin, Quartz.Plugins
Message templates are fully configurable via standard Quartz property configuration.
Bug Fixes
- Fix triggers getting permanently stuck in ACQUIRED state — Triggers could become permanently stuck if the scheduler crashed or was killed between acquiring a trigger and firing it. (#2980)
- Fix StoreCalendar overriding paused trigger state — Storing a calendar with
updateTriggers: trueno longer resets paused triggers back to normal state. (#2968) - Fix DoCheckin not retrying transient database errors — Cluster checkin now properly retries on transient database failures instead of silently failing. (#2970)
- Fix Dashboard
/_blazorendpoint conflict — The Quartz Dashboard no longer conflicts with host applications that also use Blazor. (#2975) - Fix spurious ERROR log on zombie transaction rollback — Rolling back a zombie transaction no longer logs a misleading ERROR. (#2978)
Full Changelog: v3.17.0...v3.17.1
v3.17.0
This is a major bug-fix release with 40+ fixes spanning trigger state management, clustering reliability, DST handling, misfire accuracy, and scheduler lifecycle. An optional database schema migration improves misfire handling.
Highlights
Optional database migration: MISFIRE_ORIG_FIRE_TIME column
A new optional column MISFIRE_ORIG_FIRE_TIME on QRTZ_TRIGGERS enables correct ScheduledFireTimeUtc for misfired triggers when using "fire now" misfire policies. Without it, ScheduledFireTimeUtc equals FireTimeUtc for misfired triggers (the pre-existing behavior). RAMJobStore does not require this migration.
Migration script: database/schema_30_add_misfire_orig_fire_time.sql (covers SQL Server, PostgreSQL, MySQL, SQLite, Oracle, Firebird)
Clustering and concurrency fixes
- Fix
DisallowConcurrentExecutionjobs running simultaneously in cluster (#2697) - Fix false cluster recovery causing
DisallowConcurrentExecutionviolation (#2915) - Fix triggers stuck in BLOCKED state for
DisallowConcurrentExecutionjobs (#2822) - Fix
FIRED_TRIGGERSnot cleaned up on job deletion mid-execution (#1696) - Handle transient database exceptions (deadlocks) with automatic retry (#2883, #2952)
- Fix PostgreSQL transaction abort error on lock contention (#2884)
- Fix SQLite "database is locked" errors with dedicated semaphore (#2323)
- Add MySQL FORCE INDEX hints for slow trigger acquisition queries (#547)
Trigger state and fire time accuracy
- Fix
GetTriggerStatereturning Complete instead of Blocked for executing triggers (#2255) - Fix
RAMJobStore.TriggersFiredskipping triggers causing wrong trigger/job execution (#1386) - Fix
ScheduledFireTimeUtcreturning wrong value after misfire (#2899) - Fix
PreviousFireTimeUtcreset to null on restart withOverWriteExistingData=true(#1834) - Fix
SimpleTriggerfirst fire time wrong when created long before scheduling (#2455) - Fix
CronTriggerdouble-firing when rescheduled with oldStartTimeUtc(#2909) - Fix recovery trigger missing original
JobData(#2083) - Fix misfired blocked triggers not being deleted from database (#1646)
- Fix
DoNothingmisfire policy skipping fire times within threshold (#2912)
DST and time handling
- Fix
DailyTimeIntervalTriggerextra fire during DST fall-back (#2917) - Fix
DailyTimeIntervalTriggermutatingStartTimeUtcduring fire time computation (#2906) - Fix
DailyTimeIntervalTriggerRepeatCountto apply per day (#1633) - Fix scheduler thread stuck after system clock jumps backward (#1508)
- Fix
ComputeFireTimesBetweenmodifying trigger StartTimeUtc (#2722)
Scheduler lifecycle and threading
- Fix scheduler hang on shutdown: replace
Monitor.Waitwith asyncSemaphoreSlim(#2877) - Fix shutdown deadlock (#2830)
- Release acquired triggers on shutdown instead of leaking them (#2881)
- Fix
DedicatedThreadPoolthreads leaking on scheduler shutdown (#1357) - Use dedicated thread for scheduler loop to prevent missed triggers under high CPU (#781)
Job execution
- Fix
RefireImmediatelywithJobChainingJobListenerfiring chain prematurely (#663) - Fix
AsyncLocalflow fromIJobFactory.NewJobtoIJob.Execute(#1528) - Add
IJobDetailproperty toJobExecutionException(#1442)
API and configuration
- Fix
idleWaitTimeof zero silently ignored instead of throwing (#1394) - Fix
AddJob/AddTriggerambiguous references without removing overloads (#2795) - Fix
PauseJobs/ResumeJobinteraction bug in RAMJobStore (#761) - Fix
RemoteSchedulerignoring localquartz.scheduler.instanceName(#313) - Restore DB trigger fields for custom (blob) triggers (#2949)
Dashboard
- Fix dashboard CSS 404 in API-only projects (#2886)
What's Changed
- Add IJobDetail property to JobExecutionException by @lahma in #2869
- Fix ComputeFireTimesBetween modifying trigger StartTimeUtc by @lahma in #2870
- Convert integration tests to use Testcontainers.NET by @lahma in #2873
- docs: document RequiresAspNetWebAssets for API-only apps (.NET 10+) by @lahma in #2875
- Fix DoCheckin lock returner warning and ReferenceEquals comparisons by @lahma in #2876
- Fix scheduler hang on shutdown: replace Monitor.Wait with async SemaphoreSlim by @lahma in #2877
- Release acquired triggers on shutdown instead of leaking them by @lahma in #2881
- Port: Handle transient database exceptions with automatic retry (3.x) by @lahma in #2883
- Port PostgreSQL transaction abort fix to 3.x (#2829) by @lahma in #2884
- Port DST fall-back infinite trigger fix to 3.x (#2865) by @lahma in #2885
- Fix dashboard CSS 404 in API-only projects (#2886) by @lahma in #2887
- Port shutdown deadlock fix to 3.x (#2830) by @lahma in #2889
- Use dedicated thread for scheduler loop to prevent missed triggers under high CPU (#781) by @lahma in #2895
- Fix triggers stuck in BLOCKED state for DisallowConcurrentExecution jobs by @Copilot in #2822
- Split integration tests into per-database CI workflows by @lahma in #2900
- Fix ScheduledFireTimeUtc returning wrong value after misfire by @lahma in #2899
- Remove docker folder and docker-compose.yml by @lahma in #2904
- Fix recovery trigger missing original JobData (#2083) by @lahma in #2905
- Fix CronTrigger double-firing when rescheduled with old StartTimeUtc by @lahma in #2909
- Fix DailyTimeIntervalTrigger mutating StartTimeUtc during fire time computation by @lahma in #2906
- Fix DisallowConcurrentExecution jobs running simultaneously in cluster by @lahma in #2908
- Fix DoNothing misfire policy skipping fire times within threshold by @lahma in #2912
- Fix false cluster recovery causing DisallowConcurrentExecution violation by @lahma in #2915
- Fix DailyTimeIntervalTrigger RepeatCount to apply per day (#1633) by @lahma in #2918
- Add DST spring-forward tests for daily, monthly, and EU timezone intervals by @lahma in #2919
- Fix DailyTimeIntervalTrigger extra fire during DST fall-back by @lahma in #2917
- Fix SQLite database is locked errors (#2323) by @lahma in #2923
- Fix PauseJobs/ResumeJob interaction bug in RAMJobStore by @lahma in #2893
- Add MySQL FORCE INDEX hints for slow trigger queries by @lahma in #2894
- Fix misfired blocked triggers not being deleted from database by @Copilot in #2820
- Fix AddJob/AddTrigger ambiguous references without removing overloads (#2795) by @lahma in #2925
- Add CronExpression DST regression tests (#2156) by @lahma in #2927
- Fix scheduler thread stuck after system clock jumps backward (#1508) by @lahma in #2929
- Fix AsyncLocal flow from IJobFactory.NewJob to IJob.Execute (#1528) by @lahma in #2930
- Fix RAMJobStore.TriggersFired skipping triggers causing wrong job execution (#1386) by @lahma in #2933
- Enable NUnit parallel test fixture execution by @lahma in #2936
- Fix GetTriggerState returning Complete instead of Blocked for executing triggers (#2255) by @lahma in #2935
- Fix unit tests failing on DST transition days by @lahma in #2941
- Fix RefireImmediately with JobChainingJobListener firing chain prematurely (#663) by @lahma in #2940
- Fix FIRED_TRIGGERS not cleaned up on job deletion mid-execution (#1696) by @lahma in #2945
- Restore DB trigger fields for custom (blob) triggers by @lahma in #2949
- Backport trigger type switch test coverage to 3.x by @lahma in #2948
- Fix idleWaitTime of zero silently ignored instead of throwing (#1394) by @lahma in #2950
- Fix RemoteScheduler ignoring local quartz.scheduler.instanceName (#313) by @lahma in #2953
- Fix DedicatedThreadPool threads leaking on scheduler shutdown by @lahma in #2954
- Fix TriggersFired not retrying transient database errors by @lahma in #2952
- Fix PreviousFireTimeUtc reset to null on restart with OverWriteExistingData (#1834) by @lahma in #2957
- Fix SimpleTrigger f...
Quartz.NET 3.16.1
This release hopefully fixes the hiccup we had with the versioning/packaging of the new Dashboard package.
What's Changed
- Add missing packaging and publish for Quartz.Dashboard by @lahma in #2853
- Fix CronTrigger incorrectly scheduling when end date is in the past by @jafin in #2856
Full Changelog: v3.16.0...v3.16.1
Quartz.NET 3.16.0
On top of great community fixes, this release also brings the experimental Dashboard into action. You can enable it on NET 8 and later, see documentation for details.
What's Changed
- Add tests for AddJob, AddTrigger, ScheduleJob and AddCalendar by @bdovaz in #2794
- Use NET 10 SDK and update projects to use NET 10 by @lahma in #2800
- Handle ObjectDisposedException when canceling jobs by @lahma in #2811
- Fix trigger ERROR state during shutdown - release acquired triggers gracefully by @lahma in #2812
- fix (CronExpression): Throw Exception for invalid cron expressions by @jafin in #2836
- Convert solution file to slnx format by @lahma in #2840
- Fix XML scheduling plugin to log errors and notify listeners on parsing failures by @jafin in #2839
- Add IdleWaitTime property to SchedulerBuilder (#2835) by @lahma in #2842
- Fix CronExpression not always returning correct next schedule for weekdayonly and month flags by @jafin in #2838
- fix(CalendarIntervalTriggerImpl): infinite loop bug in
CalendarIntervalTriggerImplby @jafin in #2837 - Fix GetScheduler(name) returning null before default scheduler initialization by @jafin in #2845
- Port DirectoryScanJob listener DI support to 3.x by @lahma in #2851
- Backport Quartz.Dashboard to 3.x (in-process only) by @lahma in #2852
Full Changelog: v3.15.1...v3.16.0
Quartz.NET 3.15.1
Quartz.NET 3.15.0
What's Changed
- allow to execute a job if remaining day time equals interval by @sherlock1982 in #2726
- Switch to file-scoped namespaces by @lahma in #2750
- Refactor PostgreSQL script to lower case qrtz_simprop_triggers table … by @hannes-justsolve in #2753
- Fix IDriverDelegate XML documentation for SelectTriggerToAcquire by @lahma in #2757
- Interrupt the scheduler thread when shutdown is occurring by @lahma in #2758
- Validate that cron expression has at most 7 tokens by @lahma in #2760
- When storing job with replace check update count returned by @lahma in #2761
- Introduce JobExecutionProcessException to provide more context by @lahma in #2762
- Fix DailyTimeIntervalTrigger.RepeatInterval setter validation by @lahma in #2763
- Fix CronExpression increment error messages by @lahma in #2764
- Implement CronExpression.GetTimeBefore() by @lahma in #2765
- Optimize cron parsing by @lahma in #2769
- Preserve Dates in Newtonsoft.Json NameValueCollectionConverter by @lahma in #2770
New Contributors
- @sherlock1982 made their first contribution in #2726
- @hannes-justsolve made their first contribution in #2753
Full Changelog: v3.14.0...v3.15.0
Quartz.NET 3.14.0
What's Changed
- Change
DirtyFlagMap.Addto useAllowNullAttributeinstead ofMaybeNullAttributeby @fbenoit-coexya in #2660 - Don't transition triggers to error state on scheduler shutdown/disposal by @lahma in #2704
- Handle hosted service stopping during startup gracefully by @lahma in #2705
- Upgrade packages by @lahma in #2706
- Add NET 8 and NET 9 targets, remove NET 6 target by @lahma in #2707
New Contributors
- @fbenoit-coexya made their first contribution in #2660
Full Changelog: v3.13.1...v3.14.0