C # DateTime.Now точность

97

Я просто столкнулся с неожиданным поведением DateTime.UtcNow при выполнении некоторых модульных тестов. Похоже, что когда вы вызываете DateTime.Now/UtcNow в быстрой последовательности, он, кажется, возвращает вам то же значение для более длительного, чем ожидалось, интервала времени, а не фиксирует более точные приращения миллисекунды.

Я знаю, что есть класс Stopwatch, который лучше подходит для точного измерения времени, но мне было любопытно, может ли кто-нибудь объяснить это поведение в DateTime? Документирована ли официальная точность DateTime.Now (например, с точностью до 50 мс?)? Почему DateTime.Now должен быть менее точным, чем может обрабатывать большинство тактовых частот ЦП? Может, он просто предназначен для ЦП с наименьшим общим знаменателем?

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
Прочтите stackoverflow.com/questions/307582/…
Дэн Дипло
Когда я запустил приведенный выше код, я получил только 3 уникальных значения для тиков и миллисекунд, а окончательное время секундомера составило 147 мс, так что похоже, что на моей машине это только около 50 мс ...
Энди Уайт
Должен сказать, цикл запускался несколько раз, но я видел только 3 различных значения ...
Энди Уайт
Для тех, кто приходит сюда, вот TL; DR // Использование функции QueryPerformanceCounter «Извлекает текущее значение счетчика производительности, которое представляет собой отметку времени с высоким разрешением (<1 мкс), которую можно использовать для измерений временных интервалов». (Для управляемого кода, класс System.Diagnostics.Stopwatch использует QPC в качестве точного времени.) Msdn.microsoft.com/en-us/library/windows/desktop/...
AnotherUser

Ответы:

181

Почему DateTime.Now должен быть менее точным, чем может обрабатывать большинство тактовых частот ЦП?

Хорошие часы должны быть точными и точными ; это разные. Как гласит старая шутка, остановленные часы показывают точную точность дважды в день, а часы, замедляющие движение на минуту, никогда не будут точными в любое время. Но часы, отстающие на минуту, всегда имеют точность до ближайшей минуты, тогда как остановленные часы не имеют никакой полезной точности.

Почему DateTime должен быть точным , скажем, до микросекунды, если он не может быть точным до микросекунды? Большинство людей не имеют источников официальных сигналов времени с точностью до микросекунды. Поэтому давая шесть цифр после десятичного знака точности , последние пять из которых мусор будет лежа .

Помните, что цель DateTime - представлять дату и время . Высокоточная синхронизация вовсе не является целью DateTime; как вы заметили, это цель секундомера. Цель DateTime - представить дату и время для таких целей, как отображение текущего времени для пользователя, вычисление количества дней до следующего вторника и т. Д.

Короче говоря, "сколько времени?" и "сколько времени это заняло?" это совершенно разные вопросы; не используйте инструмент, предназначенный для ответа на один вопрос, чтобы ответить на другой.

Спасибо за вопрос; из этого получится хорошая статья в блоге! :-)

Эрик Липперт
источник
2
@ Эрик Липперт: У Раймонда Чена есть старое, но полезное решение по самой теме разницы между "точностью" и "точностью": blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx
Джейсон
3
Хорошо, хорошее замечание о точности и точности. Думаю, я до сих пор не верю утверждению, что DateTime неточен, потому что «это не обязательно». Если у меня есть транзакционная система, и я хочу отметить дату и время для каждой записи, мне кажется интуитивно понятным использование класса DateTime, но кажется, что в .NET есть более точные / точные компоненты времени, так почему DateTime будет сделал менее способным. Думаю, мне придется еще кое-что почитать ...
Энди Уайт
11
ОК @ Энди, предположим, у вас есть такая система. На одном компьютере вы помечаете транзакцию как произошедшую 1 января, 12:34: 30.23498273. На другой машине в вашем кластере вы помечаете транзакцию как произошедшую 1 января, 12:34: 30.23498456. Какая транзакция произошла первой? Если вы не знаете, что часы двух машин синхронизированы с точностью до микросекунды друг от друга, вы не знаете, какая из них возникла первой. Дополнительная точность - это мусор, вводящий в заблуждение . Если бы у меня был свой способ, все DateTimes были бы округлены до ближайшей секунды, как это было в VBScript.
Эрик Липперт
14
не то чтобы это решило проблему, о которой вы упомянули, поскольку в среднем несинхронизированные ПК обычно отключаются через несколько минут . Теперь, если округление до 1 ничего не решает, тогда зачем вообще округлять? Другими словами, я не понимаю вашего аргумента в пользу того, почему абсолютные значения должны иметь меньшую точность, чем точность измерений дельта-времени.
Роман Старков
3
Скажем, я создаю журнал активности, который требует (1) знать, когда что-то произошло с точки зрения календарного пространства (в течение нескольких секунд) (2), очень точно знать интервал между событиями (в пределах 50 миллисекунд). Похоже, что самым безопасным вариантом для этого было бы использовать DateTime.Now для отметки времени первого действия, а затем использовать секундомер для последующих действий, чтобы определить смещение от начального DateTime. Вы бы посоветовали такой подход, Эрик?
devuxer
18

