using Cronos; public abstract class CronJobService : IHostedService, IDisposable { private System.Timers.Timer _timer; private readonly CronExpression _expression; private readonly TimeZoneInfo _timeZoneInfo; private DateTimeOffset? _nextRun; protected abstract Task ExecuteAsync(CancellationToken cancellationToken); protected CronJobService(string cronExpression, TimeZoneInfo timeZoneInfo) { _expression = CronExpression.Parse(cronExpression, CronFormat.Standard); _timeZoneInfo = timeZoneInfo; } public virtual async Task StartAsync(CancellationToken cancellationToken) { await ScheduleJob(cancellationToken); } protected virtual async Task ScheduleJob(CancellationToken cancellationToken) { _nextRun = _expression.GetNextOccurrence(DateTimeOffset.Now, _timeZoneInfo); if (_nextRun.HasValue) { var delay = _nextRun.Value - DateTimeOffset.Now; if (delay.TotalMilliseconds <= 0) { await ScheduleJob(cancellationToken); return; } _timer = new System.Timers.Timer(delay.TotalMilliseconds); _timer.Elapsed += async (sender, args) => { _timer.Dispose(); if (!cancellationToken.IsCancellationRequested) { await ExecuteAsync(cancellationToken); } if (!cancellationToken.IsCancellationRequested) { await ScheduleJob(cancellationToken); } }; _timer.Start(); } } public virtual Task StopAsync(CancellationToken cancellationToken) { _timer?.Stop(); return Task.CompletedTask; } public virtual void Dispose() { _timer?.Dispose(); } }