如何在.NET/C#中获取tick精度的时间戳?

61 .net c# timestamp

到目前为止,我用来DateTime.Now获取时间戳,但是我注意到如果你DateTime.Now在一个循环中打印,你会看到它以大约的离散跳跃增量.15毫秒 但对于我的应用程序中的某些情况,我需要获得最准确的时间戳,最好是精确到tick(= 100 ns).有任何想法吗?

更新:

显然,StopWatch/ QueryPerformanceCounter是要走的路,但它只能用于测量时间,因此我考虑DateTime.Now在应用程序启动时调用,然后StopWatch运行,然后只将经过的时间从StopWatch返回的初始值中添加DateTime.Now.至少那应该给我准确的相对时间戳,对吧?你怎么看待(黑客)?

注意:

StopWatch.ElapsedTicks不同于StopWatch.Elapsed.Ticks!我使用前者假设1 tick = 100 ns,但在这种情况下1 tick = 1/StopWatch.Frequency.所以要获得相当于DateTime使用的刻度StopWatch.Elapsed.Ticks.我刚刚学到了这个困难的方法.

笔记2:

使用StopWatch方法,我注意到它与实时不同步.大约10个小时后,它提前5秒.所以我想我们必须每隔X左右重新同步一次,其中X可能是1小时30分钟,15分钟等等.我不确定重新同步的最佳时间跨度是什么,因为每次重新同步都会改变偏移量,这可能是最多20毫秒.

Mus*_*sis 56

DateTime.Now读取的系统时钟的值仅每15 ms左右更新一次(或在某些系统上为10 ms),这就是为什么时间在这些间隔周围量化的原因.由于您的代码在多线程操作系统中运行,因此存在额外的量化效应,因此存在应用程序不"活动"并且因此不测量实际当前时间的延伸.

由于您正在寻找超精确的时间戳值(而不是仅计时任意持续时间),因此Stopwatch该类本身无法满足您的需求.我想你必须自己做一些DateTime/ Stopwatch混合动力.当您的应用程序启动时,您将存储当前DateTime.UtcNow值(即应用程序启动时的原始分辨率时间),然后还启动一个Stopwatch对象,如下所示:

DateTime _starttime = DateTime.UtcNow;
Stopwatch _stopwatch = Stopwatch.StartNew();
Run Code Online (Sandbox Code Playgroud)

然后,只要您需要高分辨率DateTime值,就可以这样:

DateTime highresDT = _starttime.AddTicks(_stopwatch.Elapsed.Ticks);
Run Code Online (Sandbox Code Playgroud)

你也可能想要定期重置_starttime和_stopwatch,以防止结果时间与系统时间过于不同步(虽然我不确定这实际上会发生,但无论如何都需要很长时间才能发生) .

更新:因为看起来秒表确实与系统时间不一致(每小时多半秒),我认为DateTime根据调用之间的时间间隔重置混合类是有意义的时间:

public class HiResDateTime
{
    private static DateTime _startTime;
    private static Stopwatch _stopWatch = null;
    private static TimeSpan _maxIdle = 
        TimeSpan.FromSeconds(10);

    public static DateTime UtcNow
    {
        get
        {
            if ((_stopWatch == null) || 
                (_startTime.Add(_maxIdle) < DateTime.UtcNow))
            {
                Reset();
            }
            return _startTime.AddTicks(_stopWatch.Elapsed.Ticks);
        }
    }

