<PackageReference Include="System.Reactive" Version="4.4.1" />

PeriodicTimerSystemClockMonitor

(Infrastructure) Monitors for system clock changes based on a periodic timer.
using System.ComponentModel; using System.Reactive.Concurrency; using System.Reactive.Disposables; using System.Threading; using System.Threading.Tasks; namespace System.Reactive.PlatformServices { [EditorBrowsable(EditorBrowsableState.Never)] public class PeriodicTimerSystemClockMonitor : INotifySystemClockChanged { private readonly TimeSpan _period; private IDisposable _timer; private long _lastTimeUnixMillis; private EventHandler<SystemClockChangedEventArgs> _systemClockChanged; private const int SyncMaxRetries = 100; private const double SyncMaxDelta = 10; private const int MaxError = 100; public event EventHandler<SystemClockChangedEventArgs> SystemClockChanged { add { NewTimer(); _systemClockChanged = (EventHandler<SystemClockChangedEventArgs>)Delegate.Combine(_systemClockChanged, value); } remove { _systemClockChanged = (EventHandler<SystemClockChangedEventArgs>)Delegate.Remove(_systemClockChanged, value); Disposable.TrySetSerial(ref _timer, Disposable.Empty); } } public PeriodicTimerSystemClockMonitor(TimeSpan period) { _period = period; } private void NewTimer() { Disposable.TrySetSerial(ref _timer, Disposable.Empty); long num = 0; while (true) { DateTimeOffset utcNow = SystemClock.UtcNow; long num2 = utcNow.ToUnixTimeMilliseconds(); Interlocked.Exchange(ref _lastTimeUnixMillis, num2); Disposable.TrySetSerial(ref _timer, ConcurrencyAbstractionLayer.Current.StartPeriodicTimer(TimeChanged, _period)); utcNow = SystemClock.UtcNow; if ((double)Math.Abs(utcNow.ToUnixTimeMilliseconds() - num2) <= 10 || Volatile.Read(ref _timer) == Disposable.Empty) break; if (++num >= 100) Task.Delay(10).Wait(); } } private void TimeChanged() { DateTimeOffset utcNow = SystemClock.UtcNow; long num = utcNow.ToUnixTimeMilliseconds(); long num2 = (long)((double)Volatile.Read(ref _lastTimeUnixMillis) + _period.TotalMilliseconds); if (Math.Abs(num - num2) >= 100) { _systemClockChanged?.Invoke(this, new SystemClockChangedEventArgs(DateTimeOffset.FromUnixTimeMilliseconds(num2), utcNow)); NewTimer(); } else Interlocked.Exchange(ref _lastTimeUnixMillis, SystemClock.UtcNow.ToUnixTimeMilliseconds()); } } }