/Xtensible.Time.Clock

An easy to use mockable clock.

Primary LanguageC#MIT LicenseMIT

Xtensible.Time

Nuget Codacy Badge Build status

TL;DR

An easy to use mockable clock. Pass WallClock to your services in production/dev code and use MockClock in unit tests; or, if you don't want to pass around a clock, do Clock.Default = new MockClock(); in your unit tests.

Also includes useful rounding extension (RoundUp, RoundDown, and RoundNearest) that can be used on any DateTime or DateTimeOffset, including the time that comes from Clock.

Usage

MockClock and WallClock

Xtensible.Time supports two styles. You can write services that take in a clock object like so:

public class MyService()
{
  private IClock Clock {get;}
  public MyService(IClock clock)
  {
    Clock = clock;
  }
  
  public DateTimeOffset GetTime()
  {
    return Clock.Default.Now;
  }
}

In your app (desktop, asp.net, xamarin, etc), you'd inject a WallClock like so:

var service = new MyService(new WallClock());

and in your unit tests, you'd inject a MockClock like so:

var service = new MyService(new MockClock(new DateTimeOffset(2019, 7, 1, 14, 0, 0, TimeSpan.Zero)));

If you find passing around clock objects cumbersome, you can override the Default clock in the static Clock object in your unit tests' constructor. By default, the Default clock is a WallClock:

  Clock.Default = new MockClock(new DateTimeOffset(2019, 7, 1, 14, 0, 0, TimeSpan.Zero));

Adjusting the MockClock's time

MockClock has an Adjust method that can update the clock. You can use timespans or an adjustment in milliseconds:

  var clock = new MockClock(new DateTimeOffset(2019, 7, 1, 14, 0, 0, TimeSpan.Zero));
  // new time will be Jul 1, 2019 14:01:00
  clock.Adjust(60000);

For your convenience, Clock has an AsMockClock() method so that you can do the following in your unit tests:

  // new time will be Jul 1, 2019 14:01:00
  Clock.AsMockClock().Adjust(60000);

Rounding

New with 1.1, rounding extension methods have been included (RoundUp(), RoundDown(), RoundNearest()). These can be used on any DateTime or DateTimeOffset.

Examples:

var t = DateTimeOffset.Parse("2019-07-01T07:22:16.3000000Z");
// expected: Jul 1 7:22:16
var roundedDownTime = t.RoundDown(TimeSpan.FromSeconds(1));

Since the extension methods work with any DateTime or DateTimeOffset, they of course will work with Clock:

  Clock.Default = new MockClock(new DateTimeOffset(2019, 7, 1, 14, 0, 0, TimeSpan.Zero));
  // expected: Jul 1 7:22:16
  var roundedDownTime = Clock.Default.UtcNow.RoundDown(TimeSpan.FromSeconds(1));