diff --git a/Duplicati/Library/RestAPI/LiveControls.cs b/Duplicati/Library/RestAPI/LiveControls.cs index 0e8b41d78..8cffe137a 100644 --- a/Duplicati/Library/RestAPI/LiveControls.cs +++ b/Duplicati/Library/RestAPI/LiveControls.cs @@ -94,7 +94,7 @@ namespace Duplicati.Server /// /// The time to pause for, used to ensure that a user set pause can override the suspend pause /// - private DateTime m_suspendMinimumPause = new DateTime(0); + private DateTime m_suspendMinimumPause = new DateTime(0, DateTimeKind.Utc); /// /// Gets the current state for the control @@ -124,7 +124,7 @@ namespace Duplicati.Server /// /// The time that the current pause is expected to expire /// - private DateTime m_waitTimeExpiration = new DateTime(0); + private DateTime m_waitTimeExpiration = new DateTime(0, DateTimeKind.Utc); /// /// The connection to use @@ -141,6 +141,20 @@ namespace Duplicati.Server Init(); } + /// + /// Clamps the milliseconds to a valid range for the timer + /// + /// The duration to clamp + /// The clamped milliseconds + private static long ClampMilliseconds(TimeSpan duration) + { + if (duration.TotalMilliseconds < 100) + return 100; + if (duration > TimeSpan.FromHours(24)) + return (long)TimeSpan.FromHours(24).TotalMilliseconds; + return (long)duration.TotalMilliseconds; + } + /// /// Constructs a new instance of the LiveControl /// @@ -152,14 +166,14 @@ namespace Duplicati.Server if (!string.IsNullOrEmpty(settings.StartupDelayDuration) && settings.StartupDelayDuration != "0") { - long milliseconds = 0; - try { milliseconds = (long)Duplicati.Library.Utility.Timeparser.ParseTimeSpan(settings.StartupDelayDuration).TotalMilliseconds; } + var startupDelay = new TimeSpan(0); + try { startupDelay = Library.Utility.Timeparser.ParseTimeSpan(settings.StartupDelayDuration); } catch { } - if (milliseconds > 0) + if (startupDelay.Ticks > 0) { - m_waitTimeExpiration = DateTime.Now.AddMilliseconds(milliseconds); - m_waitTimer.Change(milliseconds, System.Threading.Timeout.Infinite); + m_waitTimeExpiration = DateTime.UtcNow.Add(startupDelay); + m_waitTimer.Change(ClampMilliseconds(startupDelay), System.Threading.Timeout.Infinite); m_state = LiveControlState.Paused; } } @@ -169,17 +183,17 @@ namespace Duplicati.Server { if (pausedUntil.Value.Ticks == 0) { - m_waitTimeExpiration = new DateTime(0); + m_waitTimeExpiration = new DateTime(0, DateTimeKind.Utc); m_waitTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); m_state = LiveControlState.Paused; } else if (pausedUntil.Value > DateTime.UtcNow && pausedUntil.Value > m_waitTimeExpiration) { - var ms = (long)(pausedUntil.Value - DateTime.UtcNow).TotalMilliseconds; - if (ms > 100) + var period = pausedUntil.Value - DateTime.UtcNow; + if (period.TotalMilliseconds > 100) { m_waitTimeExpiration = pausedUntil.Value; - m_waitTimer.Change(ms, System.Threading.Timeout.Infinite); + m_waitTimer.Change(ClampMilliseconds(period), System.Threading.Timeout.Infinite); m_state = LiveControlState.Paused; } } @@ -227,13 +241,13 @@ namespace Duplicati.Server lock (m_lock) if (!string.IsNullOrEmpty(timeout)) { - long milliseconds = (long)Duplicati.Library.Utility.Timeparser.ParseTimeSpan(timeout).TotalMilliseconds; - m_waitTimeExpiration = DateTime.Now.AddMilliseconds(milliseconds); - m_waitTimer.Change(milliseconds, System.Threading.Timeout.Infinite); + var delay = Library.Utility.Timeparser.ParseTimeSpan(timeout); + m_waitTimeExpiration = DateTime.UtcNow.Add(delay); + m_waitTimer.Change(ClampMilliseconds(delay), System.Threading.Timeout.Infinite); } else { - m_waitTimeExpiration = new DateTime(0); + m_waitTimeExpiration = new DateTime(0, DateTimeKind.Utc); m_waitTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); } } @@ -267,7 +281,7 @@ namespace Duplicati.Server lock (m_lock) { m_transfersPaused = alsoTransfers; - m_waitTimeExpiration = new DateTime(0); + m_waitTimeExpiration = new DateTime(0, DateTimeKind.Utc); ResetTimer(null); @@ -295,7 +309,7 @@ namespace Duplicati.Server ResetTimer(null); m_transfersPaused = false; - m_waitTimeExpiration = new DateTime(0); + m_waitTimeExpiration = new DateTime(0, DateTimeKind.Utc); m_state = LiveControlState.Running; if (StateChanged != null) ev = CreateEvent(); @@ -326,8 +340,8 @@ namespace Duplicati.Server LiveControlEvent ev = null; lock (m_lock) { - m_waitTimeExpiration = DateTime.Now.AddMilliseconds((long)timeout.TotalMilliseconds); - m_waitTimer.Change((long)timeout.TotalMilliseconds, System.Threading.Timeout.Infinite); + m_waitTimeExpiration = DateTime.UtcNow.Add(timeout); + m_waitTimer.Change(ClampMilliseconds(timeout), System.Threading.Timeout.Infinite); m_transfersPaused = alsoTransfers; @@ -375,7 +389,7 @@ namespace Duplicati.Server { this.SetPauseMode(); m_pausedForSuspend = true; - m_suspendMinimumPause = new DateTime(0); + m_suspendMinimumPause = new DateTime(0, DateTimeKind.Utc); } else { @@ -393,7 +407,7 @@ namespace Duplicati.Server //If we have been been paused due to suspending, we un-pause now if (m_pausedForSuspend) { - long delayTicks = (m_suspendMinimumPause - DateTime.Now).Ticks; + long delayTicks = (m_suspendMinimumPause - DateTime.UtcNow).Ticks; var appset = m_connection.ApplicationSettings; if (!string.IsNullOrEmpty(appset.StartupDelayDuration) && appset.StartupDelayDuration != "0") @@ -411,7 +425,7 @@ namespace Duplicati.Server } m_pausedForSuspend = false; - m_suspendMinimumPause = new DateTime(0); + m_suspendMinimumPause = new DateTime(0, DateTimeKind.Utc); } } diff --git a/Duplicati/WebserverCore/Services/StatusService.cs b/Duplicati/WebserverCore/Services/StatusService.cs index 8fe48a08d..7c3845b8b 100644 --- a/Duplicati/WebserverCore/Services/StatusService.cs +++ b/Duplicati/WebserverCore/Services/StatusService.cs @@ -54,7 +54,7 @@ public class StatusService( ProgramState = GetProgramState(), HasWarning = settingsService.GetSettings().UnackedWarning, HasError = settingsService.GetSettings().UnackedError, - EstimatedPauseEnd = liveControls.EstimatedPauseEnd, + EstimatedPauseEnd = liveControls.EstimatedPauseEnd.ToUniversalTime(), SuggestedStatusIcon = MapStateToIcon(), UpdateDownloadLink = settingsService.GetSettings().NewVersionUpdateUrl }; @@ -65,7 +65,7 @@ public class StatusService( private void PullLiveControls(ServerStatusDto status) { - status.EstimatedPauseEnd = liveControls.EstimatedPauseEnd; + status.EstimatedPauseEnd = liveControls.EstimatedPauseEnd.ToUniversalTime(); status.ProgramState = GetProgramState(); status.SuggestedStatusIcon = MapStateToIcon(); }