How to Use the .Net Timer Class to Trigger an Event at a Specific Time

How to use the .NET Timer class to trigger an event at a specific time?

How about something like this, using the System.Threading.Timer class?

var t = new Timer(TimerCallback);

// Figure how much time until 4:00
DateTime now = DateTime.Now;
DateTime fourOClock = DateTime.Today.AddHours(16.0);

// If it's already past 4:00, wait until 4:00 tomorrow
if (now > fourOClock)
{
fourOClock = fourOClock.AddDays(1.0);
}

int msUntilFour = (int)((fourOClock - now).TotalMilliseconds);

// Set the timer to elapse only once, at 4:00.
t.Change(msUntilFour, Timeout.Infinite);

Note that if you use a System.Threading.Timer, the callback specified by TimerCallback will be executed on a thread pool (non-UI) thread—so if you're planning on doing something with your UI at 4:00, you'll have to marshal the code appropriately (e.g., using Control.Invoke in a Windows Forms app, or Dispatcher.Invoke in a WPF app).

C# How can I trigger an event at a specific time of day?

Think out of the box.

No need for coding on this kind of job - use Scheduled Tasks, they have been in windows for a long time. You can kick off your program from this.

Update: (following update to question)

If you need to trigger a method from an already running service, use a timer and test DateTime.Now against your target time.

How to start a timer at a specific time for a specific time?

The method below starts an initial timer with an interval based on the datetime supplied (i.e. the time entered into your textbox). When the the time is reached it starts a secondary timer that will fire every four hours from that point on.

I used System.Timers Timer rather than the Sytem.Threading Timer (because ironically the system.threading timer is not thread safe!)

    public void StartTimer(DateTime time)
{
const int fourHoursInMilliseconds = 4 * 3600 * 1000;

var fourHourTimer = new Timer() {Interval = fourHoursInMilliseconds};
fourHourTimer.Elapsed += (sender, e) =>
{
//code to handle the elapsed event here
};

var span = time - DateTime.Now;
var timer = new Timer {Interval = span.TotalMilliseconds, AutoReset = false};
timer.Elapsed += (sender, e) => { fourHourTimer.Start();};
timer.Start();
}

Hope this is what you need.

How to periodically trigger an time-lapse event every specific time interval

Have UpdateEvent set the timer interval after an event:

private void UpdateEvent()
{
if (EventIndex.Equals(1))
{
timer1.Interval = //delay until event B;
}
else
{
timer1.Interval = //delay until event A;
}
}

How to set timer to execute at specific time in c#

What you should do is write your program that does whatever you need it to do, and then use your OS's built-in task scheduler to fire it off. That'd be the most reliable. Windows's Task Scheduler, for instance, can start your app before the user logs in, handle restarting the app if necessary, log errors and send notifications, etc.

Otherwise, you'll have to run your app 24/7, and have it poll for the time at regular intervals.

For instance, you could change the interval every minute:

timer.Interval = 1000 * 60;

And inside your Elapsed event, check the current time:

static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
if (DateTime.Now.Hour == 1 && DateTime.Now.Minute == 0)
{
// do whatever
}
}

But this is really unreliable. Your app may crash. And dealing with DateTime's can be tricky.

C#: How to start a thread at a specific time

You can set up a timer at 16:00. I've answered a similar question here.
That should help you for sure.

private System.Threading.Timer timer;
private void SetUpTimer(TimeSpan alertTime)
{
DateTime current = DateTime.Now;
TimeSpan timeToGo = alertTime - current.TimeOfDay;
if (timeToGo < TimeSpan.Zero)
{
return;//time already passed
}
this.timer = new System.Threading.Timer(x =>
{
this.SomeMethodRunsAt1600();
}, null, timeToGo, Timeout.InfiniteTimeSpan);
}

private void SomeMethodRunsAt1600()
{
//this runs at 16:00:00
}

Then set it up using

SetUpTimer(new TimeSpan(16, 00, 00));

Edit: Keep the reference of the Timer as it's subject to garbage collection irrespective of the Timer is active or not.

How to call a method daily, at specific time, in C#?

  • Create a console app that does what you're looking for
  • Use the Windows "Scheduled Tasks" functionality to have that console app executed at the time you need it to run

That's really all you need!

