【问题标题】:C# DateTime.Now precisionC# DateTime.Now 精度
【发布时间】:2011-01-09 17:33:42
【问题描述】:

我刚刚在进行一些单元测试时遇到了 DateTime.UtcNow 的一些意外行为。看来,当您快速连续调用 DateTime.Now/UtcNow 时,它似乎会在比预期更长的时间间隔内返回相同的值,而不是捕获更精确的毫秒增量。

我知道有一个更适合进行精确时间测量的 Stopwatch 类,但我很好奇是否有人可以在 DateTime 中解释这种行为?是否记录了 DateTime.Now 的官方精度(例如,精确到 50 毫秒?)?为什么 DateTime.Now 的精度低于大多数 CPU 时钟可以处理的精度?也许它只是为最低公分母 CPU 设计的?

public static void Main(string[] args)
{
    var stopwatch = new Stopwatch();
    stopwatch.Start();
    for (int i=0; i<1000; i++)
    {
        var now = DateTime.Now;
        Console.WriteLine(string.Format(
            "Ticks: {0}\tMilliseconds: {1}", now.Ticks, now.Millisecond));
    }

    stopwatch.Stop();
    Console.WriteLine("Stopwatch.ElapsedMilliseconds: {0}",
        stopwatch.ElapsedMilliseconds);

    Console.ReadLine();
}

【问题讨论】:

  • 你得到相同值的时间间隔是多少?
  • 当我运行上面的代码时,我只得到了 3 个唯一的刻度和毫秒值,最后的秒表时间是 147 毫秒,所以在我的机器上它似乎只精确到 50 毫秒左右。 ..
  • 我应该说,循环运行了很多次,但我只看到了 3 个不同的值...
  • 对于任何来这里的人,这里是 TL;DR// 使用 QueryPerformanceCounter 函数“检索性能计数器的当前值,这是一个可以使用的高分辨率(msdn.microsoft.com/en-us/library/windows/desktop/…

标签: c# .net datetime precision time-precision


【解决方案1】:

对于它的价值,Eric Lippert 没有实际检查 .NET 源代码,而是提供了a comment on this SO question,说 DateTime 仅精确到大约 30 毫秒。用他的话来说,不精确到纳秒的原因是它“不需要”。

【讨论】:

  • 而且情况可能会更糟。在 VBScript 中,Now() 函数将返回的结果四舍五入到最接近的秒,尽管返回的值具有足够的可用精度以精确到微秒这一事实。在 C# 中,该结构称为 DateTime;它旨在代表典型的现实世界非科学领域的日期和时间,例如您的人寿保险何时到期或自上次重新启动以来已经过了多长时间。它不适用于高精度的亚秒级计时。
【解决方案2】:

这个属性的分辨率取决于系统定时器,它 取决于底层操作系统。它往往在 0.5 之间 和 15 毫秒。

因此,在短时间内(例如在循环中)重复调用 Now 属性可能会返回相同的值。

MSDN Link

