C # DateTime.Now Genauigkeit

96

Ich habe gerade ein unerwartetes Verhalten mit DateTime.UtcNow festgestellt, als ich einige Unit-Tests durchgeführt habe. Wenn Sie DateTime.Now/UtcNow schnell hintereinander aufrufen, erhalten Sie anscheinend den gleichen Wert für ein länger als erwartetes Zeitintervall zurück, anstatt genauere Millisekunden-Inkremente zu erfassen.

Ich weiß, dass es eine Stoppuhrklasse gibt, die sich besser für präzise Zeitmessungen eignet, aber ich war neugierig, ob jemand dieses Verhalten in DateTime erklären könnte. Gibt es eine offizielle Genauigkeit, die für DateTime.Now dokumentiert ist (z. B. auf 50 ms genau?)? Warum sollte DateTime.Now weniger präzise sein als die meisten CPU-Uhren? Vielleicht ist es nur für die CPU mit dem kleinsten gemeinsamen Nenner ausgelegt?

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();
}
Andy White
quelle
In welchem ​​Intervall erhalten Sie denselben Wert zurück?
ChrisF
Als ich den obigen Code ausgeführt habe, habe ich nur 3 eindeutige Werte für Ticks und Millisekunden erhalten, und die Endstoppzeit betrug 147 ms. Es scheint also, dass sie auf meiner Maschine nur auf ungefähr 50 ms genau ist ...
Andy White
Ich sollte sagen, die Schleife lief ein paar Mal, aber ich sah nur 3 verschiedene Werte ...
Andy White
Für alle, die hierher kommen, ist hier die TL; DR // Verwenden Sie die QueryPerformanceCounter-Funktion "Ruft den aktuellen Wert des Leistungsindikators ab, bei dem es sich um einen Zeitstempel mit hoher Auflösung (<1us) handelt, der für Zeitintervallmessungen verwendet werden kann." (Für verwalteten Code verwendet die System.Diagnostics.Stopwatch-Klasse QPC als genaue Zeitbasis
AnotherUser

Antworten:

178

Warum sollte DateTime.Now weniger präzise sein als die meisten CPU-Uhren?

Eine gute Uhr sollte sowohl präzise als auch genau sein . das sind anders. Wie der alte Witz schon sagt, ist eine angehaltene Uhr zweimal am Tag genau, eine Uhr, die eine Minute langsam ist, zu keiner Zeit genau. Aber die Uhr, die eine Minute langsamer ist, ist immer auf die nächste Minute genau, während eine angehaltene Uhr überhaupt keine nützliche Genauigkeit hat.

Warum sollte die DateTime auf eine Mikrosekunde genau sein, wenn sie möglicherweise nicht auf die Mikrosekunde genau sein kann ? Die meisten Menschen haben keine Quelle für offizielle Zeitsignale, die auf die Mikrosekunde genau sind. Daher sechs Stellen nach dem Komma geben Präzision , die letzten fünf davon sind Müll würde liegend .

Denken Sie daran, dass DateTime ein Datum und eine Uhrzeit darstellt . Hochpräzise Timings sind überhaupt nicht der Zweck von DateTime. Wie Sie bemerken, ist dies der Zweck von StopWatch. Der Zweck von DateTime besteht darin, ein Datum und eine Uhrzeit für Zwecke wie die Anzeige der aktuellen Uhrzeit für den Benutzer, die Berechnung der Anzahl der Tage bis zum nächsten Dienstag usw. darzustellen.

Kurz gesagt: "Wie spät ist es?" und "wie lange hat das gedauert?" sind ganz andere Fragen; Verwenden Sie kein Tool zur Beantwortung einer Frage, um die andere zu beantworten.

Danke für die Frage; Dies wird einen guten Blog-Artikel machen! :-)

Eric Lippert
quelle
2
@Eric Lippert: Raymond Chen hat einen Oldie, aber einen Goodie zum Thema Unterschied zwischen "Präzision" und "Genauigkeit": blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx
jason
3
Ok, guter Punkt über Präzision vs. Genauigkeit. Ich glaube, ich kaufe die Aussage, dass DateTime nicht korrekt ist, immer noch nicht wirklich, weil "es nicht sein muss". Wenn ich ein Transaktionssystem habe und für jeden Datensatz eine Datums- / Uhrzeitangabe markieren möchte, erscheint es mir intuitiv, die DateTime-Klasse zu verwenden, aber es scheint, dass es in .NET genauere / präzisere Zeitkomponenten gibt. Warum sollte DateTime also sein? weniger fähig gemacht. Ich denke, ich muss noch etwas lesen ...
Andy White
11
OK @Andy, nehmen wir an, Sie haben ein solches System. Auf einem Computer markieren Sie eine Transaktion als am 1. Januar, 12: 34: 30.23498273. Auf einem anderen Computer in Ihrem Cluster markieren Sie eine Transaktion als am 1. Januar, 12: 34: 30.23498456. Welche Transaktion fand zuerst statt? Wenn Sie nicht wissen, dass die beiden Maschinentakte innerhalb einer Mikrosekunde voneinander synchronisiert sind, wissen Sie nicht, welche zuerst aufgetreten ist. Die zusätzliche Präzision ist irreführender Müll . Wenn es nach mir ginge, würden alle DateTimes wie in VBScript auf die nächste Sekunde gerundet.
Eric Lippert
14
nicht, dass dies das von Ihnen erwähnte Problem lösen würde, da durchschnittliche nicht synchronisierte PCs normalerweise minutenlang ausfallen . Wenn das Runden auf 1s nichts löst, warum dann überhaupt runden? Mit anderen Worten, ich folge Ihrem Argument nicht, warum die absoluten Werte eine geringere Genauigkeit haben sollten als die Genauigkeit von Deltazeitmessungen.
Roman Starkov
3
Angenommen, ich erstelle ein Aktivitätsprotokoll, bei dem (1) bekannt sein muss, wann etwas in Bezug auf den Kalenderbereich aufgetreten ist (innerhalb weniger Sekunden) (2) der Abstand zwischen Ereignissen (innerhalb von etwa 50 Millisekunden) sehr genau bekannt ist. Es klingt so, als wäre es am sichersten, DateTime.Now für den Zeitstempel der ersten Aktion zu verwenden und dann eine Stoppuhr für nachfolgende Aktionen zu verwenden, um den Versatz von der anfänglichen DateTime zu bestimmen. Ist dies der Ansatz, den Sie empfehlen würden, Eric?
Devuxer
18

