2010-01-26 9 views
74

Ho appena eseguito un comportamento imprevisto con DateTime.UtcNow mentre eseguivo alcuni test unitari. Sembra che quando si chiama DateTime.Now/UtcNow in rapida successione, sembra che si restituisca lo stesso valore per un intervallo di tempo più lungo del previsto, invece di acquisire incrementi di millisecondo più precisi.C# DateTime.Ora precisione

So che esiste una classe di cronometro che sarebbe più adatta per eseguire misurazioni precise del tempo, ma ero curioso se qualcuno potesse spiegare questo comportamento in DateTime? Esiste una precisione ufficiale documentata per DateTime.Ora (ad esempio, precisa entro 50   ms?)? Perché DateTime.Ora sarà reso meno preciso di quello che potrebbe gestire la maggior parte degli orologi della CPU? Forse è solo progettato per il minimo comune denominatore della 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(); 
} 
+0

Qual è l'intervallo durante il quale si ottiene indietro lo stesso valore? – ChrisF

+3

Leggi http://stackoverflow.com/questions/307582/how-frequent-is-datetime-now-updated-or-is-there-a-more-precise-api-to-get-the –

+0

Quando ho eseguito il sopra codice, ho ottenuto solo 3 valori unici per tick e millisecondi, e il tempo di cronometro finale era di 147 ms, quindi sembra che sulla mia macchina è solo preciso a circa 50ms ... –

risposta

148

Perché DataTime.Ora essere reso meno preciso di quello che potrebbe gestire la maggior parte degli orologi della CPU?

Un buon orologio dovrebbe essere sia precisa e accurata; quelli sono diversi Come dice la vecchia battuta, un orologio fermo è esattamente preciso due volte al giorno, un orologio al minuto lento non è mai preciso in qualsiasi momento. Ma l'orologio un minuto lento è sempre preciso al minuto più vicino, mentre un orologio fermo non ha alcuna precisione utile.

Perché il DateTime essere preciso, per dire un microsecondo quando non può eventualmente essere accurata al microsecondo? La maggior parte delle persone non ha alcuna fonte per i segnali orari ufficiali che siano precisi al microsecondo.Dando quindi sei cifre dopo la cifra decimale della precisione , le ultime cinque delle quali sono immondizia sarebbe giacente.

Ricordare che lo scopo di DateTime è rappresentare una data e un'ora. I tempi di alta precisione non hanno affatto lo scopo di DateTime; come si nota, questo è lo scopo di StopWatch. Lo scopo di DateTime è rappresentare una data e un'ora per scopi come visualizzare l'ora corrente all'utente, calcolare il numero di giorni fino al martedì successivo e così via.

In breve, "che ore sono?" e "quanto ci è voluto?" sono domande completamente diverse; non utilizzare uno strumento progettato per rispondere a una domanda per rispondere all'altra.

Grazie per la domanda; questo sarà un buon articolo del blog! :-)

+2

@Eric Lippert: Raymond Chen ha un vecchio, ma tesoro dell'argomento della differenza tra "precisione" e "precisione": http://blogs.msdn.com/oldnewthing/archive/2005/09/02/459952. aspx – jason

+1

Ok, buon punto di precisione rispetto alla precisione. Suppongo che ancora non compro la dichiarazione che DateTime non è accurato perché "non deve essere". Se ho un sistema transazionale e voglio segnare un datetime per ogni record, a me sembra intuitivo usare la classe DateTime, ma sembra che ci siano componenti più precisi e precisi in .NET, quindi perché DateTime dovrebbe essere reso meno capace. Immagino che dovrò fare ancora un po 'di lettura ... –

+9

OK @Andy, supponiamo che tu abbia un tale sistema. Su una macchina contrassegni una transazione come avvenuta al 1 gennaio 12: 34: 30.23498273. Su un'altra macchina nel tuo cluster contrassegni una transazione come avvenuta al 1 gennaio 12: 34: 30.23498456. Quale transazione si è verificata per prima? A meno che tu non sappia che le due macchine orologi sono sincronizzate con un microsecondo l'una dall'altra, non hai idea di quale sia la prima. La precisione extra è * immondizia spazzatura *. Se avessi la mia strada, tutti i DateTimes sarebbero arrotondati al secondo più prossimo, come lo erano in VBScript. –

2

From MSDN documentation:

La risoluzione di questa proprietà dipende dal timer di sistema.

Essi affermano inoltre che la risoluzione approssimativa su Windows NT 3.5 e versioni successive è di 10 ms   :)

5

Per quel che vale, a corto di realtà controllando la fonte .NET, Eric Lippert fornito un commento su this SO question dicendo che DateTime ha una precisione di circa 30 ms. Il ragionamento per non essere accurato al nanosecondo, nelle sue parole, è che "non ha bisogno di essere".

+2

E potrebbe essere peggio. In VBScript la funzione Now() arrotonda il risultato restituito al secondo più vicino, despie il fatto che il valore restituito abbia una precisione disponibile sufficiente per essere precisi al microsecondo. In C#, la struttura si chiama DateTime; è inteso a rappresentare una data e un'ora per i tipici domini non scientifici del mondo reale, come quando scade la tua assicurazione sulla vita o da quanto tempo è trascorso dal tuo ultimo riavvio. Non è inteso per il cronometraggio del secondo tempo ad alta precisione. –

13

La precisione di DateTime è in qualche modo specifica per il sistema su cui viene eseguito. La precisione è correlata alla velocità di un interruttore di contesto, che tende ad essere intorno a 15 o 16   ms. (Sul mio sistema, in realtà è di circa 14 ms   dal mio test, ma ho visto alcuni computer portatili in cui è più vicino a 35-40 ms   precisione.)

Peter Bromberg ha scritto an article on high precision code timing in C#, che lo discute.

+1

Le 4 macchine Win7 che ho avuto nel corso degli anni hanno avuto all'incirca una precisione di 1ms. Now() sleep (1) Now() ha sempre comportato un cambiamento di ~ 1ms in datetime durante il test. – Bengie

+0

Vedo anche la precisione di ~ 1ms. – Timo

2

A partire da MSDN troverete che DateTime.Now ha una risoluzione di approssimativa di 10 millisecondi su tutti i sistemi operativi NT.

La precisione effettiva dipende dall'hardware. È possibile ottenere una migliore precisione utilizzando QueryPerformanceCounter.

9

Vorrei una Datetime.Now precisa :), così ho cucinato questo in su:

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); } } 
} 
+1

Mi piace questa soluzione, ma non ne ero sicuro, quindi ho fatto la mia domanda (http://stackoverflow.com/q/18257987/270348). Come da commento/risposta di Servy, non dovresti mai resettare il cronometro. – RobSiklos

+1

Forse non nel tuo contesto, ma la reimpostazione ha senso nel mio contesto - mi limito ad assicurarmi che sia fatta prima che il tempo inizi effettivamente. – Jimmy

Problemi correlati