2010-01-26 17 views
74

Me encontré con un comportamiento inesperado con DateTime.UtcNow mientras hacía algunas pruebas unitarias. Parece que cuando llama a DateTime.Now/UtcNow en sucesión rápida, parece devolverle el mismo valor por un intervalo de tiempo mayor de lo esperado, en lugar de capturar incrementos de milisegundos más precisos.C# DateTime.Now precisión

Sé que hay una clase de Cronómetro que sería más adecuada para hacer mediciones de tiempo precisas, pero tenía curiosidad si alguien pudiera explicar este comportamiento en DateTime? ¿Existe una precisión oficial documentada para DateTime.Now (por ejemplo, precisa dentro de 50   ms?)? ¿Por qué DateTime.Now se haría menos preciso que lo que la mayoría de los relojes de CPU podrían manejar? ¿Tal vez está diseñado para la CPU con el mínimo común denominador?

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

¿Cuál es el intervalo durante el cual se obtiene de vuelta el mismo valor? – ChrisF

+3

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

+0

Cuando ejecuté el encima del código, obtuve solo 3 valores únicos para tics y milisegundos, y el tiempo final de detención del reloj fue de 147 ms, por lo que parece que en mi máquina solo es preciso alrededor de 50 ms ... –

Respuesta

148

¿Por qué DateTime.Now se haría menos preciso que lo que la mayoría de los relojes con CPU podían manejar?

Un buen reloj debe ser a la vez precisa y precisa; esos son diferentes Como dice la vieja broma, un reloj parado es exactamente exacto dos veces al día, un reloj de un minuto lento nunca es preciso en ningún momento. Pero el reloj un minuto lento siempre es preciso al minuto más cercano, mientras que un reloj parado no tiene precisión útil en absoluto.

¿Por qué el DateTime ser precisa a, digamos un microsegundo cuando no puede ser exacta al microsegundo? La mayoría de las personas no tienen ninguna fuente de señales horarias oficiales que sean precisas a un microsegundo.Por lo tanto, dando seis dígitos después del lugar decimal de precisión, los últimos cinco de los cuales son basura sería mintiendo.

Recuerde, el propósito de DateTime es representar una fecha y hora. Los tiempos de alta precisión no son el propósito de DateTime; como notas, ese es el propósito de StopWatch. El objetivo de DateTime es representar una fecha y una hora para fines como mostrar la hora actual al usuario, calcular el número de días hasta el próximo martes, y así sucesivamente.

En resumen, "¿qué hora es?" y "¿cuánto tiempo tomó eso?" son preguntas completamente diferentes; no use una herramienta diseñada para responder una pregunta para responder la otra.

Gracias por la pregunta; esto hará un buen artículo de blog! :-)

+2

@Eric Lippert: Raymond Chen tiene un viejo pero bueno sobre el tema de la diferencia entre "precisión" y "precisión": http://blogs.msdn.com/oldnewthing/archive/2005/09/02/459952. aspx – jason

+1

Bien, buen punto acerca de la precisión frente a la precisión. Supongo que todavía no compro la afirmación de que DateTime no es exacto porque "no tiene que ser así". Si tengo un sistema transaccional, y quiero marcar una fecha y hora para cada registro, para mí parece intuitivo usar la clase DateTime, pero parece que hay componentes de tiempo más precisos/precisos en .NET, ¿por qué sería DateTime? hecho menos capaz. Creo que tendré que leer un poco más ... –

+9

OK @Andy, supongamos que tienes un sistema así. En una máquina, marca una transacción como ocurrida el 1 de enero, 12: 34: 30.23498273. En otra máquina de su clúster, marca una transacción como ocurrida el 1 de enero, 12: 34: 30.23498456. ¿Qué transacción ocurrió primero? A menos que sepa que los relojes de las dos máquinas están sincronizados en un microsegundo uno con el otro, no tiene idea de cuál ocurrió primero. La precisión adicional es * basura engañosa *. Si me salía con la mía, todos los DateTimes se redondearían al segundo más cercano, como lo estaban en VBScript. –

2

From MSDN documentation:

La resolución de esta propiedad depende del temporizador del sistema.

También afirman que la resolución aproximada en Windows NT 3.5 y versiones posteriores es de 10 ms   :)

5

Por lo que vale la pena, a falta de control de la fuente realidad .NET, Eric Lippert proporciona un comentario en this SO question diciendo que DateTime solo tiene una precisión de aproximadamente 30 ms. El razonamiento para no ser exacto en nanosegundos, en sus palabras, es que "no tiene que ser así".

+2

Y podría ser peor. En VBScript, la función Now() redondea el resultado devuelto al segundo más cercano, y despierta el hecho de que el valor devuelto tiene suficiente precisión disponible para precisar el microsegundo. En C#, la estructura se llama DateTime; su intención es representar una fecha y una hora para dominios no científicos típicos del mundo real, como cuando expira su seguro de vida o cuánto tiempo ha pasado desde su último reinicio. No está diseñado para un tiempo de sub-segundo de alta precisión. –

13

precisión de DateTime es algo específico del sistema que está siendo ejecutado en. La precisión está relacionada con la velocidad de un cambio de contexto, que tiende a ser alrededor de 15 o 16   ms. (En mi sistema, en realidad es de unos 14 ms   de mi prueba, pero he visto algunos ordenadores portátiles, donde está más cerca de 35-40 ms   exactitud.)

Peter Bromberg escribió an article on high precision code timing en C#, donde se discute este.

+1

Las 4 máquinas Win7 que he tenido a lo largo de los años han tenido aproximadamente una precisión de 1 ms. Now() sleep (1) Now() siempre produjo un cambio de ~ 1ms en datetime cuando estaba realizando la prueba. – Bengie

+0

Estoy viendo la precisión ~ 1ms también. – Timo

2

De MSDN usted encontrará que tiene una resolución de DateTime.Now aproximada de 10 milisegundos en todos los sistemas operativos NT.

La precisión real depende del hardware. Se puede obtener una mejor precisión usando QueryPerformanceCounter.

9

Me gustaría tener un Datetime.Now precisa :), así que esto cocinado:

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

Me gusta esta solución, pero no estaba seguro, así que hice mi propia pregunta (http://stackoverflow.com/q/18257987/270348). Según el comentario/respuesta de Servy, nunca deberías reiniciar el cronómetro. – RobSiklos

+1

Quizás no en su contexto, pero restablecerlo tiene sentido en mi contexto; solo me aseguro de que esté listo antes de que comience el tiempo. – Jimmy