You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+11-11Lines changed: 11 additions & 11 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,19 +4,19 @@ Extensions for [`System.TimeProvider`](https://learn.microsoft.com/en-us/dotnet/
4
4
5
5
An instance of `TimeProvider` for production use is available on the `TimeProvider.System` property, and `ManualTimeProvider` can be used during testing.
6
6
7
-
During testing, you can move time forward by calling `Advance(TimeSpan)` or `SetUtcNow(DateTimeOffset)` on `ManualTimeProvider`. This allows you to write tests that run fast and predictable, even if the system under test pauses execution for multiple minutes using e.g. `TimeProvider.Delay(TimeSpan)`, the replacement for `Task.Delay(TimeSpan)`.
7
+
During testing, you can move time forward by calling `Advance(TimeSpan)` or `SetUtcNow(DateTimeOffset)` on `ManualTimeProvider`. This allows you to write tests that run fast and predictably, even if the system under test pauses execution for multiple minutes using e.g. `TimeProvider.Delay(TimeSpan)`, the replacement for `Task.Delay(TimeSpan)`.
8
8
9
9
## Difference between `ManualTimeProvider` and `FakeTimeProvider`
10
10
11
-
The .NET team has published a similar testspecific time provider, the [`Microsoft.Extensions.Time.Testing.FakeTimeProvider`](https://www.nuget.org/packages/Microsoft.Extensions.Time.Testing.FakeTimeProvider/).
11
+
The .NET team has published a similar test-specific time provider, the [`Microsoft.Extensions.Time.Testing.FakeTimeProvider`](https://www.nuget.org/packages/Microsoft.Extensions.Time.Testing.FakeTimeProvider/).
12
12
13
-
The public API of both `FakeTimeProvider` and `ManualTimeProvider` are identical, but there are some differences in when time is set before timer callbacks. Lets illustrate this with an example:
13
+
The public API of both `FakeTimeProvider` and `ManualTimeProvider` are identical, but there are some differences in when time is set before timer callbacks. Let's illustrate this with an example:
14
14
15
-
For example, if we create a`ITimer` with a *due time* and *period* set to **1 second**, the `DateTimeOffset` returned from `GetUtcNow()` during the timer callback may be different depending on the amount passed to `Advance()` (or `SetUtcNow()`).
15
+
For example, if we create an`ITimer` with a *due time* and *period* set to **1 second**, the `DateTimeOffset` returned from `GetUtcNow()` during the timer callback may be different depending on the amount passed to `Advance()` (or `SetUtcNow()`).
16
16
17
-
If we call `Advance(TimeSpan.FromSeconds(1))` three times, effectively moving time forward by three seconds, the timer callback will be invoked once at time 00:01`, `00:02`, and `00:03`, as illustarted in the drawing below. Both `FakeTimeProvider` and `ManualTimeProvider` behaves like this:
17
+
If we call `Advance(TimeSpan.FromSeconds(1))` three times, effectively moving time forward by three seconds, the timer callback will be invoked once at times `00:01`, `00:02`, and `00:03`, as illustrated in the drawing below. Both `FakeTimeProvider` and `ManualTimeProvider` behaves like this:
18
18
19
-

19
+

20
20
21
21
However, if we instead call `Advance(TimeSpan.FromSeconds(3))` once, the two implementations behave differently. `ManualTimeProvider` will invoke the timer callback at the same time as if we had called `Advance(TimeSpan.FromSeconds(1))` three times, as illustrated in the drawing below:
22
22
@@ -26,13 +26,13 @@ However, `FakeTimeProvider` will invoke the timer callback at time `00:03` three
26
26
27
27

28
28
29
-
Technically, both implementations are correct. The `ITimer` abstractions only promises to invoke the callback timer *on or after the duetime/period has elapsed*, never before.
29
+
Technically, both implementations are correct since the `ITimer` abstractions only promise to invoke the callback timer *on or after the due time/period has elapsed*, never before.
30
30
31
-
However, I strongly prefer the `ManualTimeProvider` approach since it behaves consistently independent of how time is moved forward. It seeems much more in the spirit of how a deterministic time provider should behave and avoids users being surprised when writing tests. I imaging users may get stuck for a while trying to debug why time is not set as expected due the suttle difference in behavior of `FakeTimeProvider`.
31
+
However, I strongly prefer the `ManualTimeProvider` approach since it behaves consistently independent of how time is moved forward. It seems much more in the spirit of how a deterministic time provider should behave and avoids users being surprised when writing tests. I imagine users may get stuck for a while trying to debug why the time reported by `GetUtcNow()`is not set as expected due to the subtle difference in behavior of `FakeTimeProvider`.
32
32
33
33
## Known limitations:
34
34
35
-
- If running on .NET versions earlier than .NET 8.0, there is a constraint when invoking `CancellationTokenSource.CancelAfter(TimeSpan)` on the `CancellationTokenSource` object returned by `CreateCancellationTokenSource(TimeSpan delay)`. This action will not terminate the initial timer indicated by the `delay` argument initially passed the `CreateCancellationTokenSource` method. However, this restriction does not apply on .NET 8.0 and later versions.
35
+
- If running on .NET versions earlier than .NET 8.0, there is a constraint when invoking `CancellationTokenSource.CancelAfter(TimeSpan)` on the `CancellationTokenSource` object returned by `CreateCancellationTokenSource(TimeSpan delay)`. This action will not terminate the initial timer indicated by the `delay` argument initially passed the `CreateCancellationTokenSource` method. However, this restriction does not apply to .NET 8.0 and later versions.
36
36
- To enable controlling `PeriodicTimer` via `TimeProvider` in versions of .NET earlier than .NET 8.0, the `TimeProvider.CreatePeriodicTimer` returns a `PeriodicTimerWrapper` object instead of a `PeriodicTimer` object. The `PeriodicTimerWrapper` type is just a lightweight wrapper around the original `System.Threading.PeriodicTimer` and will behave identically to it.
37
37
38
38
## Installation
@@ -42,7 +42,7 @@ Get the latest release from https://www.nuget.org/packages/TimeProviderExtension
42
42
## Set up in production
43
43
44
44
To use in production, pass in `TimeProvider.System` to the types that depend on `TimeProvider`.
45
-
This can be done directly or via an IoC Container, e.g. .NETs built-in `IServiceCollection` like so:
45
+
This can be done directly or via an IoC Container, e.g., .NETs built-in `IServiceCollection` like so:
46
46
47
47
```c#
48
48
services.AddSingleton(TimeProvider.System);
@@ -84,7 +84,7 @@ As an example, let us test the "Stuff Service" below that performs specific task
84
84
1-second delay. We have two versions, one that uses the standard types in .NET, and one that uses the `TimeProvider`.
85
85
86
86
```c#
87
-
// Version of stuff service that uses the builtin DateTimeOffset, PeriodicTimer, and Task.Delay
87
+
// Version of stuff service that uses the built-in DateTimeOffset, PeriodicTimer, and Task.Delay
0 commit comments