C # DateTime.Now precisione


97

Ho appena riscontrato un comportamento imprevisto con DateTime.UtcNow mentre eseguivo alcuni test unitari. Sembra che quando chiami DateTime.Now/UtcNow in rapida successione, sembra che ti restituisca lo stesso valore per un intervallo di tempo più lungo del previsto, piuttosto che acquisire incrementi di millisecondi più precisi.

So che esiste una classe Stopwatch che sarebbe più adatta per eseguire misurazioni del tempo precise, ma ero curioso se qualcuno potesse spiegare questo comportamento in DateTime? Esiste una precisione ufficiale documentata per DateTime.Now (ad esempio, precisa entro 50 ms?)? Perché DateTime.Now dovrebbe essere reso meno preciso di quello che la maggior parte dei clock della CPU potrebbe gestire? Forse è progettato solo per la CPU con il minimo comune denominatore?

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();
}

Qual è l'intervallo durante il quale ritorni lo stesso valore?
ChrisF


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

Dovrei dire, il ciclo è stato eseguito un sacco di volte, ma ho visto solo 3 valori distinti ...
Andy White

Per chiunque venga qui, ecco il TL; DR // Usa la funzione QueryPerformanceCounter "Recupera il valore corrente del contatore delle prestazioni, che è un timestamp ad alta risoluzione (<1us) che può essere utilizzato per le misurazioni dell'intervallo di tempo." (Per il codice gestito, la classe System.Diagnostics.Stopwatch utilizza QPC come base il tempo preciso.) Msdn.microsoft.com/en-us/library/windows/desktop/...
AnotherUser

Risposte:


180

Perché DateTime.Now dovrebbe essere reso meno preciso di quello che la maggior parte dei clock della CPU potrebbe gestire?

Un buon orologio dovrebbe essere sia preciso che accurato ; quelli sono diversi. Come recita la vecchia barzelletta, un orologio fermo è esattamente preciso due volte al giorno, un orologio lento al minuto 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 dovrebbe essere preciso , diciamo un microsecondo, quando non può essere preciso al microsecondo? La maggior parte delle persone non ha alcuna fonte per i segnali orari ufficiali che siano precisi al microsecondo. Quindi dare sei cifre dopo la cifra decimale di precisione , le ultime cinque delle quali sono spazzatura sarebbe mentire .

Ricorda, lo scopo di DateTime è rappresentare una data e un'ora . I tempi ad alta precisione non sono affatto lo scopo di DateTime; come noti, questo è lo scopo di StopWatch. Lo scopo di DateTime è rappresentare una data e un'ora per scopi quali la visualizzazione dell'ora corrente all'utente, il calcolo del numero di giorni fino al martedì successivo e così via.

Insomma, "che ore sono?" e "quanto tempo 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 caro sull'argomento della differenza tra "precisione" e "accuratezza": blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx
jason

3
Ok, buon punto sulla precisione e l'accuratezza. Immagino di non accettare ancora l'affermazione che DateTime non è accurato perché "non deve esserlo". Se ho un sistema transazionale e voglio contrassegnare un datetime per ogni record, a me sembra intuitivo usare la classe DateTime, ma sembra che ci siano componenti temporali più precisi / precisi in .NET, quindi perché DateTime dovrebbe essere reso meno capace. Immagino che dovrò leggere ancora un po '...
Andy White

11
OK @ Anddy, supponi di avere un tale sistema. Su una macchina contrassegni una transazione come avvenuta il 1 ° gennaio, 12: 34: 30.23498273. Su un'altra macchina nel tuo cluster contrassegni una transazione come avvenuta il 1 ° gennaio, 12: 34: 30.23498456. Quale transazione è avvenuta per prima? A meno che tu non sappia che gli orologi delle due macchine sono sincronizzati entro un microsecondo l'uno dall'altro, non hai idea di quale sia avvenuto per primo. La precisione extra è fuorviante spazzatura . Se avessi fatto a modo mio, tutti i DateTimes sarebbero arrotondati al secondo più vicino, come erano in VBScript.
Eric Lippert,

14
non che questo risolverebbe il problema che hai menzionato, dal momento che i PC medi non sincronizzati di solito sono fuori da pochi minuti . Ora, se l'arrotondamento a 1 non risolve nulla, perché arrotondare? In altre parole, non seguo la tua argomentazione sul motivo per cui i valori assoluti dovrebbero avere una precisione minore dell'accuratezza delle misurazioni del tempo delta.
Roman Starkov

