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
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;
}
}
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).
.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.
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.
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
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
}
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.