C # DateTime. Тепер точність


97

Я просто зіткнувся з якоюсь несподіваною поведінкою з DateTime.UtcNow, виконуючи деякі модульні тести. Схоже, що коли ви швидко викликаєте DateTime.Now/UtcNow, здається, це повертає вам одне і те ж значення на більш тривалий час, ніж очікувалося, замість того, щоб отримувати більш точні кроки в мілісекундах.

Я знаю, що існує клас Секундомір, який більше підходить для точного вимірювання часу, але мені було цікаво, чи хтось може пояснити цю поведінку в 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 унікальних значення для кліщів і мілісекунд, а остаточний час секундоміра становив 147 мс, тож, здається, на моїй машині він становить лише близько 50 мс ...
Енді Уайт

Слід сказати, цикл запускався купу разів, але я бачив лише 3 різних значення ...
Енді Уайт

Для тих, хто приїжджає сюди, ось TL; DR // Використовуйте функцію QueryPerformanceCounter "Отримує поточне значення лічильника продуктивності, що є міткою часу з високою роздільною здатністю (<1us), яку можна використовувати для вимірювання інтервалу часу." (Для керованого коду клас System.Diagnostics.Stopwatch використовує QPC як точний часовий базис.) Msdn.microsoft.com/en-us/library/windows/desktop/…
AnotherUser

Відповіді:


180

Чому DateTime.Now стане менш точним, ніж те, що може справити більшість годинників процесора?

Хороший годинник повинен бути одночасно точним і точним ; ті різні. Як каже стара жарт, зупинений годинник є точно точним двічі на день, годинник на хвилину повільно ніколи не буває точним у будь-який час. Але годинник на хвилину повільно завжди точний з точністю до хвилини, тоді як зупинений годинник взагалі не має корисної точності.

Чому DateTime повинен бути точним , скажімо, мікросекунди, коли він не може бути точним до мікросекунди? У більшості людей немає жодного джерела офіційних часових сигналів, точних до мікросекунд. Тому, якщо дати шість цифр після коми після точності , останні п’ять з яких - сміття , лежатиме .

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

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

Дякую за питання; з цього вийде гарна стаття в блозі! :-)


2
@Eric Lippert: Raymond Chen має Олді , але позитивний герой на самому тему різниці між «точністю» і «точність»: blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx
Джейсоном

3
Добре, хороший момент щодо точності проти точності. Я думаю, я все ще не купую твердження, що DateTime не є точним, тому що "це не повинно бути". Якщо у мене є система транзакцій, і я хочу позначити час для кожного запису, мені здається інтуїтивно зрозумілим використання класу DateTime, але, здається, в .NET є більш точні / точні компоненти часу, то чому DateTime буде зробив менш здібними. Я думаю, мені доведеться ще трохи прочитати ...
Енді Уайт

11
Добре @Andy, припустимо, у вас є така система. На одній машині ви позначаєте транзакцію, яка відбулася 1 січня о 12:34: 30.23498273. На іншій машині у вашому кластері ви позначаєте транзакцію, що відбулася 1 січня, 12:34: 30.23498456. Яка операція відбулася першою? Якщо ви не знаєте, що дві годинникові машини синхронізовані з точністю до мікросекунди одна з одною, ви не уявляєте, яка з них сталася першою. Додаткова точність вводить в оману сміття . Якби я мав свій спосіб, усі DateTimes були б округлені з точністю до секунди, як це було у VBScript.
Eric Lippert,

14
не те, що це дозволить вирішити проблему, про яку ви згадали, оскільки середні несинхронізовані ПК зазвичай виходять за лічені хвилини . Тепер, якщо округлення до 1s нічого не вирішує, то навіщо взагалі округляти? Іншими словами, я не слідую вашому аргументу, чому абсолютні значення повинні мати меншу точність, ніж точність вимірювань дельта-часу.
Роман Старков

3
Скажімо, я створюю журнал активності, який вимагає (1) знати, коли щось трапилося з точки зору календаря (протягом декількох секунд) (2), знаючи дуже точно інтервал між подіями (протягом приблизно 50 мілісекунд). Здається, найбезпечнішою ставкою для цього було б використання DateTime. Тепер для позначки часу першої дії, а потім за допомогою секундоміра для подальших дій визначте зсув від початкового DateTime. Це такий підхід, який би ти порадив, Еріку?
devuxer

18

Точність DateTime дещо специфічна для системи, на якій вона працює. Точність пов'язана зі швидкістю перемикання контексту, яка, як правило, становить близько 15 або 16 мс. (У моїй системі це фактично близько 14 мс від мого тестування, але я бачив деякі ноутбуки, де це наближається до точності 35-40 мс.)

Пітер Бромберг написав статтю про високоточну синхронізацію коду в C #, де це обговорюється.


2
4 машини Win7, які я мав протягом багатьох років, мали приблизно 1 мс точності. Now () sleep (1) Now () завжди призводив до зміни дати і часу на ~ 1 мс, коли я тестував.
Бенгі,

Я також бачу точність ~ 1 мс.
Тимо

12

Я хотів би отримати точний Datetime. Зараз :), тому я приготував це:

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; він призначений для подання дати та часу для типових реальних ненаукових доменів, наприклад, коли закінчується термін дії вашого страхування життя або скільки часу минуло з моменту останньої перезавантаження. Він не призначений для високоточної синхронізації секунди.
Eric Lippert

3

З документації MSDN :

Роздільна здатність цієї властивості залежить від системного таймера.

Вони також стверджують, що приблизна роздільна здатність для Windows NT 3.5 та пізніших версій становить 10 мс :)


1

Роздільна здатність цієї властивості залежить від системного таймера, який залежить від базової операційної системи. Це, як правило, становить від 0,5 до 15 мілісекунд.

Як результат, повторні виклики властивості Now через короткий проміжок часу, наприклад, у циклі, можуть повернути те саме значення.

Посилання MSDN

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.