6

Im trying to start a stopwatch from a given time (decimal value pulled from a database). However, because the Stopwatch.Elapsed.Add returns a new Timespan rather than modify the Stopwatch, I can't work out the best way forward.

var offsetTimeStamp = new System.TimeSpan(0,0,0).Add(TimeSpan.FromSeconds((double)jd.ActualTime));
Stopwatch.Elapsed.Add(offsetTimeStamp);
Stopwatch.Start();

Any ideas how I can do this? Cheers

Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
dynamicuser
  • 1,522
  • 3
  • 24
  • 52
  • Why would you need that? If you want to add an offset, then measure what you want to measure and then add the offset to the result... – Patryk Ćwiek Jul 16 '13 at 09:29
  • 1
    The timer is created when a 'job' is 'started'. Every 10 seconds, the stopwatch current time is submitted to the database. When the job is paused the stopwatch stops ticking. If the PC was reset IE powercut etc, the stopwatch is lost from memory (a list of stopwatches). Therefore I need to recreate each stopwatch when the job is restarted, therefore resuming timing from its 'jd.ActualTime'. – dynamicuser Jul 16 '13 at 09:35
  • Why not `TimeSpan total = Stopwatch.Elapsed + offsetTimeStamp;` or similar? – Nolonar Jul 16 '13 at 09:35

5 Answers5

7

The normal StopWatch does not support initialization with an offset timespan and TimeSpan is a struct, therefore Elapsed is immutable. You could write a wrapper around StopWatch:

public class StopWatchWithOffset
{
    private Stopwatch _stopwatch = null;
    TimeSpan _offsetTimeSpan;

    public StopWatchWithOffset(TimeSpan offsetElapsedTimeSpan)
    {
        _offsetTimeSpan = offsetElapsedTimeSpan;
        _stopwatch = new Stopwatch();
    }

    public void Start()
    {
        _stopwatch.Start();
    }

    public void Stop()
    {
        _stopwatch.Stop();
    }

    public TimeSpan ElapsedTimeSpan
    {
        get
        {
            return _stopwatch.Elapsed + _offsetTimeSpan;
        }
        set
        {
            _offsetTimeSpan = value;
        }
    }
}

Now you can add a start-timespan:

var offsetTimeStamp = TimeSpan.FromHours(1);
var watch = new StopWatchWithOffset(offsetTimeStamp);
watch.Start();
System.Threading.Thread.Sleep(300); 
Console.WriteLine(watch.ElapsedTimeSpan);// 01:00:00.2995983
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • 2
    In the `set` accessor of `ElapsedTimeSpan`, shouldn't it be `_offsetTimeSpan = value - _stopwatch.Elapsed` to maintain the same relation between these three magnitude as the `get` accessor has? Otherwise if i _set_ the property and _get_ it immediately after, it has not acquired the value I set it to. – Jeppe Stig Nielsen Feb 26 '17 at 09:50
0

The Elapsed property of StopWatch is read only, which makes sense. A stopwatch simply measures the amount of time that passed between start and stop.

If you want to add a timespan to the value - get the Elapsed value in a variable and add a timespan to it, after you have measured it (i.e. after stopping).

Oded
  • 489,969
  • 99
  • 883
  • 1,009
0

I think you want to start your Stopwatch after a certain mount of time specified by a TimeSpan. I wonder why you don't want to start your Stopwatch at a time specified by a DateTime instead?

public class MyStopwatch : Stopwatch
{
    public void Start(long afterMiliseconds)
    {
        Timer t = new Timer() { Interval = 1 };
        int i = 0;
        t.Tick += (s, e) =>
        {
            if (i++ == afterMiliseconds)
            {
                Start();
                t.Stop();
            }
        };
        t.Start();
    }
}
//use it
var offsetTimeStamp = new System.TimeSpan(0,0,0).Add(TimeSpan.FromSeconds((double)jd.ActualTime));
myStopwatch.Start((long)offsetTimeStamp.TotalMiliseconds);
King King
  • 61,710
  • 16
  • 105
  • 130
0

If you add this file to your project, there is nothing you need to change in your project. This class inherits from the original Stopwatch class and has the same name and the same methods/properties, but with additional features:

  • SetOffset() method
  • Initialization with offset

.

using System;

public class Stopwatch : System.Diagnostics.Stopwatch
{
    TimeSpan _offset = new TimeSpan();

    public Stopwatch()
    {
    }

    public Stopwatch(TimeSpan offset)
    {
        _offset = offset;
    }

    public void SetOffset(TimeSpan offsetElapsedTimeSpan)
    {
        _offset = offsetElapsedTimeSpan;
    }

    public TimeSpan Elapsed
    {
        get{ return base.Elapsed + _offset; }
        set{ _offset = value; }
    }

    public long ElapsedMilliseconds
    {
        get { return base.ElapsedMilliseconds + _offset.Milliseconds; }
    }

    public long ElapsedTicks
    {
        get { return base.ElapsedTicks + _offset.Ticks; }
    }

}
Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
Andrew_STOP_RU_WAR_IN_UA
  • 9,318
  • 5
  • 65
  • 101
  • 1
    The practice of __hiding__ members of the base class (such as `Elapsed`) with __new__ members with the same names, I do not like. This is confusing e.g. if you have a variable of compile-time type `System.Diagnostics.Stopwatch` which happens to be a `YourNamespace.Stopwatch` at run-time. For this reason, I like Tim Schmelter's answer more. `System.Diagnostics.Stopwatch` does not declare these properties `virtual`, it was not designed for it, and for that reason deriving from that class is not the best solution. – Jeppe Stig Nielsen Feb 26 '17 at 09:55
0

This isn't a great fit for the OPs scenario (which I'm guessing they solved 8 years ago), but if you just need to create stopwatches for unit tests or other non-production scenarios then you can use reflection to modify the elapsed time.

This isn't going to give you the best performance, and can break if the underlying implementation of Stopwatch changes, so I'd be very circumspect using this approach.

However, for unit tests where you need to pass around a Stopwatch and can't change to use an alternate implementation, I find this approach to work well and the risk to be acceptable.

/// <summary>
/// Some static mechanisms for creating Stopwatch instances that start from a specific time.
/// </summary>
public static class TestStopwatch
{
    /// <summary>
    /// Creates a <see cref="Stopwatch"/> instance with a specified amount of time already elapsed
    /// </summary>
    /// <param name="start">The <see cref="TimeSpan"/> indicated the elapsed time to start from.</param>
    public static Stopwatch WithElapsed(TimeSpan start)
    {
        var sw = new Stopwatch();

        var elapsedProperty = typeof(Stopwatch).GetField("_elapsed", BindingFlags.NonPublic | BindingFlags.Instance);

        long rawElapsedTicks = start.Ticks;

        if (Stopwatch.IsHighResolution)
        {
            rawElapsedTicks = (long)((double)rawElapsedTicks / (10000000 / (double)Stopwatch.Frequency));
        }

        elapsedProperty.SetValue(sw, rawElapsedTicks);

        return sw;
    }

    /// <summary>
    /// Initializes a new <see cref="Stopwatch"/> instance, sets the elapsed time property to the specified value,
    /// and starts measuring elapsed time.
    /// </summary>
    /// <param name="start">The <see cref="TimeSpan"/> indicated the elapsed time to start from.</param>
    public static Stopwatch StartNew(TimeSpan start)
    {
        var sw = TestStopwatch.WithElapsed(start);
        sw.Start();
        return sw;
    }
}
Reed Rector
  • 492
  • 3
  • 6