Die Genauigkeit von DateTime ist etwas spezifisch für das System, auf dem es ausgeführt wird. Die Genauigkeit hängt von der Geschwindigkeit eines Kontextwechsels ab, die tendenziell bei 15 oder 16 ms liegt. (Auf meinem System sind es ungefähr 14 ms von meinen Tests, aber ich habe einige Laptops gesehen, bei denen die Genauigkeit näher an 35-40 ms liegt.)

Peter Bromberg schrieb einen Artikel über hochpräzises Code-Timing in C #, in dem dies diskutiert wird.

Reed Copsey
quelle
2
Die 4 Win7-Maschinen, die ich im Laufe der Jahre hatte, hatten alle eine Genauigkeit von ungefähr 1 ms. Now () sleep (1) Now () führte beim Testen immer zu einer Änderung der Datums- und Uhrzeitdauer um ~ 1 ms.
Bengie
Ich sehe auch die Genauigkeit von ~ 1 ms.
Timo
13

Ich hätte gerne eine genaue Datetime.Now :), also habe ich mir das ausgedacht:

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); } }
}
Jimmy
quelle
2
Ich mag diese Lösung, war mir aber nicht sicher und stellte meine eigene Frage ( stackoverflow.com/q/18257987/270348 ). Gemäß dem Kommentar / der Antwort von Servy sollten Sie die Stoppuhr niemals zurücksetzen.
RobSiklos
1
Vielleicht nicht in Ihrem Kontext, aber das Zurücksetzen macht in meinem Kontext Sinn - ich stelle nur sicher, dass es fertig ist, bevor das Timing tatsächlich beginnt.
Jimmy
Sie benötigen das Abonnement nicht und müssen die Stoppuhr nicht zurücksetzen. Das Ausführen dieses Codes alle ~ 10 ms ist nicht erforderlich und verbraucht CPU. Und dieser Code ist überhaupt nicht threadsicher. Initialisieren Sie einfach myStopwatchStartTime = DateTime.UtcNow; einmal im statischen Konstruktor.
VeganHunter
1
@VeganHunter Ich bin mir nicht sicher, ob ich Ihren Kommentar richtig verstehe, aber Sie scheinen zu glauben, dass TimeChanged alle ~ 10ms aufgerufen wird? Das tut es nicht.
Jimmy
@ Jimmy, du hast recht. Mein schlechtes, ich habe den Code falsch verstanden. Das Ereignis SystemEvents.TimeChanged wird nur aufgerufen, wenn der Benutzer die Systemzeit ändert. Es ist ein seltenes Ereignis.
VeganHunter
5

Aus MSDN geht hervor , dass DateTime.Nowdie Auflösung auf allen NT-Betriebssystemen ungefähr 10 Millisekunden beträgt.

Die tatsächliche Genauigkeit hängt von der Hardware ab. Mit kann eine bessere Präzision erzielt werden QueryPerformanceCounter.

Kevin Montrose
quelle
5

Eric Lippert gab einen Kommentar zu dieser SO-Frage ab, in dem er sagte, dass DateTime nur auf ca. 30 ms genau ist. Der Grund dafür, dass er nicht nanosekundengenau ist, ist in seinen Worten, dass es "nicht sein muss".

Scott Anderson
quelle
4
Und es könnte schlimmer sein. In VBScript rundet die Funktion Now () das zurückgegebene Ergebnis auf die nächste Sekunde, wobei die Tatsache berücksichtigt wird, dass der zurückgegebene Wert eine ausreichende verfügbare Genauigkeit aufweist, um auf die Mikrosekunde genau zu sein. In C # heißt die Struktur DateTime. Es soll ein Datum und eine Uhrzeit für typische nichtwissenschaftliche Bereiche in der realen Welt darstellen, z. B. wann Ihre Lebensversicherung abläuft oder wie lange es seit Ihrem letzten Neustart her ist. Es ist nicht für hochpräzises Subsekunden-Timing vorgesehen.
Eric Lippert
3

Aus der MSDN-Dokumentation :

Die Auflösung dieser Eigenschaft hängt vom Systemzeitgeber ab.

Sie behaupten auch, dass die ungefähre Auflösung unter Windows NT 3.5 und höher 10 ms beträgt :)

Tomas Vana
quelle
1

Die Auflösung dieser Eigenschaft hängt vom Systemzeitgeber ab, der vom zugrunde liegenden Betriebssystem abhängt. Sie liegt tendenziell zwischen 0,5 und 15 Millisekunden.

Infolgedessen können wiederholte Aufrufe der Now-Eigenschaft in einem kurzen Zeitintervall, z. B. in einer Schleife, denselben Wert zurückgeben.

MSDN Link

Iman
quelle