Getting the Time the Traditional Way

We usually use DateTime.Now and DateTimeOffset.Now to get the current time, but code written this way is hard to test: the time depends on the system clock, which is difficult to control and reproduce — the only way to change it is to change the system time.

var now1 = DateTime.Now;
var nowUtc1 = DateTime.UtcNow;

var now2 = DateTimeOffset.Now;
var nowUtc2 = DateTimeOffset.UtcNow;

The New TimeProvider Type

To solve the problems above, .NET 8 introduced the TimeProvider type, which can be used to get the time and makes it easy to control the time in tests.

var now = TimeProvider.System.GetLocalNow();
var nowUtc = TimeProvider.System.GetUtcNow();

Overriding TimeProvider to Simulate the Time

We can override the TimeProvider type to simulate the time, which makes testing easier.

public class MyTimeProvider : TimeProvider
{
    public override DateTimeOffset GetUtcNow() => DateTimeOffset.UtcNow;
}

Dependency Injection of a TimeProvider Implementation

The default implementation, TimeProvider.System, uses the system time.

builder.Services.AddSingleton(TimeProvider.System);

Inject MyTimeProvider to use a custom time source.

builder.Services.AddSingleton<TimeProvider, MyTimeProvider>();

Code That Uses TimeProvider

public class MyService(TimeProvider timeProvider)
{

    public void DoSomething()
    {
        var now = timeProvider.GetLocalNow();
        var nowUtc = timeProvider.GetUtcNow();
    }
}

A demonstration of using TimeProvider in the Zero framework.

Unit Testing Time-Dependent Code

Microsoft.Extensions.TimeProvider.Testing