Update: if you want to do this inside your app, you have several options:

  • in a Windows Forms app, you could tap into the Application.Idle event and check to see whether you've reached the time in the day to call your method. This method is only called when your app isn't busy with other stuff. A quick check to see if your target time has been reached shouldn't put too much stress on your app, I think...
  • in a ASP.NET web app, there are methods to "simulate" sending out scheduled events - check out this CodeProject article
  • and of course, you can also just simply "roll your own" in any .NET app - check out this CodeProject article for a sample implementation

Update #2: if you want to check every 60 minutes, you could create a timer that wakes up every 60 minutes and if the time is up, it calls the method.

Something like this:

using System.Timers;

const double interval60Minutes = 60 * 60 * 1000; // milliseconds to one hour

Timer checkForTime = new Timer(interval60Minutes);
checkForTime.Elapsed += new ElapsedEventHandler(checkForTime_Elapsed);
checkForTime.Enabled = true;

and then in your event handler:

void checkForTime_Elapsed(object sender, ElapsedEventArgs e)
{
if (timeIsReady())
{
SendEmail();
}
}

How to trigger .NET Core 3.1 Hosted Service at certain time?

By using the Timer method, you could run the task at a specific time, calculate the timespan between the current and target time and pass it as the dueTime parameter.

code as below:

    public Task StartAsync(CancellationToken cancellationToken)
{
//DateTime.Today: 00:00:00
TimeSpan delayTime = DateTime.Today.AddHours(21) - DateTime.Now;
TimeSpan intervalTime = TimeSpan.FromMinutes(1); //86400000s == 1 day
_timer = new Timer(DoWork, null, delayTime, intervalTime);
return Task.CompletedTask;
}

Besides, the timer or Task.Delay() methods are more applied to executing a method at specified intervals, if you want to implement scheduled tasks, I suggest you could also try to use the Cronos package and the Cron Expressions to configure the scheduled task (reference: link).

The Cronos package is a lightweight but full-fledged library for parsing cron expressions and calculating next occurrences with time zones and daylight saving time in mind. The Cronos is an open source project sponsored by HangfireIO, and you can read detailed documentation from its GitHub repository.
The details steps as below:

  1. Install the Cronos package via NuGet.

  2. Create a CronJobService service with the following code:

     public abstract class CronJobService : IHostedService, IDisposable
    {
    private System.Timers.Timer _timer;
    private readonly CronExpression _expression;
    private readonly TimeZoneInfo _timeZoneInfo;

    protected CronJobService(string cronExpression, TimeZoneInfo timeZoneInfo)
    {
    _expression = CronExpression.Parse(cronExpression);
    _timeZoneInfo = timeZoneInfo;
    }

    public virtual async Task StartAsync(CancellationToken cancellationToken)
    {
    await ScheduleJob(cancellationToken);
    }

    protected virtual async Task ScheduleJob(CancellationToken cancellationToken)
    {
    var next = _expression.GetNextOccurrence(DateTimeOffset.Now, _timeZoneInfo);
    if (next.HasValue)
    {
    var delay = next.Value - DateTimeOffset.Now;
    if (delay.TotalMilliseconds <= 0) // prevent non-positive values from being passed into Timer
    {
    await ScheduleJob(cancellationToken);
    }
    _timer = new System.Timers.Timer(delay.TotalMilliseconds);
    _timer.Elapsed += async (sender, args) =>
    {
    _timer.Dispose(); // reset and dispose timer
    _timer = null;

    if (!cancellationToken.IsCancellationRequested)
    {
    await DoWork(cancellationToken);
    }

    if (!cancellationToken.IsCancellationRequested)
    {
    await ScheduleJob(cancellationToken); // reschedule next
    }
    };
    _timer.Start();
    }
    await Task.CompletedTask;
    }

    public virtual async Task DoWork(CancellationToken cancellationToken)
    {
    await Task.Delay(5000, cancellationToken); // do the work
    }

    public virtual async Task StopAsync(CancellationToken cancellationToken)
    {
    _timer?.Stop();
    await Task.CompletedTask;
    }

    public virtual void Dispose()
    {
    _timer?.Dispose();
    }
    }

    public interface IScheduleConfig<T>
    {
    string CronExpression { get; set; }
    TimeZoneInfo TimeZoneInfo { get; set; }
    }

    public class ScheduleConfig<T> : IScheduleConfig<T>
    {
    public string CronExpression { get; set; }
    public TimeZoneInfo TimeZoneInfo { get; set; }
    }

    public static class ScheduledServiceExtensions
    {
    public static IServiceCollection AddCronJob<T>(this IServiceCollection services, Action<IScheduleConfig<T>> options) where T : CronJobService
    {
    if (options == null)
    {
    throw new ArgumentNullException(nameof(options), @"Please provide Schedule Configurations.");
    }
    var config = new ScheduleConfig<T>();
    options.Invoke(config);
    if (string.IsNullOrWhiteSpace(config.CronExpression))
    {
    throw new ArgumentNullException(nameof(ScheduleConfig<T>.CronExpression), @"Empty Cron Expression is not allowed.");
    }

    services.AddSingleton<IScheduleConfig<T>>(config);
    services.AddHostedService<T>();
    return services;
    }
    }
  3. create a ScheduleJob.cs:

     public class ScheduleJob: CronJobService
    {
    private readonly ILogger<ScheduleJob> _logger;

    public ScheduleJob(IScheduleConfig<ScheduleJob> config, ILogger<ScheduleJob> logger)
    : base(config.CronExpression, config.TimeZoneInfo)
    {
    _logger = logger;
    }

    public override Task StartAsync(CancellationToken cancellationToken)
    {
    _logger.LogInformation("ScheduleJob starts.");
    return base.StartAsync(cancellationToken);
    }

    public override Task DoWork(CancellationToken cancellationToken)
    {
    _logger.LogInformation($"{DateTime.Now:hh:mm:ss} ScheduleJob is working.");
    return Task.CompletedTask;
    }

    public override Task StopAsync(CancellationToken cancellationToken)
    {
    _logger.LogInformation("ScheduleJob is stopping.");
    return base.StopAsync(cancellationToken);
    }
    }
  4. Register the ScheduleJob service in the ConfigureServices method.

     public void ConfigureServices(IServiceCollection services)
    {
    services.AddHostedService<HelloWorldHostedService>();

    services.AddCronJob<ScheduleJob>(c=>
    {
    c.TimeZoneInfo = TimeZoneInfo.Local;
    c.CronExpression = @"25 21 * * *"; // 21:25 PM daily.
    });

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }

    Then the result as below:

    Sample Image

