If you write unit tests you know that using DateTime.Now directly is not a good idea 🤔. You probably have something like IDateTimeProvider or something similar in your project.

🌠 Microsoft after 21 years came up with their own solution. Starting with .NET 8, we have the TimeProvider abstract class. We can have this one injected where we need it and use it. In the project we then configure the usage on TimeProvider.System.

🧪 In tests, we can use the FakeTimeProvider class from the Microsoft.Extensions.TimeProvider.Testing library. This class will allow us to travel in time to test our scenarios.

Use the new TimeProvider instead of the original DateTime.Now or DateTime.UtcNow.

// 👇 Inject time provider
public class Basket(TimeProvider timeProvider, LoyaltyLevel loyaltyLevel)
{
    private readonly DateTimeOffset _expireAt = timeProvider.GetUtcNow()  // 👈 Use time provider
        .AddDays(loyaltyLevel == LoyaltyLevel.Standard ? 1 : 7);

    public bool IsExpired => timeProvider.GetUtcNow() > _expireAt; // 👈 Use time provider
}

TimeProvider.System is available for standard use cases. Inject it where you need it.

// 👇 Use System time provider in your code
var basket = new Basket(TimeProvider.System, LoyaltyLevel.Gold);

In tests use FakeTimeProvider and time travel as needed using Advance method.

[Fact]
public void GoldendUserShouldBeExpiredAfter7Days()
{
    // 👇 Use fake time provider in your test
    var fakeTime = new FakeTimeProvider(DateTimeOffset.UtcNow);
    var basket = new Basket(fakeTime, LoyaltyLevel.Gold);

    // 👇 Travel in time
    fakeTime.Advance(TimeSpan.FromDays(8));

    Assert.True(basket.IsExpired);
}