ミリ秒単位の精度でWindowsシステム時刻を取得するにはどうすればよいですか?
上記が不可能な場合、どのようにしてオペレーティングシステムの開始時間を取得できますか?ミリ秒の精度でシステム時間を計算するために、timeGetTime()と一緒にこの値を使用したいと思います。
前もって感謝します。
GetTickCount
はあなたのためにそれを成し遂げません。
QueryPerformanceFrequency
/QueryPerformanceCounter
を調べてください。ただし、ここでの唯一の問題はCPUのスケーリングですので、調査を行ってください。
MSDN Magazineのこの記事をお試しください。実際にはかなり複雑です。
これは、いくつかの理由を説明するための上記のコメントの詳細です。
まず、 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が引用したもので使用されました。ただし、これらのアプローチにはドリフト(周波数スケーリングなど)がある場合があるため、システム時刻と同期する必要があります。
Windowsでは、すべての時間のベースはGetSystemTimeAsFiletime
と呼ばれる関数です。
FILETIME
構造体は、1600年1月1日以降の100ns間隔の数を記録します。つまり、その分解能は100nsに制限されています。
これが最初の関数です。
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
に移動する方法があります:
現在のシステム時刻を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
次に、UTCで現在の時刻が必要ない場合はどうでしょうか。現地時間でそれが欲しい場合はどうしますか? Windowsは、UTCにあるFILETIME
をローカル時間に変換する関数を提供します:FileTimeToLocalFileTime
FILETIME
を返す関数をlocal時間ですでに書くことができます:
FILETIME GetLocalTimeAsFileTime()
{
FILETIME ftNow;
GetSystemTimeAsFileTime(ref ftNow);
//convert to local
FILETIME ftNowLocal
FileTimeToLocalFileTime(ftNow, ref ftNowLocal);
return ftNowLocal;
}
local FILETIMEをSYSTEMTIMEにデコードするとします。それは問題ありません、FileTimeToSystemTime
を再び使用できます:
さいわい、Windowsにはすでに値を返す関数が用意されています。
別の考慮事項があります。 Windows 8以前は、時計の分解能は約15msでした。 Windows 8では、クロックを100nsに改善しました(FILETIME
の解像度に一致)。
GetSystemTimeAsFileTime
(レガシー、15msの解像度)GetSystemTimeAsPreciseFileTime
(Windows 8、100nsの解像度)つまり、常に新しい値を優先する必要があります。
あなたは時間を求めました。しかし、いくつかの選択肢があります。
タイムゾーン:
フォーマット:
FILETIME
(システムネイティブ、100nsの解像度)SYTEMTIME
(デコード、1msの解像度)FILETIME
GetSytemTimeAsPreciseFileTime
(またはGetSystemTimeAsFileTime
)SYSTEMTIME
GetSystemTime
GetLocalTime
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);
}
}
私たちは皆、退屈な説明ではなく簡単なスニペットを求めてここに来るので、次のように記述します。
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!)
パフォーマンスカウンターベースのドリフトの答えがどのように上がったかはわかりません。スリップバグを実行しないでください。
GetSystemTimeAsFileTime は、Win32関数の中で絶対時間に関して最高の精度を提供します。ジョエル・クラークが示唆したQPF/QPCは、相対的な時間を改善します。
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
まあ、これは非常に古いですが、Windows Cライブラリには別の便利な関数 _ftime
があり、ローカル時間をtime_t
、ミリ秒、タイムゾーン、夏時間フラグ。