I have a service that always works, it has a timer to perform a certain action every day at 2am.
TimeSpan runTime = new TimeSpan(2, 0, 0); // 2 AM TimeSpan timeToFirstRun = runTime - DateTime.Now.TimeOfDay; if (timeToFirstRun.TotalHours < 0) { timeToFirstRun += TimeSpan.FromDays(1.0); } _dailyNodalRunTimer = new Timer( RunNodalDailyBatch, null, timeToFirstRun, TimeSpan.FromDays(1.0)); //repeat event daily
This initialization code is called once when the service is first started, in the last few days I went to the log when the Timer started:
2011-05-21 02:00:01.580 2011-05-22 02:00:03.840 ... 2011-05-31 02:00:25.227 2011-06-01 02:00:27.423 2011-06-02 02:00:29.847
As you can see, he drifts for 2 seconds every day, farther and farther from there when he was supposed to shoot (at 2 a.m.).
Am I using this incorrectly or is this timer not intended to pinpoint? I could recreate the timer every day or it has a fire at a short interval and repeatedly check if I want to perform an action, but this is like hacking.
EDIT
I tried using System.Timers.Timer and it seems to have the same problem. Resetting the interval is that you cannot schedule the start time before the first tick in System.Timers.Timer, as you can in System.Threading.Timer
int secondsInterval = 5; double secondsUntilRunFirstRun = secondsInterval - (DateTime.Now.TimeOfDay.TotalSeconds % secondsInterval); var timer = new System.Timers.Timer(secondsUntilRunFirstRun * 1000.0); timer.AutoReset = true; timer.Elapsed += (sender, e) => { Console.WriteLine(DateTime.Now.ToString("hh:mm:ss.fff")); if (timer.Interval != (secondsInterval * 1000.0)) timer.Interval = secondsInterval * 1000.0; }; timer.Start();
Make the following times, you can see how they drift a little:
06:47:40.020 06:47:45.035 06:47:50.051 ... 06:49:40.215 06:49:45.223 06:49:50.232
So, I think the best approach is to simply reschedule the timer in the tick handler? The following ticks at regular intervals for ~ 15 milliseconds
double secondsUntilRunFirstRun = secondsInterval - (DateTime.Now.TimeOfDay.TotalSeconds % secondsInterval); var timer = new System.Timers.Timer(secondsUntilRunFirstRun * 1000.0); timer.AutoReset = false; timer.Elapsed += (sender, e) => { Console.WriteLine(DateTime.Now.ToString("hh:mm:ss.fff")); timer.Interval = (secondsInterval - (DateTime.Now.TimeOfDay.TotalSeconds % secondsInterval)) * 1000.0; }; timer.Start(); 06:51:45.009 06:51:50.001 ... 06:52:50.011 06:52:55.013 06:53:00.001