    private static void Reset()
    {
        _startTime = DateTime.UtcNow;
        _stopWatch = Stopwatch.StartNew();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您以某个固定间隔(例如每小时或某些时间)重置混合计时器,则可能会在最后一次读取时间之前设置时间,这类似于微型夏令时问题.

  • 顺便说一句,使用DateTime.UtcNow而不是DateTime.Now对于时间戳来说实际上很重要.否则,夏令时会突然将时间戳值重新推迟一个小时,从而使您每个秋天都感到困惑. (11认同)
  • 谢谢.我已经不得不实现自己的'Date','Time'和'DateAndTime'类来解决DateTime的所有缺点(例如,没有对时区的内在支持).关于日期/时间重新发明轮子似乎是极端的,但我真的很惊讶DateTime的设计和实现有多糟糕.你会想到他们拥有的所有资源和知识,他们可以做得更好. (2认同)

Mar*_*s.D 21

要获得高分辨率的滴答计数,请使用静态Stopwatch.GetTimestamp() - 方法:

long tickCount = System.Diagnostics.Stopwatch.GetTimestamp();
DateTime highResDateTime = new DateTime(tickCount);
Run Code Online (Sandbox Code Playgroud)

只需看看.NET源代码:

    public static long GetTimestamp() {
        if(IsHighResolution) {
            long timestamp = 0;    
            SafeNativeMethods.QueryPerformanceCounter(out timestamp);
            return timestamp;
        }
        else {
            return DateTime.UtcNow.Ticks;
        }   
    }
Run Code Online (Sandbox Code Playgroud)

源代码在这里:http://referencesource.microsoft.com/#System/services/monitoring/system/diagnosticts/Stopwatch.cs,69c6c3137e12dab4


Ian*_*cer 19

[接受的答案似乎不是线程安全的,并且它自己的录取可以及时倒退导致重复的时间戳,因此这个替代答案]

如果您真正关心的(根据您的评论)实际上是一个以严格升序分配并且尽可能与系统时间相对应的唯一时间戳,您可以尝试这种替代方法:

public class HiResDateTime
{
   private static long lastTimeStamp = DateTime.UtcNow.Ticks;
   public static long UtcNowTicks
   {
       get
       {
           long orig, newval;
           do
           {
               orig = lastTimeStamp;
               long now = DateTime.UtcNow.Ticks;
               newval = Math.Max(now, orig + 1);
           } while (Interlocked.CompareExchange
                        (ref lastTimeStamp, newval, orig) != orig);

           return newval;
       }
   }
}
Run Code Online (Sandbox Code Playgroud)

  • 为了记录,我不承认我的方法可以及时倒退.我写道:"如果你定期重置混合计时器(比如每小时或某事),你就有可能在最后一次读取时间之前设置时间",这就是为什么我选择了*不*来解释以某个固定间隔重置计时器.但是,我的解决方案肯定不是线程安全的,我喜欢你的解决方案的非重复功能. (5认同)
  • 此解决方案的局限性在于其*分辨率*受DateTime限制为1ms或16ms(取决于硬件).虽然时间戳将是唯一的,但它们不会精确地测量滴答.@MusiGenesis方法将提供更精确的测量. (2认同)

Jam*_*zor 12

这些建议看起来都太难了!如果您使用的是Windows 8或Server 2012或更高版本,请使用GetSystemTimePreciseAsFileTime以下命令:

[DllImport("Kernel32.dll", CallingConvention = CallingConvention.Winapi)]
static extern void GetSystemTimePreciseAsFileTime(out long filetime);

public DateTimeOffset GetNow()
{
    long fileTime;
    GetSystemTimePreciseAsFileTime(out fileTime);
    return DateTimeOffset.FromFileTime(fileTime);
}
Run Code Online (Sandbox Code Playgroud)

这比DateTime.Now没有任何努力有更多,更好的准确性.

有关详细信息,请参阅MSDN:http://msdn.microsoft.com/en-us/library/windows/desktop/hh706895(v = vs.85).aspx

  • 当然,所有这些答案都是在Windows 8/Server 2012甚至存在之前编写的.即使在今天,对于大多数代码而言,假设客户拥有8/2012并不是一个好的假设. (3认同)

Jas*_*aty 8

它确实返回操作系统已知的最准确的日期和时间.

操作系统还提供更高分辨率的时间QueryPerformanceCounterQueryPerformanceFrequency(.NET Stopwatch类).这些让你计时间隔,但不给你一天的日期和时间.你可能会争辩说,这些能够给你一个非常准确的时间和日期,但我不确定他们在很长一段时间内有多么糟糕.