3
Diciamo che sto creando un registro delle attività che richiede (1) sapere quando si è verificato qualcosa in termini di spazio del calendario (entro pochi secondi) (2) conoscere molto esattamente la spaziatura tra gli eventi (entro circa 50 millisecondi). Sembra che la scommessa più sicura per questo sarebbe usare DateTime.Now per il timestamp della prima azione, quindi utilizzare un cronometro per le azioni successive per determinare l'offset dal DateTime iniziale. È questo l'approccio che consiglieresti, Eric?
devuxer

18

La precisione di DateTime è in qualche modo specifica del sistema su cui viene eseguito. La precisione è correlata alla velocità di un cambio di contesto, che tende a essere di circa 15 o 16 ms. (Sul mio sistema, sono in realtà circa 14 ms dal mio test, ma ho visto alcuni laptop con una precisione più vicina a 35-40 ms.)

Peter Bromberg ha scritto un articolo sulla temporizzazione del codice ad alta precisione in C #, che discute questo.


2
Le 4 macchine Win7 che ho avuto nel corso degli anni avevano tutte una precisione di circa 1 ms. Now () sleep (1) Now () ha sempre comportato un cambiamento di ~ 1 ms in datetime durante il test.
Bengie

Vedo anche la precisione di ~ 1 ms.
Timo

12

Vorrei un Datetime preciso.Now :), quindi ho preparato questo:

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
Mi piace questa soluzione, ma non ne ero sicuro, quindi ho posto la mia domanda ( stackoverflow.com/q/18257987/270348 ). Secondo il commento / risposta di Servy, non dovresti mai resettare il cronometro.
RobSiklos

1
Forse non nel tuo contesto, ma il ripristino ha senso nel mio contesto: mi assicuro solo che sia fatto prima che il tempo inizi effettivamente.
Jimmy

Non è necessario l'abbonamento e non è necessario azzerare il cronometro. L'esecuzione di questo codice ogni ~ 10 ms non è necessaria e consuma CPU. E questo codice non è affatto thread-safe. Basta inizializzare myStopwatchStartTime = DateTime.UtcNow; una volta, nel costruttore statico.
VeganHunter

1
@VeganHunter Non sono sicuro di aver capito bene il tuo commento, ma sembra che tu pensi che TimeChanged venga chiamato ogni ~ 10 ms? Non è così.
Jimmy

@ Jimmy, hai ragione. Colpa mia, ho frainteso il codice. L'evento SystemEvents.TimeChanged viene chiamato solo se l'utente modifica l'ora di sistema. È un evento raro.
VeganHunter

6

Da MSDN scoprirai che DateTime.Nowha una risoluzione approssimativa di 10 millisecondi su tutti i sistemi operativi NT.

La precisione effettiva dipende dall'hardware. Una migliore precisione può essere ottenuta utilizzando QueryPerformanceCounter.


5

Per quel che vale, a meno di controllare effettivamente la fonte .NET, Eric Lippert ha fornito un commento su questa domanda SO dicendo che DateTime è accurato solo a circa 30 ms. Il motivo per non essere accurato al nanosecondo, secondo le sue parole, è che "non è necessario che lo sia."


4
E potrebbe essere peggio. In VBScript la funzione Now () arrotonda il risultato restituito al secondo più vicino, nonostante il fatto che il valore restituito abbia una precisione disponibile sufficiente per essere preciso al microsecondo. In C #, la struttura si chiama DateTime; ha lo scopo di rappresentare una data e un orario per i tipici domini non scientifici del mondo reale, come quando scade la tua assicurazione sulla vita o quanto tempo è passato dal tuo ultimo riavvio. Non è inteso per tempi inferiori ai secondi di alta precisione.
Eric Lippert,

3

Dalla documentazione MSDN :

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

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


1

La risoluzione di questa proprietà dipende dal timer di sistema, che dipende dal sistema operativo sottostante. Tende ad essere compreso tra 0,5 e 15 millisecondi.

Di conseguenza, le chiamate ripetute alla proprietà Now in un breve intervallo di tempo, ad esempio in un ciclo, possono restituire lo stesso valore.

MSDN Link

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.