【讨论】:

    【解决方案3】:

    DateTime 的精度在一定程度上取决于运行它的系统。精度与上下文切换的速度有关,通常在 15 或 16 毫秒左右。 (在我的系统上,我的测试实际上大约需要 14 毫秒,但我看到一些笔记本电脑的准确度接近 35-40 毫秒。)

    Peter Bromberg 用 C# 编写了 an article on high precision code timing,其中讨论了这一点。

    【讨论】:

    • 我这几年用过的 4 台 Win7 机器,精度都在 1ms 左右。 Now() sleep(1) Now() 在我测试时总是导致日期时间发生约 1 毫秒的变化。
    【解决方案4】:

    From MSDN documentation:

    这个属性的分辨率 取决于系统计时器。

    他们还声称 Windows NT 3.5 及更高版本的近似分辨率为 10 毫秒 :)

    【讨论】:

      【解决方案5】:

      我想要一个精确的 Datetime.Now :),所以我做了这个:

      public class PreciseDatetime
      {
          // using DateTime.Now resulted in many many log events with the same timestamp.
          // use static variables in case there are many instances of this class in use in the same program
          // (that way they will all be in sync)
          private static readonly Stopwatch myStopwatch = new Stopwatch();
          private static System.DateTime myStopwatchStartTime;
      
          static PreciseDatetime()
          {
              Reset();
      
              try
              {
                  // In case the system clock gets updated
                  SystemEvents.TimeChanged += SystemEvents_TimeChanged;
              }
              catch (Exception)
              {                
              }
          }
      
          static void SystemEvents_TimeChanged(object sender, EventArgs e)
          {
              Reset();
          }
      
          // SystemEvents.TimeChanged can be slow to fire (3 secs), so allow forcing of reset
          static public void Reset()
          {
              myStopwatchStartTime = System.DateTime.Now;
              myStopwatch.Restart();
          }
      
          public System.DateTime Now { get { return myStopwatchStartTime.Add(myStopwatch.Elapsed); } }
      }
      

      【讨论】:

      • 我喜欢这个解决方案,但我不确定,所以我问了我自己的问题 (stackoverflow.com/q/18257987/270348)。根据 Servy 的评论/回答,您永远不应该重置秒表。
      • 也许不是你的上下文,但在我的上下文中重置是有意义的——我只是确保它在时间实际开始之前完成。
      • 您不需要订阅,也不需要重置秒表。不需要每 ~ 10 毫秒运行一次此代码,并且会消耗 CPU。而且这段代码根本不是线程安全的。只需初始化 myStopwatchStartTime = DateTime.UtcNow;一次,在静态构造函数中。
      • @VeganHunter 我不确定我是否理解您的评论,但您似乎认为 TimeChanged 每隔约 10 毫秒就会被调用一次?它没有。
      • @Jimmy,你是对的。我的错,我误解了代码。仅当用户更改系统时间时才调用 SystemEvents.TimeChanged 事件。这是一个罕见的事件。
      【解决方案6】:

      为什么 DateTime.Now 的精度低于大多数 CPU 时钟可以处理的精度?

      一个好的时钟应该是精确准确;那些是不同的。正如老笑话所说,停止的时钟一天准确两次,慢一分钟的时钟在任何时候都不准确。但是慢一分钟的时钟总是精确到最近的分钟,而停止的时钟根本没有有用的精度。

      当 DateTime 不可能准确 到微秒时,为什么要精确 到微秒?大多数人没有任何精确到微秒的官方时间信号来源。因此在precision的小数点后给出六位,最后五位是垃圾将是lying

      请记住,DateTime 的目的是表示日期和时间。高精度计时根本不是 DateTime 的目的。正如您所注意到的,这就是 StopWatch 的目的。 DateTime 的目的是表示日期和时间,用于向用户显示当前时间、计算距离下周二的天数等。

      简而言之,“现在几点了?” “这需要多长时间?”是完全不同的问题;不要使用旨在回答一个问题的工具来回答另一个问题。

      感谢您的提问;这将是一篇很好的博客文章! :-)

      【讨论】:

      • @Eric Lippert:Raymond Chen 在“精确度”和“准确度”之间的区别这个话题上有一个老手但很好:blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx
      • 好的,关于精度与准确性的好点。我想我仍然不相信 DateTime 不准确的说法,因为“它不一定是”。如果我有一个事务系统,并且我想为每条记录标记一个日期时间,对我来说,使用 DateTime 类似乎很直观,但似乎 .NET 中有更准确/精确的时间组件,那么为什么 DateTime 会是变得不那么有能力了。我想我得再读点书了……
      • 好的@Andy,假设你有这样的系统。在一台机器上,您将事务标记为发生在 1 月 1 日 12:34:30.23498273。在集群中的另一台机器上,您将事务标记为发生在 1 月 1 日 12:34:30.23498456。哪个交易先发生?除非您知道两台机器的时钟彼此同步在一微秒内,否则您不知道哪一个先发生。额外的精度是误导性垃圾。如果我按照自己的方式进行,所有 DateTime 都将四舍五入到最接近的秒数,就像在 VBScript 中一样。
      • 并不是说这会解决您提到的问题,因为平均未同步的 PC 通常会延迟 分钟。现在,如果四舍五入到 1 不能解决任何问题,那为什么还要四舍五入呢?换句话说,我不同意你的论点,为什么绝对值的精度应该小于增量时间测量的精度。
      • 假设我正在创建一个活动日志,该日志需要 (1) 知道在日历空间方面什么时候发生(几秒钟内)(2) 非常准确地知道事件之间的间隔(在 50大约几毫秒)。听起来最安全的选择是使用 DateTime.Now 作为第一个操作的时间戳,然后使用 Stopwatch 进行后续操作以确定与初始 DateTime 的偏移量。这是你会建议的方法吗,埃里克?
      【解决方案7】:

      MSDN,您会发现DateTime.Now 在所有 NT 操作系统上的近似分辨率为 10 毫秒。

      实际精度取决于硬件。使用QueryPerformanceCounter可以获得更好的精度。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-13
        • 2018-11-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多