Need A Timer To Fire At Specific Time And Every 5 Minutes Until Job Complete

Here's a basic framework you can start from:

Public Class Form1

Private Enum MaintenanceState
WaitingToStart
Started
End Enum

Private Target As DateTime
Private state As MaintenanceState = MaintenanceState.WaitingToStart
Private MaintenanceTime As New TimeSpan(4, 0, 0) ' 4:00 am
Private WaitingInterval As New TimeSpan(0, 5, 0) ' Five minutes

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Target = GetNextMaintenanceTarget(MaintenanceTime)
tmr_Maintenance.Interval = 1000
tmr_Maintenance.Start()
End Sub

Private Sub tmr_Maintenance_Tick(sender As Object, e As EventArgs) Handles tmr_Maintenance.Tick
' optionally display time remaining until next target
Dim ts As TimeSpan = Target.Subtract(DateTime.Now)
Label1.Text = ts.ToString("hh\:mm\:ss")

' see if we've hit the target
If DateTime.Now >= Target Then
tmr_Maintenance.Stop()

Select Case state
Case MaintenanceState.WaitingToStart

' ... start all the jobs ...

state = MaintenanceState.Started
Target = DateTime.Now.Add(WaitingInterval)

Case MaintenanceState.Started

' ... check to see if the jobs are all done ...

If Not AllJobsCompleted Then
Target = DateTime.Now.Add(WaitingInterval)
Else
state = MaintenanceState.WaitingToStart
Target = GetNextMaintenanceTarget(MaintenanceTime)
End If

End Select

tmr_Maintenance.Start()
End If
End Sub

Private Function GetNextMaintenanceTarget(ByVal time As TimeSpan) As DateTime
Dim dt As DateTime = DateTime.Today.Add(time)
If DateTime.Now > dt Then
dt = dt.AddDays(1) ' already past target time for today, next start is tomorrow
End If
Return dt
End Function

End Class


Related Topics



Leave a reply



Submit