Don’t depend on what you don’t control

Let’s say that you have a particular task in your program that is executed as the result of an external signal, but you don’t want the task to execute more often than once per second. This is different from a periodic task that you want to execute exactly (well, as close as possible to) once per second. This one, you don’t want to execute unless the external signal is received. Your first cut of the function might look like this:

// The last time a signal was processed
// Initialized to DateTime.MinValue so the first signal received
// will not be lost
DateTime LastSignalTime = DateTime.MinValue;

// Lock object to prevent re-entrancy
object SignalLock = new object();

// Minimum time between invocations
static readonly TimeSpan MinimumTime = TimeSpan.FromSeconds(1.0);

void SignalReceived(SomeObject dataObject)
{
    if (!Monitor.TryEnter(SignalLock))
    {
        // Currently processing. Exit.
        return;
    }
    try
    {
        if ((DateTime.Now - LastSignalTime) < MinimumTime)
        {
            // Already processed within the last second.
            return;
        }
        // ...
        // do processing here
        // ...
        // And then save the last signal time.
        LastSignalTime = DateTime.Now;
    }
    finally
    {
        Monitor.Exit(SignalLock);
    }
}

That looks reasonable. I covered all the bases, protecting the processing with a Monitor to prevent re-entrant use, and checking the time to ensure that I don’t process a signal more often than once per second.

There’s only one problem. The program assumes that DateTime.Now will increase by one second for every wall clock second. This code does not take into account Daylight Saving Time changes, changes caused by updating with an NTP service, or the user resetting the clock. If one of those events occurs, things fall apart.

For example, when we set the clocks ahead in the spring, this code could process twice in a very short period of time. Imagine that you processed a signal at 01:59:59.800. 200 milliseconds later, the time will be 03:00:00.000. Your code would then process another signal, even though less than one second has passed.

It’s worse when the time is set back in the fall. Say you process a signal at 01:59:59.100. 900 milliseconds after that, the time is changed to 01:00:00.000, and it will be another hour before the code does any more processing.

You can avoid those DST errors by using DateTime.UtcNow in place of DateTime.Now, but that won’t solve the problem that occurs with NTP or user-initiated time changes.

The problem here is that your code is depending on the system clock, which the program doesn’t control. The clock can change at any time, and if you depend on it for critical processing you’re going to be sorry.

You can remove that dependency by using a Stopwatch, which measures elapsed time independent of the system clock. In the code below, I initialize a Stopwatch when the program starts. I’ve modified the logic slightly to save a NextSignalTime, which represents the time when another signal can be processed.

// The amount of time elapsed since the program started
static readonly Stopwatch ProgramTimer = Stopwatch.StartNew();

// The next time a signal can be processed.
TimeSpan NextSignalTime = TimeSpan.Zero;

// Lock object to prevent re-entrancy
object SignalLock = new object();

// Minimum time between invocations
static readonly TimeSpan MinimumTime = TimeSpan.FromSeconds(1.0);

void SignalReceived(SomeObject dataObject)
{
    if (!Monitor.TryEnter(SignalLock))
    {
        // Currently processing. Exit.
        return;
    }
    try
    {
        if (ProgramTimer.Elapsed < NextSignalTime)
        {
            // Already processed within the last second.
            return;
        }
        // ...
        // do processing here
        // ...
        // And then save the next signal time.
        NextSignalTime = ProgramTimer.Elapsed + MinimumTime;
    }
    finally
    {
        Monitor.Exit(SignalLock);
    }
}

This code can’t be affected by system time changes because it doesn’t depend on the system time.

You don’t control the system clock. Don’t depend on it for measuring elapsed time. It’s fine to depend on it to tell you what time of day it is. If the user changes his clock, that’s his problem. But you can’t be at the mercy of the system clock if you want accurate elapsed time measurements.