@@ -29,7 +29,15 @@ public abstract class TimeProvider
2929 /// <param name="timestampFrequency">Frequency of the values returned from <see cref="GetTimestamp"/> method.</param>
3030 protected TimeProvider ( long timestampFrequency )
3131 {
32+ #if SYSTEM_PRIVATE_CORELIB
3233 ArgumentOutOfRangeException . ThrowIfNegativeOrZero ( timestampFrequency ) ;
34+ #else
35+ if ( timestampFrequency <= 0 )
36+ {
37+ throw new ArgumentOutOfRangeException ( nameof ( timestampFrequency ) , timestampFrequency , SR . Format ( SR . ArgumentOutOfRange_Generic_MustBeNonNegativeNonZero , nameof ( timestampFrequency ) ) ) ;
38+ }
39+ #endif // SYSTEM_PRIVATE_CORELIB
40+
3341 TimestampFrequency = timestampFrequency ;
3442 _timeToTicksRatio = ( double ) TimeSpan . TicksPerSecond / TimestampFrequency ;
3543 }
@@ -41,6 +49,9 @@ protected TimeProvider(long timestampFrequency)
4149 /// </summary>
4250 public abstract DateTimeOffset UtcNow { get ; }
4351
52+ private static readonly long s_minDateTicks = DateTime . MinValue . Ticks ;
53+ private static readonly long s_maxDateTicks = DateTime . MaxValue . Ticks ;
54+
4455 /// <summary>
4556 /// Gets a <see cref="DateTimeOffset"/> value that is set to the current date and time according to this <see cref="TimeProvider"/>'s
4657 /// notion of time based on <see cref="UtcNow"/>, with the offset set to the <see cref="LocalTimeZone"/>'s offset from Coordinated Universal Time (UTC).
@@ -53,9 +64,9 @@ public DateTimeOffset LocalNow
5364 TimeSpan offset = LocalTimeZone . GetUtcOffset ( utcDateTime ) ;
5465
5566 long localTicks = utcDateTime . Ticks + offset . Ticks ;
56- if ( ( ulong ) localTicks > DateTime . MaxTicks )
67+ if ( ( ulong ) localTicks > ( ulong ) s_maxDateTicks )
5768 {
58- localTicks = localTicks < DateTime . MinTicks ? DateTime . MinTicks : DateTime . MaxTicks ;
69+ localTicks = localTicks < s_minDateTicks ? s_minDateTicks : s_maxDateTicks ;
5970 }
6071
6172 return new DateTimeOffset ( localTicks , offset ) ;
@@ -82,7 +93,15 @@ public DateTimeOffset LocalNow
8293 /// <exception cref="ArgumentNullException"><paramref name="timeZone"/> is null.</exception>
8394 public static TimeProvider FromLocalTimeZone ( TimeZoneInfo timeZone )
8495 {
96+ #if SYSTEM_PRIVATE_CORELIB
8597 ArgumentNullException . ThrowIfNull ( timeZone ) ;
98+ #else
99+ if ( timeZone is null )
100+ {
101+ throw new ArgumentNullException ( nameof ( timeZone ) ) ;
102+ }
103+ #endif // SYSTEM_PRIVATE_CORELIB
104+
86105 return new SystemTimeProvider ( timeZone ) ;
87106 }
88107
@@ -155,7 +174,15 @@ private sealed class SystemTimeProvider : TimeProvider
155174 /// <inheritdoc/>
156175 public override ITimer CreateTimer ( TimerCallback callback , object ? state , TimeSpan dueTime , TimeSpan period )
157176 {
177+ #if SYSTEM_PRIVATE_CORELIB
158178 ArgumentNullException . ThrowIfNull ( callback ) ;
179+ #else
180+ if ( callback is null )
181+ {
182+ throw new ArgumentNullException ( nameof ( callback ) ) ;
183+ }
184+ #endif // SYSTEM_PRIVATE_CORELIB
185+
159186 return new SystemTimeProviderTimer ( dueTime , period , callback , state ) ;
160187 }
161188
@@ -165,7 +192,7 @@ public override ITimer CreateTimer(TimerCallback callback, object? state, TimeSp
165192 /// <inheritdoc/>
166193 public override DateTimeOffset UtcNow => DateTimeOffset . UtcNow ;
167194
168- /// <summary>Thin wrapper for a <see cref="TimerQueueTimer "/>.</summary>
195+ /// <summary>Thin wrapper for a <see cref="Timer "/>.</summary>
169196 /// <remarks>
170197 /// We don't return a TimerQueueTimer directly as it implements IThreadPoolWorkItem and we don't
171198 /// want it exposed in a way that user code could directly queue the timer to the thread pool.
@@ -174,33 +201,85 @@ public override ITimer CreateTimer(TimerCallback callback, object? state, TimeSp
174201 /// </remarks>
175202 private sealed class SystemTimeProviderTimer : ITimer
176203 {
204+ #if SYSTEM_PRIVATE_CORELIB
177205 private readonly TimerQueueTimer _timer ;
178-
206+ #else
207+ private readonly Timer _timer ;
208+ #endif // SYSTEM_PRIVATE_CORELIB
179209 public SystemTimeProviderTimer ( TimeSpan dueTime , TimeSpan period , TimerCallback callback , object ? state )
180210 {
181211 ( uint duration , uint periodTime ) = CheckAndGetValues ( dueTime , period ) ;
212+ #if SYSTEM_PRIVATE_CORELIB
182213 _timer = new TimerQueueTimer ( callback , state , duration , periodTime , flowExecutionContext : true ) ;
214+ #else
215+ // We want to ensure the timer we create will be tracked as long as it is scheduled.
216+ // To do that, we call the constructor which track only the callback which will make the time to be tracked by the scheduler
217+ // then we call Change on the timer to set the desired duration and period.
218+ _timer = new Timer ( _ => callback ( state ) ) ;
219+ _timer . Change ( duration , periodTime ) ;
220+ #endif // SYSTEM_PRIVATE_CORELIB
183221 }
184222
185223 public bool Change ( TimeSpan dueTime , TimeSpan period )
186224 {
187225 ( uint duration , uint periodTime ) = CheckAndGetValues ( dueTime , period ) ;
188- return _timer . Change ( duration , periodTime ) ;
226+ try
227+ {
228+ return _timer . Change ( duration , periodTime ) ;
229+ }
230+ catch ( ObjectDisposedException )
231+ {
232+ return false ;
233+ }
189234 }
190235
191236 public void Dispose ( ) => _timer . Dispose ( ) ;
192237
238+
239+ #if SYSTEM_PRIVATE_CORELIB
193240 public ValueTask DisposeAsync ( ) => _timer . DisposeAsync ( ) ;
241+ #else
242+ public ValueTask DisposeAsync ( )
243+ {
244+ _timer . Dispose ( ) ;
245+ return default ;
246+ }
247+ #endif // SYSTEM_PRIVATE_CORELIB
194248
195249 private static ( uint duration , uint periodTime ) CheckAndGetValues ( TimeSpan dueTime , TimeSpan periodTime )
196250 {
197251 long dueTm = ( long ) dueTime . TotalMilliseconds ;
252+ long periodTm = ( long ) periodTime . TotalMilliseconds ;
253+
254+ #if SYSTEM_PRIVATE_CORELIB
198255 ArgumentOutOfRangeException . ThrowIfLessThan ( dueTm , - 1 , nameof ( dueTime ) ) ;
199256 ArgumentOutOfRangeException . ThrowIfGreaterThan ( dueTm , Timer . MaxSupportedTimeout , nameof ( dueTime ) ) ;
200257
201- long periodTm = ( long ) periodTime . TotalMilliseconds ;
202258 ArgumentOutOfRangeException . ThrowIfLessThan ( periodTm , - 1 , nameof ( periodTime ) ) ;
203259 ArgumentOutOfRangeException . ThrowIfGreaterThan ( periodTm , Timer . MaxSupportedTimeout , nameof ( periodTime ) ) ;
260+ #else
261+ const uint MaxSupportedTimeout = 0xfffffffe ;
262+
263+ if ( dueTm < - 1 )
264+ {
265+ throw new ArgumentOutOfRangeException ( nameof ( dueTime ) , dueTm , SR . Format ( SR . ArgumentOutOfRange_Generic_MustBeGreaterOrEqual , nameof ( dueTime ) , - 1 ) ) ;
266+ }
267+
268+ if ( dueTm > MaxSupportedTimeout )
269+ {
270+ throw new ArgumentOutOfRangeException ( nameof ( dueTime ) , dueTm , SR . Format ( SR . ArgumentOutOfRange_Generic_MustBeLessOrEqual , nameof ( dueTime ) , MaxSupportedTimeout ) ) ;
271+ }
272+
273+ if ( periodTm < - 1 )
274+ {
275+ throw new ArgumentOutOfRangeException ( nameof ( periodTm ) , periodTm , SR . Format ( SR . ArgumentOutOfRange_Generic_MustBeGreaterOrEqual , nameof ( periodTm ) , - 1 ) ) ;
276+ }
277+
278+ if ( periodTm > MaxSupportedTimeout )
279+ {
280+ throw new ArgumentOutOfRangeException ( nameof ( periodTm ) , periodTm , SR . Format ( SR . ArgumentOutOfRange_Generic_MustBeLessOrEqual , nameof ( periodTm ) , MaxSupportedTimeout ) ) ;
281+ }
282+ #endif // SYSTEM_PRIVATE_CORELIB
204283
205284 return ( ( uint ) dueTm , ( uint ) periodTm ) ;
206285 }
0 commit comments