C# DateTime.Now 精度

C# DateTime.Now precision

提问人:Andy White 提问时间:1/27/2010 最后编辑:Peter MortensenAndy White 更新时间:6/1/2020 访问量:64716

问:

我只是在进行一些单元测试时遇到了一些意外的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();
}
C# .NET 日期 时间精度

评论

0赞 ChrisF 1/27/2010
您获得相同值的时间间隔是多久?
3赞 Dan Diplo 1/27/2010
阅读 stackoverflow.com/questions/307582/...
0赞 Andy White 1/27/2010
当我运行上面的代码时,我只得到了 3 个唯一的滴答声和毫秒值,最终秒表时间为 147 毫秒,因此在我的机器上,它似乎只能精确到 50 毫秒左右......
0赞 Andy White 1/27/2010
我应该说,循环运行了很多次,但我只看到了 3 个不同的值......
0赞 AnotherUser 6/4/2014
对于任何来到这里的人来说,这里是 TL;DR// 使用 QueryPerformanceCounter 函数“检索性能计数器的当前值,该值是可用于时间间隔测量的高分辨率 (<1us) 时间戳。(对于托管代码,System.Diagnostics.Stopwatch 类使用 QPC 作为其精确时间基础。msdn.microsoft.com/en-us/library/windows/desktop/......

答:

3赞 Tomas Vana 1/27/2010 #1

来自 MSDN 文档

此属性的解析 取决于系统计时器。

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

8赞 Scott Arrington 1/27/2010 #2

值得一提的是,除了实际检查 .NET 源代码之外,Eric Lippert 还对这个 SO 问题发表了评论,称 DateTime 仅精确到大约 30 毫秒。用他的话说,纳秒级精度不高的原因是它“不需要”。

评论

4赞 Eric Lippert 1/27/2010
而且情况可能会更糟。在 VBScript 中,Now() 函数将返回的结果四舍五入到最接近的秒数,从而表明返回的值具有足够的可用精度以精确到微秒。在 C# 中,该结构称为 DateTime;它旨在表示典型的现实世界非科学领域的日期和时间,例如您的人寿保险何时到期或自上次重启以来已经过去了多长时间。它不适用于高精度亚秒级计时。
19赞 Reed Copsey 1/27/2010 #3

DateTime 的精度在某种程度上特定于运行它的系统。精度与上下文切换的速度有关,它往往在 15 或 16 毫秒左右(在我的系统上,实际上与我的测试相比大约是 14 毫秒,但我见过一些笔记本电脑的精度接近 35-40 毫秒。

Peter Bromberg 写了一篇关于 C# 中高精度代码时序的文章,讨论了这一点。

评论

2赞 Bengie 5/15/2015
多年来,我拥有的 4 台 Win7 机器的精度都约为 1 毫秒。Now() sleep(1) Now() 在我测试时总是导致日期时间的 ~1ms 变化。
6赞 Kevin Montrose 1/27/2010 #4

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

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

212赞 Eric Lippert 1/27/2010 #5

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

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

为什么 DateTime 要精确到微秒,比如说一微秒,而它不可能精确到微秒?大多数人没有任何精确到微秒的官方时间信号的来源。因此,在精度的小数点后给出六位数字,其中最后五位是垃圾,这将是在撒谎

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

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

谢谢你的问题;这将是一篇很好的博客文章!:-)

评论

2赞 jason 1/27/2010
@Eric Lippert:Raymond Chen 在“精确度”和“准确性”之间的区别这个话题上有一句老话,但很好听:blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx
3赞 Andy White 1/28/2010
好的,关于精度与准确性的好点。我想我仍然不太相信 DateTime 不准确的说法,因为“它不一定是”。如果我有一个事务系统,并且我想为每条记录标记一个日期时间,对我来说,使用 DateTime 类似乎很直观,但似乎 .NET 中有更准确/精确的时间组件,那么为什么 DateTime 的功能会降低。我想我得再读一些......
12赞 Eric Lippert 1/28/2010
好吧,@Andy,假设你确实有这样一个系统。在一台计算机上,将事务标记为发生在 1 月 1 日 12:34:30.23498273。在群集中的另一台计算机上,将事务标记为发生在 1 月 1 日 12:34:30.23498456。哪笔交易最先发生?除非您知道两台机器的时钟在一微秒内同步,否则您不知道哪一台先发生。额外的精度是误导性的垃圾。如果我有办法,所有 DateTime 都将四舍五入到最接近的秒,就像在 VBScript 中一样。
15赞 Roman Starkov 2/11/2011
并不是说这可以解决您提到的问题,因为平均不同步的 PC 通常会在几分钟内出现。现在,如果四舍五入到 1 不能解决任何问题,那么为什么要四舍五入呢?换句话说,我不遵循您关于为什么绝对值的精度应该小于增量时间测量精度的论点。
3赞 devuxer 8/7/2013
假设我正在创建一个活动日志,该日志需要 (1) 根据日历空间(在几秒钟内)知道何时发生某些事情(2) 非常准确地知道事件之间的间隔(在 50 毫秒左右)。听起来最安全的选择是使用 DateTime.Now 作为第一个操作的时间戳,然后使用秒表进行后续操作,以确定与初始 DateTime 的偏移量。这是你建议的方法吗,埃里克?
12赞 Jimmy 2/22/2013 #6

我想要一个精确的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); } }
}

评论

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

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

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

MSDN 链接