Search code examples
c#timersystemsynchronizationclock

run every min in sync with system clock (not working on Windows Server 2003)


I am trying to get a timer run every minute in sync with the system clock (00:01:00, 00:02:00, 00:03:00, etc). This is my code.

private System.Timers.Timer timer;

public frmMain()
{
    timer = new System.Timers.Timer();
    timer.AutoReset = false;
    timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
    timer.Interval = GetInterval();
    timer.Start();
}

private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{

        System.Diagnostics.Trace.WriteLine(DateTime.Now.ToString("hh:mm:ss tt"));
            timer.Interval = GetInterval();
            timer.Start();

}
private double GetInterval()
{
    DateTime now = DateTime.Now;
    return ((60 - now.Second) * 1000 - now.Millisecond);
}

It runs perfectly on my home PC.

12:12:00 AM
12:13:00 AM
12:14:00 AM
12:15:00 AM
12:16:00 AM
12:17:00 AM
12:18:00 AM
12:19:00 AM
12:20:00 AM
12:21:00 AM

However I'm getting weird results on my VPS (windows server 2003).

12:11:59 AM
12:12:59 AM
12:13:00 AM
12:13:59 AM
12:14:00 AM
12:14:59 AM
12:15:00 AM
12:15:59 AM
12:16:00 AM
12:16:59 AM
12:17:00 AM
12:17:59 AM
12:18:00 AM
12:18:59 AM
12:19:00 AM
12:19:59 AM
12:20:00 AM
12:20:59 AM
12:21:00 AM

Is it because System.Timers.Timer does not work well on windows server 2003? Or is it an issue with my VPS?


Solution

  • Instead of using DateTime.Now and pulling the individual parts, just use the Ticks. Get the ticks when you start, then calculate what the ticks should be for the next timer tick. Once that timer tick occurs use the last value to calculate what the next value should be.

    Example:

        private const long MILLISECOND_IN_MINUTE = 60 * 1000;
        private const long TICKS_IN_MILLISECOND = 10000;
        private const long TICKS_IN_MINUTE = MILLISECOND_IN_MINUTE * TICKS_IN_MILLISECOND;
    
        private System.Timers.Timer timer;
        private long nextIntervalTick;
    
        public void frmMain()
        {
            timer = new System.Timers.Timer();
            timer.AutoReset = false;
            timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
            timer.Interval = GetInitialInterval();
            timer.Start();
        }
    
        private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
    
            System.Diagnostics.Trace.WriteLine(DateTime.Now.ToString("hh:mm:ss tt"));
            timer.Interval = GetInterval();
            timer.Start();
    
        }
        private double GetInitialInterval()
        {
            DateTime now = DateTime.Now;
            double timeToNextMin = ((60 - now.Second) * 1000 - now.Millisecond) + 15;
            nextIntervalTick = now.Ticks + ((long)timeToNextMin * TICKS_IN_MILLISECOND);
    
            return timeToNextMin;
        }
        private double GetInterval()
        {
            nextIntervalTick += TICKS_IN_MINUTE;
            return TicksToMs(nextIntervalTick - DateTime.Now.Ticks);
        }
        private double TicksToMs(long ticks)
        {
            return (double)(ticks / TICKS_IN_MILLISECOND);
        }
    

    You could probably do this using Seconds and Milliseconds like you were. The trick is to have one starting point to calculate off of (rather then determining how many seconds to the next minute). If there are additional concerns not mentioned in the original problem, like the code in timer_Elapsed might take longer then a minute to run, then you will need to add code to handle this.

    Please leave a comment if you need additional help. Otherwise please select a correct answer.