C#DateTime.Now精度


97

いくつかの単体テストを行っているときに、DateTime.UtcNowで予期しない動作が発生しました。DateTime.Now/UtcNowを連続して呼び出すと、より正確なミリ秒の増分をキャプチャするのではなく、予想よりも長い時間間隔で同じ値を返すようです。

正確な時間測定を行うのにより適したストップウォッチクラスがあることは知っていますが、誰かがこの動作をDateTimeで説明できるかどうか知りたいですか?DateTime.Nowの正式な精度は文書化されていますか(たとえば、50ミリ秒以内に正確ですか)?なぜDateTime.Nowは、ほとんどのCPUクロックが処理できるものよりも精度が低くなりますか?たぶん、それは最も一般的な分母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();
}

同じ値を返す間隔はどのくらいですか?
ChrisF


上記のコードを実行したところ、ティックとミリ秒の一意の値は3つしか得られず、最終的なストップウォッチ時間は147 msだったので、私のマシンでは約50msまでしか正確ではないようです...
Andy White

ループは何度も実行されましたが、3つの異なる値しか表示されませんでした...
Andy White

ここに来る人のために、ここにTLがあります。DR// QueryPerformanceCounter関数を使用します "パフォーマンスカウンターの現在の値を取得します。これは、時間間隔の測定に使用できる高解像度(<1us)のタイムスタンプです。" (マネージコードの場合、System.Diagnostics.Stopwatchクラスが、その正確な時間基準としてQPCを用いる。) msdn.microsoft.com/en-us/library/windows/desktop/...
AnotherUser

回答:


179

なぜDateTime.Nowは、ほとんどのCPUクロックが処理できるものよりも精度が低くなりますか?

良い時計は正確かつ正確でなければなりません。それらは異なります。古いジョークが進むにつれて、停止した時計は1日に2回正確であり、1分遅い時計はいつでも正確ではありません。しかし、1分の遅いクロックは常に最も近い分に正確ですが、停止したクロックには有用な精度がまったくありません。

DateTimeをマイクロ秒に正確にすることができないのに、マイクロ秒に正確にする必要があるのはなぜですか?ほとんどの人は、マイクロ秒まで正確な公式の時報のためのソースを持っていません。したがって、小数点以下6桁の精度を与えると、最後の5 桁はゴミであり、嘘になります。

DateTimeの目的は日付と時刻表すことです。高精度のタイミングは、DateTimeの目的ではありません。お気づきのように、それがストップウォッチの目的です。DateTimeの目的は、現在の時刻をユーザーに表示する、次の火曜日までの日数を計算するなどの目的で日付と時刻を表すことです。

つまり、「今何時?」そして、「どれくらいの時間がかかりましたか?」完全に異なる質問です。1つの質問に答えるために設計されたツールを使用して他の質問に答えないでください。

質問ありがとうございます。これは良いブログ記事になります!:-)


2
@Eric Lippert:Raymond Chenは、「正確さ」と「正確さ」の違いについての非常に古いトピックを持っていますが、blogs.msdn.com / oldnewthing
jason

3
さて、精度と精度の良い点です。「正確である必要はない」ため、DateTimeは正確ではないという記述はまだ購入していません。トランザクションシステムがあり、各レコードの日時をマークしたい場合、DateTimeクラスを使用するのは直感的に思えますが、.NETにはより正確/正確な時間コンポーネントがあるようです。能力を低下させた。私はもう少し読む必要があると思います...
Andy White

11
OK @Andy、そのようなシステムがあるとしましょう。1台のマシンで、トランザクションを1月1日12:34:30.23498273に発生するものとしてマークします。クラスタ内の別のマシンで、トランザクションを1月1日12:34:30.23498456に発生するものとしてマークします。最初に発生したトランザクションは?2つのマシンのクロックが互いに1マイクロ秒以内で同期していることがわからない限り、どちらが最初に発生したかはわかりません。余分な精度はゴミを誤解させるものです。VBScriptの場合と同じように、自分のやり方ですべてのDateTimeが最も近い秒に丸められます。
Eric Lippert、2010年

