web-dev-qa-db-fra.com

C # DateTime.Now precision

Je viens de rencontrer un comportement inattendu avec DateTime.UtcNow lors de certains tests unitaires. Il semble que lorsque vous appelez rapidement DateTime.Now/UtcNow, il semble vous redonner la même valeur pendant un intervalle de temps plus long que prévu, au lieu de capturer des incréments de millisecondes plus précis.

Je sais qu'il existe une classe Stopwatch qui conviendrait mieux pour effectuer des mesures de temps précises, mais je me demandais si quelqu'un pourrait expliquer ce comportement dans DateTime? Existe-t-il une précision officielle documentée pour DateTime.Now (par exemple, précise à 50 ms près?)? Pourquoi voudrait-on que DateTime.Now soit moins précis que ce que la plupart des horloges de processeur peuvent gérer? Peut-être est-il simplement conçu pour le processeur avec le plus petit dénominateur commun?

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

Pourquoi voudrait-on que DateTime.Now soit moins précis que ce que la plupart des horloges de processeur peuvent gérer?

Une bonne horloge devrait être à la fois précise et précise; ceux-ci sont différents. Comme le dit la vieille blague, une horloge arrêtée est exactement exacte deux fois par jour, une horloge par minute lente n'est jamais précise à tout moment. Mais l'horloge minute par minute est toujours précise à la minute près, alors qu'une horloge arrêtée n'a aucune précision utile.

Pourquoi le DateTime devrait-il être précis à, disons, une microseconde alors qu'il ne peut pas être précis à la microseconde? La plupart des gens ne disposent d'aucune source de signaux de l'heure officielle précis à la microseconde. Par conséquent, donner six chiffres après la décimale de precision, dont les cinq derniers sont garbage, serait mentant.

Rappelez-vous que le but de DateTime est de représenter une date et une heure. Les minutages de haute précision ne sont pas du tout l'objectif de DateTime; comme vous le constatez, c'est l'objectif de StopWatch. Le but de DateTime est de représenter une date et une heure à des fins telles que l'affichage de l'heure actuelle à l'utilisateur, le calcul du nombre de jours jusqu'au mardi prochain, etc.

En bref, "quelle heure est-il?" et "combien de temps cela a-t-il pris?" sont des questions complètement différentes; n'utilisez pas un outil conçu pour répondre à une question et pour répondre à l'autre.

Merci pour la question cela fera un bon article de blog! :-)

165
Eric Lippert

La précision de DateTime est quelque peu spécifique au système sur lequel il est exécuté. La précision est liée à la vitesse d'un commutateur de contexte, qui a tendance à être d'environ 15 ou 16 ms. (Sur mon système, cela fait en fait 14 ms environ, mais j'ai vu des ordinateurs portables dont la précision est plus proche de 35 à 40 ms.)

Peter Bromberg a écrit un article sur le minutage de code de haute précision en C #, qui traite de cela.

17
Reed Copsey

Je voudrais un Datetime.Now :) précis, alors je l'ai concocté:

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); } }
}
12
Jimmy

Pour ce que cela vaut, à moins de vérifier réellement la source .NET, Eric Lippert a commenté cette SO question affirmant que DateTime n’est précis qu’à 30 ms environ. Le raisonnement pour ne pas être précis à la nanoseconde, selon ses mots, est qu'il "n'a pas besoin de l'être". 

5
Scott Anderson

À partir de MSDN , vous constaterez que DateTime.Now a une résolution approximative de 10 millisecondes sur tous les systèmes d'exploitation NT.

La précision réelle dépend du matériel. Une meilleure précision peut être obtenue avec QueryPerformanceCounter .

5
Kevin Montrose

À partir de la documentation MSDN :

La résolution de cette propriété dépend de la minuterie du système.

Ils affirment également que la résolution approximative sur Windows NT 3.5 et versions ultérieures est de 10 ms :)

3
Tomas Vana

La résolution de cette propriété dépend de la minuterie du système, qui dépend du système d'exploitation sous-jacent. Il a tendance à être entre 0.5 et __. et 15 millisecondes.

Par conséquent, des appels répétés à la propriété Now dans un intervalle de temps court, tel qu'une boucle, peuvent renvoyer la même valeur.

MSDN Link

0
Iman Abidi