Damian Hickey

Mostly software and .NET related. Mostly.

SystemClock

Working on a financial application, operations and calculations are often time sensitive making for tricky unit and acceptance tests. As a result, I never use DateTime.UtcNow ( or DateTime.Now ) anywhere in my code, but instead, I use Ayende's SystemTime abstraction. For this abstraction to really work, all components and systems in your application need to support this. Two core components in my application, RavenDB and EventStore have been recently updated to include this abstraction (thanks for accepting the pull requests!) and a third, Quartz.net, already has it built in. I have an outsanding pull request for NLog to support it, but in the mean time, I am using a custom build.

If you are a library or component developer and you use DateTime.UtcNow anywhere, I strongly recommend that you add support for SystemTime.

Now that I have several SystemTime classes sitting in different libraries and frameworks I'd like one place to set them all. While one can set the exact specific time via SystemTime.UctNow, I usually like the time to move forward as usual. It's not desirable for all of my log messages to have the same timestamp for instance.

SystemClock

This is the one place to get the current SystemTime that moves forward and is also optimized (an idea I pinched from NLog):

using System;
using Beam.Common.Logging;

public class SystemClock
{
    private DateTime? _systemUtcTime;
    private DateTime _start;
    private int _lastTicks = -1;
    private DateTime _lastUtcDateTime = DateTime.MinValue;

    public DateTime UtcNow
    {
        get
        {
            int tickCount = Environment.TickCount;
            if(tickCount == _lastTicks)
            {
                return _lastUtcDateTime;
            }
            if (_systemUtcTime == null)
            {
                _lastUtcDateTime = DateTime.UtcNow;
            }
            else
            {
                var progressed = (DateTime.UtcNow - _start);
                _lastUtcDateTime = _systemUtcTime.Value + progressed;
            }
            _lastTicks = tickCount;
            return _lastUtcDateTime;
        }
    }

    public void SetSystemUtcTime(DateTime systemUtcTime)
    {
        _start = DateTime.UtcNow;
        _systemUtcTime = systemUtcTime;
        _lastTicks = -1;
    }
}

From a container perspective, SystemClock's activation scope is singleton. Now all you do is point your libraries SystemTime classes and your own to this clock at startup:

SystemClock systemClock = container.Resolve<SystemClock>();
SystemTime.UtcNow = systemClock.UtcNow
EventStore.SystemTime.UtcNow = systemClock.UtcNow;
Raven.Abstractions.SystemTime.UtcNow = systemClock.UtcNow;
Quartz.SystemTime = systemClock.UtcNow;
NLog.SystemTime = systemClock.UtcNow;
...

Then set the SytemClock's UtcNow and you're all sync'd up:

systemClock.SetUtcTime(new DateTime(1999, 12, 31, 23, 59, 59));

With this, I hope to be able to do more some interesting things such as accelerating time and jumping into the future that will allow me to similuate long running acceptance tests in shorter timeframes. More on that another time.

Further reading:

Kick the DateTime.Now addiction

SystemTime versus ISystemClock – dependencies revisited

Update: The irony is not lost on me that to effectively test this class, one must abstract over Environment.TickCount :)

blog comments powered by Disqus