14
同期していない平均的なPCは通常数分で切れるため、これであなたが言及した問題が解決するわけではありません。1に丸めても何も解決しない場合、なぜ丸めを行うのでしょうか。言い換えると、絶対値の精度がデルタ時間測定の精度よりも低くなければならない理由について、私はあなたの主張に従いません。
Roman Starkov、2011

3
(1)カレンダースペース(数秒以内)で何かがいつ発生したかを知る(2)イベント間の間隔を正確に知る(50ミリ秒以内)必要があるアクティビティログを作成するとします。最初のアクションのタイムスタンプにDateTime.Nowを使用し、その後のアクションにストップウォッチを使用して最初のDateTimeからのオフセットを決定するのが最も安全だと思われます。これはあなたが助言するアプローチですか、エリック?
devuxer 2013

18

DateTimeの精度は、実行されているシステムによって多少異なります。精度は、約15または16 msになる傾向があるコンテキストスイッチの速度に関連しています。(私のシステムでは、実際にはテストから約14ミリ秒ですが、ラップトップによっては35〜40ミリ秒の精度に近いものもあります。)

Peter BrombergがC#での高精度コードタイミングに関する記事を書い、これについて説明しています。


2
私が長年使ってきた4台のWin7マシンは、すべておよそ1msの精度でした。Now()、sleep(1)、Now()を使用すると、私がテストしているときは常に日時が〜1ms変更されました。
Bengie、

〜1msの精度も確認しています。
ティモ

12

正確なDatetime.Now :)が欲しいので、これを作りました:

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
私はこのソリューションが好きですが、確信が持てなかったため、自分で質問しました(stackoverflow.com/q/18257987/270348)。Servyからのコメント/回答に従って、ストップウォッチをリセットしないでください。
RobSiklos 2013

1
多分あなたのコンテキストではないかもしれませんが、リセットは私のコンテキストでは理にかなっています-タイミングが実際に始まる前にそれが完了したことを確認します。
ジミー

サブスクリプションは必要なく、ストップウォッチをリセットする必要もありません。10msごとにこのコードを実行する必要はなく、CPUを消費します。そして、このコードはスレッドセーフではありません。myStopwatchStartTime = DateTime.UtcNow;を初期化するだけです。静的コンストラクターで1回。
VeganHunter 2018年

1
@VeganHunterコメントを正しく理解しているかどうかはわかりませんが、TimeChangedが約10msごとに呼び出されると思いますか?そうではありません。
ジミー

@ジミー、あなたは正しい。私の悪い、私はコードを誤解しました。SystemEvents.TimeChangedイベントは、ユーザーがシステム時刻を変更した場合にのみ呼び出されます。珍しいイベントです。
VeganHunter 2018年


5

.NETソースを実際にチェックするのではなく、Eric LippertがこのSOの質問にコメントして、DateTimeの精度が約30ミリ秒しかないことを示しました。彼の言葉では、ナノ秒の正確さがない理由は、「正確である必要はない」ということです。


4
そしてそれはもっと悪いことかもしれません。VBScriptでは、Now()関数は返された結果を最も近い秒に丸め、返された値はマイクロ秒まで正確に利用できる十分な精度を持っているという事実を否定します。C#では、構造はDateTimeと呼ばれます。これは、生命保険の有効期限や最後の再起動からの経過時間など、典型的な実際の非科学的なドメインの日付と時刻を表すことを目的としています。高精度の1秒未満のタイミングを想定していません。
Eric Lippert、2010年


1

このプロパティの解決は、基になるオペレーティングシステムに依存するシステムタイマーに依存します。0.5〜15ミリ秒になる傾向があります。

その結果、ループなどの短い時間間隔でNowプロパティを繰り返し呼び出すと、同じ値が返されることがあります。

MSDNリンク

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.