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.