I would like to have an event triggered in my app which runs continuously during the day at a certain time, say at 4:00pm. I thought about running the timer every second and when the time is equal to 4:00pm run the event. That works. But I'm wondering if there's a way to just get the callback once at 4:00pm and not having to keep checking.
9 Answers
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).

- 125,917
- 54
- 300
- 447
Starting with .NET 4.5 there's a really clean solution:
public async void ScheduleAction(Action action, DateTime ExecutionTime)
{
await Task.Delay((int)ExecutionTime.Subtract(DateTime.Now).TotalMilliseconds);
action();
}
Here's a solution without async/await:
public void Execute(Action action, DateTime ExecutionTime)
{
Task WaitTask = Task.Delay((int)ExecutionTime.Subtract(DateTime.Now).TotalMilliseconds);
WaitTask.ContinueWith(_ => action);
WaitTask.Start();
}
It should be noted that this only works for about 24 days out because of int32 max value, which is plenty for your case, but worth noting.

- 199
- 12

- 4,692
- 1
- 41
- 44
-
2You can actually use a TimeSpan now for Task.Delay, meaning you can go over the 24 day limit mentioned by VoteCoffee. There's essentially no limit with TimeSpan, something like 10 million days, https://msdn.microsoft.com/en-us/library/system.timespan.maxvalue(v=vs.110).aspx – chris84948 Mar 14 '16 at 14:37
-
2Nice solution, but "ExecutionTime" must be in the future or Task.Delay will throw an ArgumentOutOfRangeException. – Rob Davis Jun 16 '16 at 22:24
-
3Beware from using `async void`, an exception in `action()` will cause the entire application to crash. Read more: http://haacked.com/archive/2014/11/11/async-void-methods/ – NucS Aug 18 '17 at 15:38
-
2@chris84948 even if you give it a timespan, `Task.Delay(Date.Subtract(DateTime.Now));` it will throw AurgumentOutOfRange when exceeding int32.MaxValue. – Guy Nov 06 '17 at 14:02
-
This seems a little dangerous to me. I think you should add a check that the calculated number of milliseconds is positive. Especially since [`Task.Delay(-1)`](https://learn.microsoft.com/dotnet/api/system.threading.tasks.task.delay?view=netframework-4.8) will never complete and other negative values will throw. – binki Apr 26 '19 at 13:48
-
It's a good suggestion and something people should be aware of. I generally post the relevant piece of code, and not necessarily all of the associated overhead like error handling/input checking/etc. – VoteCoffee Apr 26 '19 at 19:40
-
This approach assumes among [other things](https://stackoverflow.com/a/55866122/145173) that the computer doesn't enter standby; otherwise, its wake-up time will be incorrect. – Edward Brey Mar 28 '23 at 11:35
Taking VoteCoffees lead, here is a compact event based solution:
/// <summary>
/// Utility class for triggering an event every 24 hours at a specified time of day
/// </summary>
public class DailyTrigger : IDisposable
{
/// <summary>
/// Time of day (from 00:00:00) to trigger
/// </summary>
TimeSpan TriggerHour { get; }
/// <summary>
/// Task cancellation token source to cancel delayed task on disposal
/// </summary>
CancellationTokenSource CancellationToken { get; set; }
/// <summary>
/// Reference to the running task
/// </summary>
Task RunningTask { get; set; }
/// <summary>
/// Initiator
/// </summary>
/// <param name="hour">The hour of the day to trigger</param>
/// <param name="minute">The minute to trigger</param>
/// <param name="second">The second to trigger</param>
public DailyTrigger(int hour, int minute = 0, int second = 0)
{
TriggerHour = new TimeSpan(hour, minute, second);
CancellationToken = new CancellationTokenSource();
RunningTask = Task.Run(async () =>
{
while (true)
{
var triggerTime = DateTime.Today + TriggerHour - DateTime.Now;
if (triggerTime < TimeSpan.Zero)
triggerTime = triggerTime.Add(new TimeSpan(24, 0, 0));
await Task.Delay(triggerTime, CancellationToken.Token);
OnTimeTriggered?.Invoke();
}
}, CancellationToken.Token);
}
/// <inheritdoc/>
public void Dispose()
{
CancellationToken?.Cancel();
CancellationToken?.Dispose();
CancellationToken = null;
RunningTask?.Dispose();
RunningTask = null;
}
/// <summary>
/// Triggers once every 24 hours on the specified time
/// </summary>
public event Action OnTimeTriggered;
/// <summary>
/// Finalized to ensure Dispose is called when out of scope
/// </summary>
~DailyTrigger() => Dispose();
}
Consumer:`
void Main()
{
var trigger = new DailyTrigger(16); // every day at 4:00pm
trigger.OnTimeTriggered += () =>
{
// Whatever
};
Console.ReadKey();
}

- 1,782
- 20
- 29
-
2Nice that this solution provides a CancellationToken - useful for my context. – rich p Apr 27 '22 at 15:40
-
You can use Task Sceduler on windows See daily trigger example for detail.
or use bellow code if you want wrote it yourself:
public void InitTimer()
{
DateTime time = DateTime.Now;
int second = time.Second;
int minute = time.Minute;
if (second != 0)
{
minute = minute > 0 ? minute-- : 59;
}
if (minute == 0 && second == 0)
{
// DoAction: in this function also set your timer interval to 24 hours
}
else
{
TimeSpan span = //new daily timespan, previous code was hourly: new TimeSpan(0, 60 - minute, 60 - second);
timer.Interval = (int) span.TotalMilliseconds - 100;
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
}
void timer_Tick(object sender, EventArgs e)
{
timer.Interval = ...; // 24 hours
// DoAction
}

- 22,252
- 5
- 45
- 83
-
@Øyvind Bråthen, I wrote this for hourly (before), and you can see it in comments, You can use DateTime.Today.AddHours(16) for 16, but also you can use it if DateTime.Now is 16:00 in your interval which will be called every hour, Also my main focus was using windows task scheduler instead of creating the weal. (I don't like to edit the code, I'll left it as homework :D, Dan code is similar to this) – Saeed Amiri Dec 25 '10 at 08:00
.NET has lots of timer classes, but they all take time spans relative to the current time. With a relative time, there are lots of things to consider before you start your timer and to monitor for while your timer is running.
- What if the computer goes into a standby state?
- What if the computer's time changes?
- What if the expiration time is after a daylight savings time transition?
- What if the user changes the computer's time zone after you started your timer such that there is a new time zone transition before expiration that wasn't previously there?
The operating system is in a great place to handle this complexity. Application code running on .NET is not.
For Windows, the NuGet package AbsoluteTimer wraps an operating system timer that expires at an absolute time.

- 40,302
- 20
- 199
- 253
I did this way to fire 7am every morning
bool _ran = false; //initial setting at start up
private void timer_Tick(object sender, EventArgs e)
{
if (DateTime.Now.Hour == 7 && _ran==false)
{
_ran = true;
Do_Something();
}
if(DateTime.Now.Hour != 7 && _ran == true)
{
_ran = false;
}
}

- 279
- 3
- 7
-
In ways this is the easiest way to make something run on a daily basis at a certain time. I use this style for daily housekeeping in some Services IMO. – Miguelito Dec 09 '19 at 15:05
-
This solution is awesome, It doesn't even need to be linked to a timer control. Every time any event is fired, it will check the time and decide if triggering the Do_Something is necessary or if it was already done. Excellent solution. – Felipe La Rotta Aug 26 '22 at 01:39
Echo to Dan's solution, using Timercallback is a quick and neat solution. Inside the method you want to schedule a task or subroutine to be run, use the following:
t = New Timer(Sub()
'method call or code here'
End Sub, Nothing, 400, Timeout.Infinite)
use of 'Timeout.Infinite' will ensure the callback will be executed only once after 400ms. I am using VB.Net.

- 4,786
- 1
- 21
- 19
Task scheduler is a better option, and it can be easily used in C#, http://taskscheduler.codeplex.com/

- 60,503
- 9
- 116
- 147
How about this solution?
Sub Main()
Dim t As New Thread(AddressOf myTask)
t.Start()
Console.ReadLine()
End Sub
Private Sub myTask()
Dim a = "14:35"
Dim format = "dd/MM/yyyy HH:mm:ss"
Dim targetTime = DateTime.Parse(a)
Dim currentTime = DateTime.Parse(Now.ToString(format))
Console.WriteLine(currentTime)
Console.WriteLine("target time " & targetTime)
Dim bb As TimeSpan = targetTime - currentTime
If bb.TotalMilliseconds < 0 Then
targetTime = targetTime.AddDays(1)
bb = targetTime - currentTime
End If
Console.WriteLine("Going to sleep at " & Now.ToString & " for " & bb.TotalMilliseconds)
Thread.Sleep(bb.TotalMilliseconds)
Console.WriteLine("Woke up at " & Now.ToString(format))
End Sub

- 80,625
- 14
- 153
- 225

- 23
- 5
t.Change(timeUntilFour, Timeout.Infinite);
gives an error saying there's no overload for timespan, timespan. I see in the docs I can also call dispose but not sure where. Any ideas? – Behrooz Karjoo Dec 25 '10 at 02:42