C#DateTime.Now精度


97

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

我知道有一个秒表类更适合进行精确的时间测量,但是我很好奇是否有人可以在DateTime中解释这种行为?是否有记录DateTime.Now的官方精度(例如,精度在50毫秒内?)?为什么将DateTime.Now设置为比大多数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();
}

您返回相同值的间隔是多少?
克里斯·弗(ChrisF)


当我运行上面的代码时,我仅获得3个滴答和毫秒的唯一值,最后的秒表计时时间为147 ms,因此看来在我的机器上,它的精确度仅为50ms左右……
Andy White

我应该说,循环运行了很多次,但是我只看到了3个不同的值……
Andy White 2010年

对于任何来这里的人,这里都是TL; DR //使用QueryPerformanceCounter函数“检索性能计数器的当前值,这是可用于时间间隔测量的高分辨率(<1us)时间戳。” (对于托管代码中,System.Diagnostics.Stopwatch类使用QPC作为其精确的时间基准。) msdn.microsoft.com/en-us/library/windows/desktop/...
AnotherUser

Answers:


181

为什么将DateTime.Now设置为比大多数CPU时钟所能处理的精度低?

一个好的时钟应该既精确准确 ; 那些是不同的。就像老玩笑所说的那样,停下来的时钟每天精确地是两次,而慢一分钟的时钟在任何时候都永远不会是精确的。但是慢一分钟的时钟总是精确到最接近的分钟,而停止的时钟根本没有任何有用的精度。

为什么DateTime可能无法精确到一个微秒,而它可能无法精确到微秒呢?大多数人没有任何精确到微秒的官方时间信号源。因此给予小数点后六位数字精度,其在过去五年中,有垃圾撒谎

请记住,DateTime的目的是代表日期和时间。高精度计时完全不是DateTime的目的。如您所述,这就是StopWatch的目的。DateTime的目的是代表一个日期和时间,例如向用户显示当前时间,计算到下一个星期二的天数等等。

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

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


2
@埃里克·利珀特(Eric Lippert):雷蒙德·陈(Raymond Chen)对于“精度”与“准确性”之间的区别这一话题非常老套,但也很不错:blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx
杰森

3
好的,关于精度与精度的要点。我猜我还是不太同意DateTime不准确的说法,因为“不一定要如此”。如果我有一个事务处理系统,并且想为每个记录标记一个日期时间,对我来说使用DateTime类似乎很直观,但是.NET中似乎有更准确/更精确的时间组件,所以为什么DateTime是使能力下降。我想我需要做更多的阅读...
安迪·怀特2010年

11
好的,@安迪,假设您有这样一个系统。在一台计算机上,您将一笔交易标记为发生在1月1日12:34:30.23498273。在群集中的另一台计算机上,将事务标记为发生在1月1日12:34:30.23498456。首先发生哪笔交易?除非您知道两台机器的时钟彼此同步在一毫秒内,否则您不知道哪一个先发生。额外的精度会误导垃圾。如果我愿意的话,所有的DateTime都会四舍五入到最接近的秒,就像在VBScript中一样。
埃里克·利珀特

14
并不是说这可以解决您提到的问题,因为平均而言,未同步的PC通常只有几分钟的时间。现在,如果舍入到1不能解决任何问题,那为什么还要舍入呢?换句话说,为什么绝对值的精度要比增量时间测量的精度小,我不接受您的论点。
罗曼·斯塔科夫

3
假设我正在创建一个活动日志,该日志要求(1)根据日历空间(几秒钟之内)知道什么时候发生(2)非常准确地知道事件之间的间隔(50毫秒左右)。听起来最安全的选择是使用DateTime.Now作为第一个动作的时间戳,然后使用Stopwatch进行后续动作以确定与初始DateTime的偏移量。您会建议这种方法吗,埃里克?
devuxer

18

DateTime的精度在某种程度上取决于运行它的系统。精度与上下文切换的速度有关,该速度通常约为15或16 ms。(在我的系统上,实际上距离测试大约14毫秒,但是我看到一些笔记本电脑的精度接近35-40毫秒。)

Peter Bromberg 在C#中写了一篇有关高精度代码定时的文章,对此进行了讨论。


2
多年来,我拥有的4台Win7机器都具有大约1毫秒的精度。Now()sleep(1)Now()在测试时总是导致日期时间〜1ms的变化。

我也看到了〜1ms的精度。
Timo

12

我想要一个精确的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
我喜欢这种解决方案,但不确定,所以我问了自己一个问题(stackoverflow.com/q/18257987/270348)。根据Servy的评论/答案,您永远不要重置秒表。
RobSiklos

1
也许不是在您的上下文中,但在我的上下文中重置才有意义-我只是确保在计时真正开始之前完成它。
吉米

您不需要订阅,也不需要重置秒表。不必每隔10毫秒运行一次此代码,这会占用CPU。而且此代码根本不是线程安全的。只需初始化myStopwatchStartTime = DateTime.UtcNow; 一次,在静态构造函数中。
VeganHunter

1
@VeganHunter我不确定我是否理解您的评论正确,但是您似乎认为TimeChanged每隔10ms就会被调用一次?没有。
吉米

@吉米,你是对的。不好,我误解了代码。仅当用户更改系统时间时,才调用SystemEvents.TimeChanged事件。这是罕见的事件。
VeganHunter


5

值得一提的是,除了实际检查.NET源代码外,埃里克·利珀特(Eric Lippert)就此SO问题提供了一条评论,指出DateTime仅精确到大约30 ms。用他的话说,之所以不能精确到纳秒,是因为它“不需要如此”。


4
而且情况可能更糟。在VBScript中,Now()函数将返回的结果四舍五入到最接近的秒,这是因为这样的事实,即返回的值具有足够的可用精度,可以精确到微秒。在C#中,该结构称为DateTime。它旨在代表典型的现实世界非科学领域的日期和时间,例如您的人寿保险到期或自上次重启以来已经存在了多长时间。不适用于高精度的亚秒级计时。
埃里克·利珀特


1

此属性的分辨率取决于系统计时器,该计时器取决于基础操作系统。它通常在0.5到15毫秒之间。

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

MSDN链接

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.