从指定时间开始计时

本文关键字:开始 定时间 | 更新日期: 2023-09-27 18:22:25

我试图从给定的时间(从数据库中提取的十进制值)启动秒表。然而,由于Stopwatch.Elapsed.Add返回一个新的Timespan,而不是修改Stopwatch,所以我无法找到最佳的前进方式。

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

你知道我该怎么做吗?干杯

从指定时间开始计时

正常的StopWatch不支持偏移时间跨度的初始化,而TimeSpanstruct,因此Elapsed是不可变的。你可以围绕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;
        }
    }
}

现在您可以添加一个开始时间跨度:

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

StopWatchElapsed属性是只读的,这是有意义的。秒表只是测量从开始到停止之间经过的时间。

如果要将时间跨度添加到值中,请获取变量中的Elapsed值,并在测量后(即停止后)向其添加时间跨度。

我认为您希望在TimeSpan指定的一定时间后启动Stopwatch。我想知道为什么您不想在DateTime指定的时间启动Stopwatch

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);

这不太适合OP场景(我猜他们8年前就解决了这个问题),但如果你只需要为单元测试或其他非生产场景创建秒表,那么你可以使用反射来修改经过的时间。

这不会给你最好的性能,如果Stopwatch的底层实现发生变化,可能会中断,所以我会非常谨慎地使用这种方法。

然而,对于需要绕过Stopwatch并且不能更改为使用替代实现的单元测试,我发现这种方法运行良好,并且风险是可以接受的。

/// <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;
    }
}

如果将此文件添加到项目中,则项目中不需要更改任何内容。该类继承自原始Stopwatch类,具有相同的名称和相同的方法/属性,但具有其他功能:

  • SetOffset()方法
  • 带偏移量的初始化

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; }
    }
}