web-dev-qa-db-ja.com

ミリ秒単位の精度でWindowsシステム時刻を取得するにはどうすればよいですか?

ミリ秒単位の精度でWindowsシステム時刻を取得するにはどうすればよいですか?

上記が不可能な場合、どのようにしてオペレーティングシステムの開始時間を取得できますか?ミリ秒の精度でシステム時間を計算するために、timeGetTime()と一緒にこの値を使用したいと思います。

前もって感謝します。

21
axilmar

GetTickCountはあなたのためにそれを成し遂げません。

QueryPerformanceFrequency/QueryPerformanceCounterを調べてください。ただし、ここでの唯一の問題はCPUのスケーリングですので、調査を行ってください。

8
Joel Clark

MSDN Magazineのこの記事をお試しください。実際にはかなり複雑です。

継続的に更新される、Windows用の高解像度タイムプロバイダーを実装する

21
Kevin Gale

これは、いくつかの理由を説明するための上記のコメントの詳細です。

まず、 GetSystemTime *呼び出しは、システムの時刻を提供する唯一のWin32 APIです。ほとんどのアプリケーションはより高い解像度を維持するために必要なオーバーヘッドを必要としないため、この時間はかなり粗い粒度を持っています。時間は、(おそらく)64ビットのミリ秒単位で内部的に格納されます。 timeGetTimeを呼び出すと、下位32ビットが取得されます。 GetSystemTimeなどを呼び出すと、日数などに変換した後、システムの開始時刻を含めて、このミリ秒の時間を返すようにWindowsに要求します。

マシンには2つのタイムソースがあります。CPUのクロックとオンボードクロック(リアルタイムクロック(RTC)、プログラマブルインターバルタイマー(PIT)、高精度イベントタイマー(HPET)など)です。 1つ目は約0.5ns(2GHz)の分解能を持ち、2つ目は一般に1msの周期までプログラム可能です(新しいチップ(HPET)はより高い分解能を持っています)。 Windowsはこれらの定期的な目盛りを使用して、システム時刻の更新などの特定の操作を実行します。

アプリケーションはこの期間を timerBeginPeriod を介して変更できます。ただし、これはシステム全体に影響します。 OSは、要求された頻度で定期的なイベントをチェック/更新します。 CPUの負荷/周波数が低い場合、電力節約のためにアイドル期間があります。高周波では、プロセッサを低電力状態にする時間はありません。詳細は Timer Resolution を参照してください。最後に、各ティックにはいくつかのオーバーヘッドがあり、周波数を上げるとより多くのCPUサイクルが消費されます。

より高い解決時間の場合、システム時間はこの精度に維持されず、ビッグベンが秒針を持っていることになります。 QueryPerformanceCounter(QPC)またはCPUのティック(rdtsc)を使用すると、システム時間のティック間の解像度を提供できます。このようなアプローチは、MSDNマガジンの記事Kevinが引用したもので使用されました。ただし、これらのアプローチにはドリフト(周波数スケーリングなど)がある場合があるため、システム時刻と同期する必要があります。

11
Brian

Windowsでは、すべての時間のベースはGetSystemTimeAsFiletimeと呼ばれる関数です。

  • 100nsの時間を保持できる構造を返します。
  • UTCで保管されています

FILETIME構造体は、1600年1月1日以降の100ns間隔の数を記録します。つまり、その分解能は100nsに制限されています。

これが最初の関数です。

enter image description here

1600年1月1日以降の64ビットの100nsティックの数はやや扱いにくいです。 Windowsは、この64ビット整数を有用な部分にデコードできる便利なヘルパー関数FileTimeToSystemTimeを提供しています。

record SYSTEMTIME {
   wYear: Word;
   wMonth: Word;
   wDayOfWeek: Word;
   wDay: Word;
   wHour: Word;
   wMinute: Word;
   wSecond: Word;
   wMilliseconds: Word;
}

SYSTEMTIMEには1msの組み込みの解像度制限があることに注意してください

FILETIMEからSYSTEMTIMEに移動する方法があります:

enter image description here

現在のシステム時刻をSYSTEIMTIME構造体として取得する関数を書くことができます:

SYSTEMTIME GetSystemTime()
{
    //Get the current system time utc in it's native 100ns FILETIME structure
    FILETIME ftNow;
    GetSytemTimeAsFileTime(ref ft);

    //Decode the 100ns intervals into a 1ms resolution SYSTEMTIME for us
    SYSTEMTIME stNow;
    FileTimeToSystemTime(ref stNow);

    return stNow;
}

Windowsがあなたのためにそのような関数をすでに書いたことを除いて: GetSystemTime

enter image description here

UTCではなくローカル

次に、UTCで現在の時刻が必要ない場合はどうでしょうか。現地時間でそれが欲しい場合はどうしますか? Windowsは、UTCにあるFILETIMEをローカル時間に変換する関数を提供します:FileTimeToLocalFileTime

enter image description here

FILETIMEを返す関数をlocal時間ですでに書くことができます:

FILETIME GetLocalTimeAsFileTime()
{
   FILETIME ftNow;
   GetSystemTimeAsFileTime(ref ftNow);

   //convert to local
   FILETIME ftNowLocal
   FileTimeToLocalFileTime(ftNow, ref ftNowLocal);

   return ftNowLocal;
}

enter image description here

local FILETIMEをSYSTEMTIMEにデコードするとします。それは問題ありません、FileTimeToSystemTimeを再び使用できます:

enter image description here

さいわい、Windowsにはすでに値を返す関数が用意されています。

enter image description here

正確

別の考慮事項があります。 Windows 8以前は、時計の分解能は約15msでした。 Windows 8では、クロックを100nsに改善しました(FILETIMEの解像度に一致)。

  • GetSystemTimeAsFileTime(レガシー、15msの解像度)
  • GetSystemTimeAsPreciseFileTime(Windows 8、100nsの解像度)

つまり、常に新しい値を優先する必要があります。

enter image description here

あなたは時間を求めました

あなたは時間を求めました。しかし、いくつかの選択肢があります。

タイムゾーン:

  • UTC (システムネイティブ)
  • ローカルタイムゾーン

フォーマット:

  • FILETIME(システムネイティブ、100nsの解像度)
  • SYTEMTIME(デコード、1msの解像度)

