Source: kevintsengtw/dotnet-testing-agent-skills (MIT). Ported into dotnet-agent-harness.
DateTime and Time-Dependent Testing Guide
Applicable Scenarios
This skill guides how to use Microsoft.Bcl.TimeProvider to solve testing problems with time-dependent code. Through time
abstraction, make "current time" controllable, predictable, and reproducible.
Applicable Scenarios
- Business Hours Validation: System determines whether to allow operations based on current time
- Promotion Control: Logic that activates during specific dates or time periods
- Cache Expiration Mechanism: Determines whether data is valid based on time
- Scheduled Task Triggers: Background jobs that run at scheduled times
- Token Expiration: Security mechanisms that are time-sensitive
Required Packages
<!-- Production code -->
<PackageReference Include="Microsoft.Bcl.TimeProvider" Version="9.0.0" />
<!-- Test project -->
<PackageReference Include="Microsoft.Extensions.TimeProvider.Testing" Version="9.0.0" />
```text
---
## Core Principles
### Principle One: Time Abstraction - Replace DateTime with TimeProvider
**Traditional Problem Code**:
```csharp
// ❌ Untestable - uses static time directly
public class OrderService
{
public bool CanPlaceOrder()
{
var now = DateTime.Now;
return now.Hour >= 9 && now.Hour < 17;
}
}
```text
**Testable Refactoring**:
```csharp
// ✅ Testable - receives TimeProvider through dependency injection
public class OrderService
{
private readonly TimeProvider _timeProvider;
public OrderService(TimeProvider timeProvider)
{
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
}
public bool CanPlaceOrder()
{
var now = _timeProvider.GetLocalNow();
return now.Hour >= 9 && now.Hour < 17;
}
}
```text
**Dependency Injection Configuration**:
```csharp
// Program.cs - production environment uses system time
services.AddSingleton(TimeProvider.System);
services.AddScoped<OrderService>();
```text
### Principle Two: FakeTimeProvider Controls Test Time
FakeTimeProvider provides complete time control capabilities:
| Method | Purpose | When to Use |
| ------ | ------- | ----------- |
| `SetUtcNow(DateTimeOffset)` | Set UTC time | When precise UTC time is needed |
| `SetLocalTimeZone(TimeZoneInfo)` | Set local timezone | When testing timezone-related logic |
| `Advance(TimeSpan)` | Fast-forward time | When testing expiration, delay logic |
| `GetUtcNow()` | Get UTC time | Read current simulated time |
| `GetLocalNow()` | Get local time | Read local simulated time |
**Recommended Extension Method**:
```csharp
public static class FakeTimeProviderExtensions
{
/// <summary>
/// Sets FakeTimeProvider local time
/// </summary>
public static void SetLocalNow(this FakeTimeProvider fakeTimeProvider, DateTime localDateTime)
{
fakeTimeProvider.SetLocalTimeZone(TimeZoneInfo.Local);
var utcTime = TimeZoneInfo.ConvertTimeToUtc(localDateTime, TimeZoneInfo.Local);
fakeTimeProvider.SetUtcNow(utcTime);
}
}
```text
### Principle Three: Each Test Uses Independent Time Environment
```csharp
// ✅ Correct: each test creates independent FakeTimeProvider
public class OrderServiceTests
{
[Fact]
public void CanPlaceOrder_DuringBusinessHours_ShouldReturnTrue()
{
// Arrange - independent instance
var fakeTimeProvider = new FakeTimeProvider();
fakeTimeProvider.SetLocalNow(new DateTime(2024, 3, 15, 14, 0, 0));
var sut = new OrderService(fakeTimeProvider);
// Act
var result = sut.CanPlaceOrder();
// Assert
result.Should().BeTrue();
}
}
// ❌ Avoid: multiple tests sharing static instance
public class BadTestClass
{
private static readonly FakeTimeProvider SharedProvider = new(); // will interfere with each other
}
```text
---
## Advanced Time Control Techniques
### Time Freezing
When you need to verify multiple operations occur at the "same time point":
```csharp
[Fact]
public void ProcessBatch_AtFixedTimePoint_ShouldGenerateSameTimestamp()
{
var fakeTimeProvider = new FakeTimeProvider();
var fixedTime = new DateTime(2024, 12, 25, 10, 30, 0);
fakeTimeProvider.SetLocalNow(fixedTime);
var processor = new BatchProcessor(fakeTimeProvider);
var result1 = processor.ProcessItem("Item1");
var result2 = processor.ProcessItem("Item2");
// Time is frozen, both operations have same timestamp
result1.Timestamp.Should().Be(result2.Timestamp);
}
```text
### Time Fast-Forward (Advance)
Test time-sensitive logic like cache expiration, token invalidation:
```csharp
[Fact]
public void Cache_AfterExpirationTime_ShouldClearItems()
{
var fakeTimeProvider = new FakeTimeProvider();
fakeTimeProvider.SetLocalNow(new DateTime(2024, 3, 15, 10, 0, 0));
var cache = new TimedCache(fakeTimeProvider, TimeSpan.FromMinutes(5));
cache.Set("key", "value");
// After 3 minutes - not yet expired
fakeTimeProvider.Advance(TimeSpan.FromMinutes(3));
cache.Get("key").Should().Be("value");
// After another 3 minutes (total 6 minutes) - expired
fakeTimeProvider.Advance(TimeSpan.FromMinutes(3));
cache.Get("key").Should().BeNull();
}
```text
> **Important**: `Advance()` is non-blocking, instantly jumps time without actually waiting.
### Time Rewind
Test historical data processing or replay scenarios:
```csharp
[Fact]
public void HistoricalDataProcessor_GoingBackInTime_ShouldProcessCorrectly()
{
var fakeTimeProvider = new FakeTimeProvider();
var historicalTime = new DateTime(2020, 1, 15, 9, 0, 0);
fakeTimeProvider.SetLocalNow(historicalTime);
var processor = new HistoricalDataProcessor(fakeTimeProvider);
var result = processor.ProcessDataForDate(historicalTime.Date);
result.ProcessedAt.Should().Be(historicalTime);
}
```text
---
## Practical Testing Patterns
### Pattern One: Parameterized Boundary Testing
```csharp
[Theory]
[InlineData(8, false)] // 8 AM - before business hours
[InlineData(9, true)] // 9 AM - business hours start
[InlineData(12, true)] // 12 PM - during business hours
[InlineData(16, true)] // 4 PM - during business hours
[InlineData(17, false)] // 5 PM - business hours end
[InlineData(18, false)] // 6 PM - after business hours
public void CanPlaceOrder_AtDifferentTimes_ShouldReturnCorrectResult(int hour, bool expected)
{
var fakeTimeProvider = new FakeTimeProvider();
fakeTimeProvider.SetLocalNow(new DateTime(2024, 3, 15, hour, 0, 0));
var sut = new OrderService(fakeTimeProvider);
sut.CanPlaceOrder().Should().Be(expected);
}
```text
### Pattern Two: Trading Hours Window Testing
```csharp
[Theory]
[InlineData("09:30:00", true)] // Morning trading hours
[InlineData("12:00:00", false)] // Lunch break
[InlineData("14:30:00", true)] // Afternoon trading hours
[InlineData("15:30:00", false)] // After trading ends
public void IsInTradingHours_AtDifferentTimes_ShouldReturnCorrectResult(string timeStr, bool expected)
{
var fakeTimeProvider = new FakeTimeProvider();
var testTime = DateTime.Today.Add(TimeSpan.Parse(timeStr));
fakeTimeProvider.SetLocalNow(testTime);
var sut = new TradingService(fakeTimeProvider);
sut.IsInTradingHours().Should().Be(expected);
}
```text
### Pattern Three: Schedule Trigger Logic Testing
```csharp
[Theory]
[InlineData("2024-03-15 14:30:00", "2024-03-15 14:00:00", true)] // Time to execute
[InlineData("2024-03-15 13:30:00", "2024-03-15 14:00:00", false)] // Not yet time
public void ShouldExecuteJob_BasedOnTime_ShouldReturnCorrectResult(
string currentTimeStr, string scheduledTimeStr, bool expected)
{
var fakeTimeProvider = new FakeTimeProvider();
fakeTimeProvider.SetLocalNow(DateTime.Parse(currentTimeStr));
var schedule = new JobSchedule { NextExecutionTime = DateTime.Parse(scheduledTimeStr) };
var sut = new ScheduleService(fakeTimeProvider);
sut.ShouldExecuteJob(schedule).Should().Be(expected);
}
```text
---
## AutoFixture Integration
### FakeTimeProviderCustomization
```csharp
public class FakeTimeProviderCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Register(() => new FakeTimeProvider());
}
}
```text
### AutoDataWithCustomization Attribute
```csharp
public class AutoDataWithCustomizationAttribute : AutoDataAttribute
{
public AutoDataWithCustomizationAttribute() : base(CreateFixture)
{
}
private static IFixture CreateFixture()
{
return new Fixture()
.Customize(new AutoNSubstituteCustomization())
.Customize(new FakeTimeProviderCustomization());
}
}
```text
### Using Matching.DirectBaseType
```csharp
[Theory]
[AutoDataWithCustomization]
public void GetTimeBasedDiscount_OnFriday_ShouldReturnTenPercentDiscount(
[Frozen(Matching.DirectBaseType)] FakeTimeProvider fakeTimeProvider,
OrderService sut)
{
// Matching.DirectBaseType tells AutoFixture:
// When TimeProvider (base type) is needed, use FakeTimeProvider (derived type)
var fridayTime = new DateTime(2024, 3, 15, 14, 0, 0); // Friday
fakeTimeProvider.SetLocalNow(fridayTime);
sut.GetTimeBasedDiscount().Should().Be("Happy Friday: 10% Discount");
}
```text
> **Key**: Must use `[Frozen(Matching.DirectBaseType)]`, otherwise AutoFixture cannot correctly inject FakeTimeProvider into constructors requiring TimeProvider.
---
## Best Practices Checklist
### ✅ Code Design
- [ ] All time-dependent classes receive `TimeProvider` through constructor
- [ ] Use `_timeProvider.GetLocalNow()` instead of `DateTime.Now`
- [ ] Use `_timeProvider.GetUtcNow()` instead of `DateTime.UtcNow`
- [ ] DI container registers `TimeProvider.System` as production implementation
### ✅ Test Design
- [ ] Each test method uses independent `FakeTimeProvider` instance
- [ ] Use `SetLocalNow()` extension method to simplify time setup
- [ ] Use `Advance()` to test time-sensitive logic (cache, expiration, delay)
- [ ] Tests cover boundary conditions (start time, end time, critical points)
### ✅ Advanced Considerations
- [ ] FakeTimeProvider is thread-safe, can be used for parallel tests
- [ ] Use `IDisposable` pattern to properly dispose FakeTimeProvider
- [ ] Use `SetLocalTimeZone()` to explicitly set timezone for timezone tests
---
## Reference Resources
### Original Articles
This skill content is distilled from the "Old School Software Engineer's Testing Practice - 30 Day Challenge" article series:
- **Day 16 - Testing Dates and Times: Replace DateTime with Microsoft.Bcl.TimeProvider**
- Article: https://ithelp.ithome.com.tw/articles/10375821
- Sample code: https://github.com/kevintsengtw/30Days_in_Testing_Samples/tree/main/day16
### Official Documentation
- [TimeProvider API](https://learn.microsoft.com/dotnet/api/system.timeprovider)
- [Microsoft.Bcl.TimeProvider NuGet](https://www.nuget.org/packages/Microsoft.Bcl.TimeProvider/)
- [Microsoft.Extensions.TimeProvider.Testing NuGet](https://www.nuget.org/packages/Microsoft.Extensions.TimeProvider.Testing/)
### Related Skills
- `autofixture-basics` - AutoFixture automatic test data generation
- `nsubstitute-mocking` - Test doubles and mocking
- `autodata-xunit-integration` - xUnit and AutoFixture AutoData integration
1---2name: dotnet-testing-datetime-testing-timeprovider3category: testing4subcategory: specialized5description: |6 Specialized skill for testing time-dependent logic using TimeProvider. Use when testing DateTime, controlling time flow, handling timezone conversions, and testing expiration logic. Covers TimeProvider abstraction, FakeTimeProvider time control, time freezing and fast-forwarding.7 Keywords: datetime, time testing, TimeProvider, FakeTimeProvider, DateTime.Now, time-dependent, cache expiration, token expiration, Microsoft.Bcl.TimeProvider, GetUtcNow, SetUtcNow, Advance, time freeze, time freezing, time fast-forward8targets: ['*']9license: MIT10metadata:11 author: Kevin Tseng12 version: '1.0.0'13 tags: '.NET, testing, TimeProvider, DateTime, time testing'14 related_skills: 'unit-test-fundamentals, nsubstitute-mocking, filesystem-testing-abstractions'15claudecode: {}16opencode: {}17codexcli:18 short-description: '.NET skill guidance for dotnet-testing-datetime-testing-timeprovider'19copilot: {}20geminicli: {}21antigravity: {}22---2324Source: kevintsengtw/dotnet-testing-agent-skills (MIT). Ported into dotnet-agent-harness.2526# DateTime and Time-Dependent Testing Guide2728## Applicable Scenarios2930This skill guides how to use Microsoft.Bcl.TimeProvider to solve testing problems with time-dependent code. Through time31abstraction, make "current time" controllable, predictable, and reproducible.3233### Applicable Scenarios3435- **Business Hours Validation**: System determines whether to allow operations based on current time36- **Promotion Control**: Logic that activates during specific dates or time periods37- **Cache Expiration Mechanism**: Determines whether data is valid based on time38- **Scheduled Task Triggers**: Background jobs that run at scheduled times39- **Token Expiration**: Security mechanisms that are time-sensitive4041### Required Packages4243````xml44<!-- Production code -->45<PackageReference Include="Microsoft.Bcl.TimeProvider" Version="9.0.0" />4647<!-- Test project -->48<PackageReference Include="Microsoft.Extensions.TimeProvider.Testing" Version="9.0.0" />49```text5051---5253## Core Principles5455### Principle One: Time Abstraction - Replace DateTime with TimeProvider5657**Traditional Problem Code**:5859```csharp60// ❌ Untestable - uses static time directly61public class OrderService62{63 public bool CanPlaceOrder()64 {65 var now = DateTime.Now;66 return now.Hour >= 9 && now.Hour < 17;67 }68}69```text7071**Testable Refactoring**:7273```csharp74// ✅ Testable - receives TimeProvider through dependency injection75public class OrderService76{77 private readonly TimeProvider _timeProvider;7879 public OrderService(TimeProvider timeProvider)80 {81 _timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));82 }8384 public bool CanPlaceOrder()85 {86 var now = _timeProvider.GetLocalNow();87 return now.Hour >= 9 && now.Hour < 17;88 }89}90```text9192**Dependency Injection Configuration**:9394```csharp95// Program.cs - production environment uses system time96services.AddSingleton(TimeProvider.System);97services.AddScoped<OrderService>();98```text99100### Principle Two: FakeTimeProvider Controls Test Time101102FakeTimeProvider provides complete time control capabilities:103104| Method | Purpose | When to Use |105| ------ | ------- | ----------- |106| `SetUtcNow(DateTimeOffset)` | Set UTC time | When precise UTC time is needed |107| `SetLocalTimeZone(TimeZoneInfo)` | Set local timezone | When testing timezone-related logic |108| `Advance(TimeSpan)` | Fast-forward time | When testing expiration, delay logic |109| `GetUtcNow()` | Get UTC time | Read current simulated time |110| `GetLocalNow()` | Get local time | Read local simulated time |111112**Recommended Extension Method**:113114```csharp115public static class FakeTimeProviderExtensions116{117 /// <summary>118 /// Sets FakeTimeProvider local time119 /// </summary>120 public static void SetLocalNow(this FakeTimeProvider fakeTimeProvider, DateTime localDateTime)121 {122 fakeTimeProvider.SetLocalTimeZone(TimeZoneInfo.Local);123 var utcTime = TimeZoneInfo.ConvertTimeToUtc(localDateTime, TimeZoneInfo.Local);124 fakeTimeProvider.SetUtcNow(utcTime);125 }126}127```text128129### Principle Three: Each Test Uses Independent Time Environment130131```csharp132// ✅ Correct: each test creates independent FakeTimeProvider133public class OrderServiceTests134{135 [Fact]136 public void CanPlaceOrder_DuringBusinessHours_ShouldReturnTrue()137 {138 // Arrange - independent instance139 var fakeTimeProvider = new FakeTimeProvider();140 fakeTimeProvider.SetLocalNow(new DateTime(2024, 3, 15, 14, 0, 0));141 var sut = new OrderService(fakeTimeProvider);142143 // Act144 var result = sut.CanPlaceOrder();145146 // Assert147 result.Should().BeTrue();148 }149}150151// ❌ Avoid: multiple tests sharing static instance152public class BadTestClass153{154 private static readonly FakeTimeProvider SharedProvider = new(); // will interfere with each other155}156```text157158---159160## Advanced Time Control Techniques161162### Time Freezing163164When you need to verify multiple operations occur at the "same time point":165166```csharp167[Fact]168public void ProcessBatch_AtFixedTimePoint_ShouldGenerateSameTimestamp()169{170 var fakeTimeProvider = new FakeTimeProvider();171 var fixedTime = new DateTime(2024, 12, 25, 10, 30, 0);172 fakeTimeProvider.SetLocalNow(fixedTime);173174 var processor = new BatchProcessor(fakeTimeProvider);175176 var result1 = processor.ProcessItem("Item1");177 var result2 = processor.ProcessItem("Item2");178179 // Time is frozen, both operations have same timestamp180 result1.Timestamp.Should().Be(result2.Timestamp);181}182```text183184### Time Fast-Forward (Advance)185186Test time-sensitive logic like cache expiration, token invalidation:187188```csharp189[Fact]190public void Cache_AfterExpirationTime_ShouldClearItems()191{192 var fakeTimeProvider = new FakeTimeProvider();193 fakeTimeProvider.SetLocalNow(new DateTime(2024, 3, 15, 10, 0, 0));194195 var cache = new TimedCache(fakeTimeProvider, TimeSpan.FromMinutes(5));196 cache.Set("key", "value");197198 // After 3 minutes - not yet expired199 fakeTimeProvider.Advance(TimeSpan.FromMinutes(3));200 cache.Get("key").Should().Be("value");201202 // After another 3 minutes (total 6 minutes) - expired203 fakeTimeProvider.Advance(TimeSpan.FromMinutes(3));204 cache.Get("key").Should().BeNull();205}206```text207208> **Important**: `Advance()` is non-blocking, instantly jumps time without actually waiting.209210### Time Rewind211212Test historical data processing or replay scenarios:213214```csharp215[Fact]216public void HistoricalDataProcessor_GoingBackInTime_ShouldProcessCorrectly()217{218 var fakeTimeProvider = new FakeTimeProvider();219 var historicalTime = new DateTime(2020, 1, 15, 9, 0, 0);220 fakeTimeProvider.SetLocalNow(historicalTime);221222 var processor = new HistoricalDataProcessor(fakeTimeProvider);223 var result = processor.ProcessDataForDate(historicalTime.Date);224225 result.ProcessedAt.Should().Be(historicalTime);226}227```text228229---230231## Practical Testing Patterns232233### Pattern One: Parameterized Boundary Testing234235```csharp236[Theory]237[InlineData(8, false)] // 8 AM - before business hours238[InlineData(9, true)] // 9 AM - business hours start239[InlineData(12, true)] // 12 PM - during business hours240[InlineData(16, true)] // 4 PM - during business hours241[InlineData(17, false)] // 5 PM - business hours end242[InlineData(18, false)] // 6 PM - after business hours243public void CanPlaceOrder_AtDifferentTimes_ShouldReturnCorrectResult(int hour, bool expected)244{245 var fakeTimeProvider = new FakeTimeProvider();246 fakeTimeProvider.SetLocalNow(new DateTime(2024, 3, 15, hour, 0, 0));247248 var sut = new OrderService(fakeTimeProvider);249250 sut.CanPlaceOrder().Should().Be(expected);251}252```text253254### Pattern Two: Trading Hours Window Testing255256```csharp257[Theory]258[InlineData("09:30:00", true)] // Morning trading hours259[InlineData("12:00:00", false)] // Lunch break260[InlineData("14:30:00", true)] // Afternoon trading hours261[InlineData("15:30:00", false)] // After trading ends262public void IsInTradingHours_AtDifferentTimes_ShouldReturnCorrectResult(string timeStr, bool expected)263{264 var fakeTimeProvider = new FakeTimeProvider();265 var testTime = DateTime.Today.Add(TimeSpan.Parse(timeStr));266 fakeTimeProvider.SetLocalNow(testTime);267268 var sut = new TradingService(fakeTimeProvider);269270 sut.IsInTradingHours().Should().Be(expected);271}272```text273274### Pattern Three: Schedule Trigger Logic Testing275276```csharp277[Theory]278[InlineData("2024-03-15 14:30:00", "2024-03-15 14:00:00", true)] // Time to execute279[InlineData("2024-03-15 13:30:00", "2024-03-15 14:00:00", false)] // Not yet time280public void ShouldExecuteJob_BasedOnTime_ShouldReturnCorrectResult(281 string currentTimeStr, string scheduledTimeStr, bool expected)282{283 var fakeTimeProvider = new FakeTimeProvider();284 fakeTimeProvider.SetLocalNow(DateTime.Parse(currentTimeStr));285286 var schedule = new JobSchedule { NextExecutionTime = DateTime.Parse(scheduledTimeStr) };287 var sut = new ScheduleService(fakeTimeProvider);288289 sut.ShouldExecuteJob(schedule).Should().Be(expected);290}291```text292293---294295## AutoFixture Integration296297### FakeTimeProviderCustomization298299```csharp300public class FakeTimeProviderCustomization : ICustomization301{302 public void Customize(IFixture fixture)303 {304 fixture.Register(() => new FakeTimeProvider());305 }306}307```text308309### AutoDataWithCustomization Attribute310311```csharp312public class AutoDataWithCustomizationAttribute : AutoDataAttribute313{314 public AutoDataWithCustomizationAttribute() : base(CreateFixture)315 {316 }317318 private static IFixture CreateFixture()319 {320 return new Fixture()321 .Customize(new AutoNSubstituteCustomization())322 .Customize(new FakeTimeProviderCustomization());323 }324}325```text326327### Using Matching.DirectBaseType328329```csharp330[Theory]331[AutoDataWithCustomization]332public void GetTimeBasedDiscount_OnFriday_ShouldReturnTenPercentDiscount(333 [Frozen(Matching.DirectBaseType)] FakeTimeProvider fakeTimeProvider,334 OrderService sut)335{336 // Matching.DirectBaseType tells AutoFixture:337 // When TimeProvider (base type) is needed, use FakeTimeProvider (derived type)338339 var fridayTime = new DateTime(2024, 3, 15, 14, 0, 0); // Friday340 fakeTimeProvider.SetLocalNow(fridayTime);341342 sut.GetTimeBasedDiscount().Should().Be("Happy Friday: 10% Discount");343}344```text345346> **Key**: Must use `[Frozen(Matching.DirectBaseType)]`, otherwise AutoFixture cannot correctly inject FakeTimeProvider into constructors requiring TimeProvider.347348---349350## Best Practices Checklist351352### ✅ Code Design353354- [ ] All time-dependent classes receive `TimeProvider` through constructor355- [ ] Use `_timeProvider.GetLocalNow()` instead of `DateTime.Now`356- [ ] Use `_timeProvider.GetUtcNow()` instead of `DateTime.UtcNow`357- [ ] DI container registers `TimeProvider.System` as production implementation358359### ✅ Test Design360361- [ ] Each test method uses independent `FakeTimeProvider` instance362- [ ] Use `SetLocalNow()` extension method to simplify time setup363- [ ] Use `Advance()` to test time-sensitive logic (cache, expiration, delay)364- [ ] Tests cover boundary conditions (start time, end time, critical points)365366### ✅ Advanced Considerations367368- [ ] FakeTimeProvider is thread-safe, can be used for parallel tests369- [ ] Use `IDisposable` pattern to properly dispose FakeTimeProvider370- [ ] Use `SetLocalTimeZone()` to explicitly set timezone for timezone tests371372---373374## Reference Resources375376### Original Articles377378This skill content is distilled from the "Old School Software Engineer's Testing Practice - 30 Day Challenge" article series:379380- **Day 16 - Testing Dates and Times: Replace DateTime with Microsoft.Bcl.TimeProvider**381 - Article: https://ithelp.ithome.com.tw/articles/10375821382 - Sample code: https://github.com/kevintsengtw/30Days_in_Testing_Samples/tree/main/day16383384### Official Documentation385386- [TimeProvider API](https://learn.microsoft.com/dotnet/api/system.timeprovider)387- [Microsoft.Bcl.TimeProvider NuGet](https://www.nuget.org/packages/Microsoft.Bcl.TimeProvider/)388- [Microsoft.Extensions.TimeProvider.Testing NuGet](https://www.nuget.org/packages/Microsoft.Extensions.TimeProvider.Testing/)389390### Related Skills391392- `autofixture-basics` - AutoFixture automatic test data generation393- `nsubstitute-mocking` - Test doubles and mocking394- `autodata-xunit-integration` - xUnit and AutoFixture AutoData integration395````