Точность DateTime зависит от системы, в которой он работает. Точность связана со скоростью переключения контекста, которая обычно составляет около 15 или 16 мс. (В моей системе это на самом деле около 14 мс по данным моего тестирования, но я видел некоторые ноутбуки, где точность приближается к 35-40 мс.)

Питер Бромберг написал статью о высокоточной синхронизации кода на C #, в которой обсуждает это.

Рид Копси
источник
2
Все 4 машины Win7, которые у меня были за эти годы, имели точность примерно 1 мс. Now () sleep (1) Now () всегда приводил к изменению даты и времени на ~ 1 мс, когда я тестировал.
Bengie
Я также вижу точность ~ 1 мс.
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 ). Согласно комментарию / ответу Серви, вам никогда не следует сбрасывать секундомер.
RobSiklos
1
Может быть, не в вашем контексте, но сброс имеет смысл в моем контексте - я просто убеждаюсь, что это сделано до того, как начнется отсчет времени.
Джимми
Вам не нужна подписка, и вам не нужно сбрасывать секундомер. Запуск этого кода каждые ~ 10 мс не требуется и требует использования ЦП. И этот код вообще не является потокобезопасным. Просто инициализируйте myStopwatchStartTime = DateTime.UtcNow; один раз в статическом конструкторе.
VeganHunter
1
@VeganHunter Я не уверен, правильно ли я понимаю ваш комментарий, но вы, кажется, думаете, что TimeChanged вызывается каждые ~ 10 мс? Это не так.
Джимми
@ Джимми, ты прав. Моя проблема, я неправильно понял код. Событие SystemEvents.TimeChanged вызывается, только если пользователь изменяет системное время. Это редкое событие.
VeganHunter
6

Из MSDN вы увидите , что DateTime.Nowесть приблизительное разрешение 10 миллисекунд на всех операционных системах NT.

Фактическая точность зависит от оборудования. Лучшую точность можно получить, используя QueryPerformanceCounter.

Кевин Монтроуз
источник
5

Что бы это ни стоило, если не считать фактической проверки источника .NET, Эрик Липперт прокомментировал этот вопрос SO, заявив, что DateTime имеет точность только примерно до 30 мс. Причина неточности в наносекундах, по его словам, заключается в том, что это «не обязательно».

Скотт Андерсон
источник
4
А могло быть и хуже. В VBScript функция Now () округляет возвращаемый результат до ближайшей секунды, несмотря на тот факт, что возвращаемое значение имеет достаточную доступную точность, чтобы быть точным до микросекунды. В C # эта структура называется DateTime; он предназначен для представления даты и времени для типичных реальных ненаучных доменов, например, когда истекает срок вашей страховки жизни или сколько времени прошло с момента вашей последней перезагрузки. Он не предназначен для высокоточного отсчета времени до секунды.
Эрик Липперт
3

Из документации MSDN :

Разрешение этого свойства зависит от системного таймера.

Еще они утверждают, что примерное разрешение на Windows NT 3.5 и новее составляет 10 мс :)

Томас Вана
источник
1

Разрешение этого свойства зависит от системного таймера, который зависит от базовой операционной системы. Обычно это от 0,5 до 15 миллисекунд.

В результате повторные вызовы свойства Now за короткий промежуток времени, например, в цикле, могут возвращать то же значение.

Ссылка MSDN

Иман
источник