概要

  • 100nsの分解能:FILETIME
    • UTC:GetSytemTimeAsPreciseFileTime(またはGetSystemTimeAsFileTime
    • ローカル:(独自のロール)
  • 1msの解像度:SYSTEMTIME
    • UTC:GetSystemTime
    • ローカル:GetLocalTime
7
Ian Boyd

Windows 8以降、Microsoftは新しいAPIコマンドGetSystemTimePreciseAsFileTimeを導入しました。

https://msdn.Microsoft.com/en-us/library/windows/desktop/hh706895%28v=vs.85%29.aspx

残念ながら、古いオペレーティングシステムでも実行する必要のあるソフトウェアを作成する場合は、これを使用できません。

私の現在の解決策は次のとおりですが、注意してください:決定された時間は正確ではなく、リアルタイムに近いだけです。結果は常にリアルタイムと同じかそれより小さくなければなりませんが、エラーが修正されます(コンピューターがスタンバイ状態になった場合を除く)。結果はミリ秒の分解能です。私の目的には十分正確です。

void GetHighResolutionSystemTime(SYSTEMTIME* pst)
{
    static LARGE_INTEGER    uFrequency = { 0 };
    static LARGE_INTEGER    uInitialCount;
    static LARGE_INTEGER    uInitialTime;
    static bool             bNoHighResolution = false;

    if(!bNoHighResolution && uFrequency.QuadPart == 0)
    {
        // Initialize performance counter to system time mapping
        bNoHighResolution = !QueryPerformanceFrequency(&uFrequency);
        if(!bNoHighResolution)
        {
            FILETIME ftOld, ftInitial;

            GetSystemTimeAsFileTime(&ftOld);
            do
            {
                GetSystemTimeAsFileTime(&ftInitial);
                QueryPerformanceCounter(&uInitialCount);
            } while(ftOld.dwHighDateTime == ftInitial.dwHighDateTime && ftOld.dwLowDateTime == ftInitial.dwLowDateTime);
            uInitialTime.LowPart  = ftInitial.dwLowDateTime;
            uInitialTime.HighPart = ftInitial.dwHighDateTime;
        }
    }

    if(bNoHighResolution)
    {
        GetSystemTime(pst);
    }
    else
    {
        LARGE_INTEGER   uNow, uSystemTime;

        {
            FILETIME    ftTemp;
            GetSystemTimeAsFileTime(&ftTemp);
            uSystemTime.LowPart  = ftTemp.dwLowDateTime;
            uSystemTime.HighPart = ftTemp.dwHighDateTime;
        }
        QueryPerformanceCounter(&uNow);

        LARGE_INTEGER   uCurrentTime;
        uCurrentTime.QuadPart = uInitialTime.QuadPart + (uNow.QuadPart - uInitialCount.QuadPart) * 10000000 / uFrequency.QuadPart;

        if(uCurrentTime.QuadPart < uSystemTime.QuadPart || abs(uSystemTime.QuadPart - uCurrentTime.QuadPart) > 1000000)
        {
            // The performance counter has been frozen (e. g. after standby on laptops)
            // -> Use current system time and determine the high performance time the next time we need it
            uFrequency.QuadPart = 0;
            uCurrentTime = uSystemTime;
        }

        FILETIME ftCurrent;
        ftCurrent.dwLowDateTime  = uCurrentTime.LowPart;
        ftCurrent.dwHighDateTime = uCurrentTime.HighPart;
        FileTimeToSystemTime(&ftCurrent, pst);
    }
}
4
David Gausmann

私たちは皆、退屈な説明ではなく簡単なスニペットを求めてここに来るので、次のように記述します。

FILETIME t;
GetSystemTimeAsFileTime(&t); // unusable as is

ULARGE_INTEGER i;
i.LowPart = t.dwLowDateTime;
i.HighPart = t.dwHighDateTime;

int64_t ticks_since_1601 = i.QuadPart; // now usable
int64_t us_since_1601   = (i.QuadPart * 1e-1);
int64_t ms_since_1601   = (i.QuadPart * 1e-4);
int64_t sec_since_1601  = (i.QuadPart * 1e-7);

// unix Epoch
int64_t unix_us  = (i.QuadPart * 1e-1) - 11644473600LL * 1000000;
int64_t unix_ms  = (i.QuadPart * 1e-4) - 11644473600LL * 1000;
double  unix_sec = (i.QuadPart * 1e-7) - 11644473600LL;

// i.QuadPart is # of 100ns ticks since 1601-01-01T00:00:00Z
// difference to Unix Epoch is 11644473600 seconds (attention to units!)

パフォーマンスカウンターベースのドリフトの答えがどのように上がったかはわかりません。スリップバグを実行しないでください。

1
user3125367

GetSystemTimeAsFileTime は、Win32関数の中で絶対時間に関して最高の精度を提供します。ジョエル・クラークが示唆したQPF/QPCは、相対的な時間を改善します。

1
Ben Voigt

QueryPerformanceCounter()は、きめ細かなタイマー解決のために構築されています。

これは、システムが提供する必要がある最高の解像度のタイマーであり、アプリケーションコードで使用してパフォーマンスのボトルネックを特定できます。

C#開発者向けの簡単な実装を次に示します。

    [DllImport("kernel32.dll")]
    extern static short QueryPerformanceCounter(ref long x);
    [DllImport("kernel32.dll")]
    extern static short QueryPerformanceFrequency(ref long x);
    private long m_endTime;
    private long m_startTime;
    private long m_frequency;

    public Form1()
    {
        InitializeComponent();
    }
    public void Begin()
    {
        QueryPerformanceCounter(ref m_startTime);
    }
    public void End()
    {
        QueryPerformanceCounter(ref m_endTime);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        QueryPerformanceFrequency(ref m_frequency);
        Begin();
        for (long i = 0; i < 1000; i++) ;
        End();
        MessageBox.Show((m_endTime - m_startTime).ToString());
    }

C/C++開発者の場合は、こちらをご覧ください: http://support.Microsoft.com/kb/815668

0
Bruno

まあ、これは非常に古いですが、Windows Cライブラリには別の便利な関数 _ftime があり、ローカル時間をtime_t、ミリ秒、タイムゾーン、夏時間フラグ。

0
Mikhail Edoshin