Skip to content

Commit 3fd6da6

Browse files
committed
feat: Enable auto advance feature for GetTimestamp() and GetElapsedTime(long)
1 parent 7f265fc commit 3fd6da6

File tree

7 files changed

+127
-9
lines changed

7 files changed

+127
-9
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1919

2020
- Replace `AutoAdvanceAmount` property with the `AutoAdvanceBehavior` property on `ManualTimeProvider`, and introduced the `AutoAdvanceBehavior` type.
2121

22+
- Enable auto advance feature for `GetTimestamp()` and `GetElapsedTime(long)`.
23+
2224
## [1.0.0-rc.1]
2325

2426
- Updated Microsoft.Bcl.TimeProvider package dependency to rc.1 version.

docs/TimeProviderExtensions.AutoAdvanceBehavior.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ Implements [System.IEquatable<](https://docs.microsoft.com/en-us/dotnet/api/S
1919

2020
## AutoAdvanceBehavior.ClockAdvanceAmount Property
2121

22-
Gets or sets the amount of time by which time advances whenever the clock is read via [System.TimeProvider.GetUtcNow](https://docs.microsoft.com/en-us/dotnet/api/System.TimeProvider.GetUtcNow 'System.TimeProvider.GetUtcNow') or [System.TimeProvider.GetLocalNow](https://docs.microsoft.com/en-us/dotnet/api/System.TimeProvider.GetLocalNow 'System.TimeProvider.GetLocalNow').
22+
Gets or sets the amount of time by which time advances whenever the clock is read via [System.TimeProvider.GetUtcNow](https://docs.microsoft.com/en-us/dotnet/api/System.TimeProvider.GetUtcNow 'System.TimeProvider.GetUtcNow')
23+
or [System.TimeProvider.GetLocalNow](https://docs.microsoft.com/en-us/dotnet/api/System.TimeProvider.GetLocalNow 'System.TimeProvider.GetLocalNow').
2324
2425
```csharp
2526
public System.TimeSpan ClockAdvanceAmount { get; set; }
@@ -33,5 +34,27 @@ public System.TimeSpan ClockAdvanceAmount { get; set; }
3334
[System.ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/System.ArgumentOutOfRangeException 'System.ArgumentOutOfRangeException')
3435
Thrown when set to a value than [System.TimeSpan.Zero](https://docs.microsoft.com/en-us/dotnet/api/System.TimeSpan.Zero 'System.TimeSpan.Zero').
3536
37+
### Remarks
38+
Set to [System.TimeSpan.Zero](https://docs.microsoft.com/en-us/dotnet/api/System.TimeSpan.Zero 'System.TimeSpan.Zero') to disable auto advance. The default value is [System.TimeSpan.Zero](https://docs.microsoft.com/en-us/dotnet/api/System.TimeSpan.Zero 'System.TimeSpan.Zero').
39+
40+
<a name='TimeProviderExtensions.AutoAdvanceBehavior.TimestampAdvanceAmount'></a>
41+
42+
## AutoAdvanceBehavior.TimestampAdvanceAmount Property
43+
44+
Gets or sets the amount of time by which time advances whenever the a timestamp is read via [System.TimeProvider.GetTimestamp](https://docs.microsoft.com/en-us/dotnet/api/System.TimeProvider.GetTimestamp 'System.TimeProvider.GetTimestamp')
45+
or an elapsed time is calculated with [System.TimeProvider.GetElapsedTime(System.Int64)](https://docs.microsoft.com/en-us/dotnet/api/System.TimeProvider.GetElapsedTime#System_TimeProvider_GetElapsedTime_System_Int64_ 'System.TimeProvider.GetElapsedTime(System.Int64)').
46+
47+
```csharp
48+
public System.TimeSpan TimestampAdvanceAmount { get; set; }
49+
```
50+
51+
#### Property Value
52+
[System.TimeSpan](https://docs.microsoft.com/en-us/dotnet/api/System.TimeSpan 'System.TimeSpan')
53+
54+
#### Exceptions
55+
56+
[System.ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/System.ArgumentOutOfRangeException 'System.ArgumentOutOfRangeException')
57+
Thrown when set to a value than [System.TimeSpan.Zero](https://docs.microsoft.com/en-us/dotnet/api/System.TimeSpan.Zero 'System.TimeSpan.Zero').
58+
3659
### Remarks
3760
Set to [System.TimeSpan.Zero](https://docs.microsoft.com/en-us/dotnet/api/System.TimeSpan.Zero 'System.TimeSpan.Zero') to disable auto advance. The default value is [System.TimeSpan.Zero](https://docs.microsoft.com/en-us/dotnet/api/System.TimeSpan.Zero 'System.TimeSpan.Zero').

docs/TimeProviderExtensions.ManualTimeProvider.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,9 +323,15 @@ public override long GetTimestamp();
323323
A long integer representing the high-frequency counter value of the underlying timer mechanism.
324324

325325
### Remarks
326+
326327
This implementation bases timestamp on [System.DateTimeOffset.UtcTicks](https://docs.microsoft.com/en-us/dotnet/api/System.DateTimeOffset.UtcTicks 'System.DateTimeOffset.UtcTicks'),
327328
since the progression of time is represented by the date and time returned from [GetUtcNow()](TimeProviderExtensions.ManualTimeProvider.md#TimeProviderExtensions.ManualTimeProvider.GetUtcNow() 'TimeProviderExtensions.ManualTimeProvider.GetUtcNow()').
328329

330+
If [TimestampAdvanceAmount](TimeProviderExtensions.AutoAdvanceBehavior.md#TimeProviderExtensions.AutoAdvanceBehavior.TimestampAdvanceAmount 'TimeProviderExtensions.AutoAdvanceBehavior.TimestampAdvanceAmount') is greater than [System.TimeSpan.Zero](https://docs.microsoft.com/en-us/dotnet/api/System.TimeSpan.Zero 'System.TimeSpan.Zero'), calling this
331+
method will move time forward by the amount specified by [TimestampAdvanceAmount](TimeProviderExtensions.AutoAdvanceBehavior.md#TimeProviderExtensions.AutoAdvanceBehavior.TimestampAdvanceAmount 'TimeProviderExtensions.AutoAdvanceBehavior.TimestampAdvanceAmount').
332+
The [long](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/long 'https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/long') returned from this method will reflect the timestamp before
333+
the auto advance was applied, if any.
334+
329335
<a name='TimeProviderExtensions.ManualTimeProvider.GetUtcNow()'></a>
330336

331337
## ManualTimeProvider.GetUtcNow() Method
@@ -342,8 +348,8 @@ public override System.DateTimeOffset GetUtcNow();
342348
[System.DateTimeOffset](https://docs.microsoft.com/en-us/dotnet/api/System.DateTimeOffset 'System.DateTimeOffset')
343349
344350
### Remarks
345-
If [AutoAdvanceAmount](https://docs.microsoft.com/en-us/dotnet/api/AutoAdvanceAmount 'AutoAdvanceAmount') is greater than [System.TimeSpan.Zero](https://docs.microsoft.com/en-us/dotnet/api/System.TimeSpan.Zero 'System.TimeSpan.Zero'), calling this
346-
method will move time forward by the amount specified by [AutoAdvanceAmount](https://docs.microsoft.com/en-us/dotnet/api/AutoAdvanceAmount 'AutoAdvanceAmount').
351+
If [ClockAdvanceAmount](TimeProviderExtensions.AutoAdvanceBehavior.md#TimeProviderExtensions.AutoAdvanceBehavior.ClockAdvanceAmount 'TimeProviderExtensions.AutoAdvanceBehavior.ClockAdvanceAmount') is greater than [System.TimeSpan.Zero](https://docs.microsoft.com/en-us/dotnet/api/System.TimeSpan.Zero 'System.TimeSpan.Zero'), calling this
352+
method will move time forward by the amount specified by [ClockAdvanceAmount](TimeProviderExtensions.AutoAdvanceBehavior.md#TimeProviderExtensions.AutoAdvanceBehavior.ClockAdvanceAmount 'TimeProviderExtensions.AutoAdvanceBehavior.ClockAdvanceAmount').
347353
The [System.DateTimeOffset](https://docs.microsoft.com/en-us/dotnet/api/System.DateTimeOffset 'System.DateTimeOffset') returned from this method will reflect the time before
348354
the auto advance was applied, if any.
349355

src/TimeProviderExtensions/AutoAdvanceBehavior.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,28 @@ namespace TimeProviderExtensions;
88
public sealed record class AutoAdvanceBehavior
99
{
1010
private TimeSpan clockAdvanceAmount = TimeSpan.Zero;
11+
private TimeSpan timestampAdvanceAmount = TimeSpan.Zero;
1112

1213
/// <summary>
13-
/// Gets or sets the amount of time by which time advances whenever the clock is read via <see cref="TimeProvider.GetUtcNow"/> or <see cref="TimeProvider.GetLocalNow"/>.
14+
/// Gets or sets the amount of time by which time advances whenever the clock is read via <see cref="TimeProvider.GetUtcNow"/>
15+
/// or <see cref="TimeProvider.GetLocalNow"/>.
1416
/// </summary>
1517
/// <remarks>
1618
/// Set to <see cref="TimeSpan.Zero"/> to disable auto advance. The default value is <see cref="TimeSpan.Zero"/>.
1719
/// </remarks>
1820
/// <exception cref="ArgumentOutOfRangeException">Thrown when set to a value than <see cref="TimeSpan.Zero"/>.</exception>
1921
public TimeSpan ClockAdvanceAmount { get => clockAdvanceAmount; set { ThrowIfLessThanZero(value); clockAdvanceAmount = value; } }
2022

23+
/// <summary>
24+
/// Gets or sets the amount of time by which time advances whenever the a timestamp is read via <see cref="TimeProvider.GetTimestamp"/>
25+
/// or an elapsed time is calculated with <see cref="TimeProvider.GetElapsedTime(long)"/>.
26+
/// </summary>
27+
/// <remarks>
28+
/// Set to <see cref="TimeSpan.Zero"/> to disable auto advance. The default value is <see cref="TimeSpan.Zero"/>.
29+
/// </remarks>
30+
/// <exception cref="ArgumentOutOfRangeException">Thrown when set to a value than <see cref="TimeSpan.Zero"/>.</exception>
31+
public TimeSpan TimestampAdvanceAmount { get => timestampAdvanceAmount; set { ThrowIfLessThanZero(value); timestampAdvanceAmount = value; } }
32+
2133
private static void ThrowIfLessThanZero(TimeSpan value, [CallerMemberName] string? parameterName = null)
2234
{
2335
if (value < TimeSpan.Zero)

src/TimeProviderExtensions/ManualTimeProvider.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,29 @@ public ManualTimeProvider(DateTimeOffset startDateTime, TimeZoneInfo localTimeZo
9393
/// </summary>
9494
/// <returns>A long integer representing the high-frequency counter value of the underlying timer mechanism.</returns>
9595
/// <remarks>
96+
/// <para>
9697
/// This implementation bases timestamp on <see cref="DateTimeOffset.UtcTicks"/>,
9798
/// since the progression of time is represented by the date and time returned from <see cref="GetUtcNow()" />.
99+
/// </para>
100+
/// <para>
101+
/// If <see cref="AutoAdvanceBehavior.TimestampAdvanceAmount"/> is greater than <see cref="TimeSpan.Zero"/>, calling this
102+
/// method will move time forward by the amount specified by <see cref="AutoAdvanceBehavior.TimestampAdvanceAmount"/>.
103+
/// The <see langword="long"/> returned from this method will reflect the timestamp before
104+
/// the auto advance was applied, if any.
105+
/// </para>
98106
/// </remarks>
99-
public override long GetTimestamp() => utcNow.UtcTicks;
107+
public override long GetTimestamp()
108+
{
109+
long result;
110+
111+
lock (callbacks)
112+
{
113+
result = utcNow.UtcTicks;
114+
Advance(AutoAdvanceBehavior.TimestampAdvanceAmount);
115+
}
116+
117+
return result;
118+
}
100119

101120
/// <summary>
102121
/// Gets a <see cref="DateTimeOffset"/> value whose date and time are set to the current

test/TimeProviderExtensions.Tests/AutoAdvanceBehaviorTests.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,15 @@ public void ClockAdvanceAmount_throws_when_lt_zero()
1212
throws.Should().Throw<ArgumentOutOfRangeException>()
1313
.And.ParamName.Should().Be(nameof(AutoAdvanceBehavior.ClockAdvanceAmount));
1414
}
15+
16+
[Fact]
17+
public void TimestampAdvanceAmount_throws_when_lt_zero()
18+
{
19+
var sut = new AutoAdvanceBehavior();
20+
21+
var throws = () => sut.TimestampAdvanceAmount = TimeSpan.FromTicks(-1);
22+
23+
throws.Should().Throw<ArgumentOutOfRangeException>()
24+
.And.ParamName.Should().Be(nameof(AutoAdvanceBehavior.TimestampAdvanceAmount));
25+
}
1526
}

test/TimeProviderExtensions.Tests/ManualTimeProviderTests.cs

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,14 +150,59 @@ async Task AsyncCallbacks(PeriodicTimer periodicTimer)
150150
public void Timer_callback_GetUtcNow_AutoAdvance()
151151
{
152152
var oneSecond = TimeSpan.FromSeconds(1);
153-
var timeProvider = new ManualTimeProvider() { AutoAdvanceBehavior = { ClockAdvanceAmount = oneSecond } };
153+
var sut = new ManualTimeProvider() { AutoAdvanceBehavior = { ClockAdvanceAmount = oneSecond } };
154154

155-
using var t1 = timeProvider.CreateTimer(_ =>
155+
using var t1 = sut.CreateTimer(_ =>
156156
{
157-
timeProvider.GetUtcNow();
157+
sut.GetUtcNow();
158158
}, null, TimeSpan.Zero, oneSecond);
159159

160-
timeProvider.GetUtcNow().Should().Be(timeProvider.Start + oneSecond);
160+
sut.GetUtcNow().Should().Be(sut.Start + oneSecond);
161+
}
162+
163+
[Fact]
164+
public void GetUtcNow_with_ClockAdvanceAmount_gt_zero()
165+
{
166+
var sut = new ManualTimeProvider() { AutoAdvanceBehavior = { ClockAdvanceAmount = 1.Seconds() } };
167+
168+
var result = sut.GetUtcNow();
169+
170+
result.Should().Be(sut.Start);
171+
sut.GetUtcNow().Should().Be(sut.Start + 1.Seconds());
172+
}
173+
174+
[Fact]
175+
public void GetLocalNow_with_ClockAdvanceAmount_gt_zero()
176+
{
177+
var sut = new ManualTimeProvider() { AutoAdvanceBehavior = { ClockAdvanceAmount = 1.Seconds() } };
178+
179+
var result = sut.GetLocalNow();
180+
181+
result.Should().Be(sut.Start);
182+
sut.GetLocalNow().Should().Be(sut.Start + 1.Seconds());
183+
}
184+
185+
[Fact]
186+
public void GetTimestamp_with_TimestampAdvanceAmount_gt_zero()
187+
{
188+
var sut = new ManualTimeProvider() { AutoAdvanceBehavior = { TimestampAdvanceAmount = 1.Seconds() } };
189+
190+
var result = sut.GetTimestamp();
191+
192+
result.Should().Be(sut.Start.Ticks);
193+
sut.GetTimestamp().Should().Be(result + 1.Seconds().Ticks);
194+
}
195+
196+
[Fact]
197+
public void GetElapsedTime_with_TimestampAdvanceAmount_gt_zero()
198+
{
199+
var sut = new ManualTimeProvider() { AutoAdvanceBehavior = { TimestampAdvanceAmount = 1.Seconds() } };
200+
var start = sut.Start.Ticks;
201+
202+
var result = sut.GetElapsedTime(start);
203+
204+
result.Should().Be(0.Seconds());
205+
sut.GetElapsedTime(start).Should().Be(1.Seconds());
161206
}
162207

163208
[Fact]

0 commit comments

Comments
 (0)