日時と時間計測
日時の取得
現在の日時はGetLocalTime
関数で取得することができます。
GetLocalTime関数は日時情報をSYSTEMTIME
構造体に格納します。
- void GetLocalTime(
LPSYSTEMTIME lpSystemTime
); - 現在の日時を取得しlpSystemTimeに格納する。
- typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME; - 日時の情報を格納する構造体。
これらの使い方はC言語のlocaltime関数とtm構造体に似ています。
wYear
メンバは西暦です。
wMonth
メンバは月です。
C言語のlocaltime
関数では「0~11」でしたが、このGetLocalTime
関数は「1~12」を取得します。
wDayOfWeek
メンバは曜日です。
日曜日は0、月曜日は1、火曜日は2…と続きます。
wDay
メンバは日付です。
wHour
メンバは時間です。
wMinute
メンバは分です。
wSecond
メンバは秒です。
wMilliseconds
メンバはミリ秒(1/1000秒)です。
#include <windows.h>
#include <strsafe.h>
//ウィンドウの生成等は省略
#define BUFFERSIZE 32
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rt;
SYSTEMTIME st;
static const WCHAR* format =
L"%04d年 %02d月 %02d日 %s曜日\n"
L"%02d時 %02d分 %02d秒";
static const WCHAR *dayOfWeek[] =
{ L"日", L"月", L"火", L"水", L"木", L"金", L"土" };
static WCHAR buf[BUFFERSIZE];
switch (message)
{
case WM_CREATE: //ウィンドウ作成
GetLocalTime(&st);
StringCchPrintf(buf, BUFFERSIZE, format,
st.wYear, st.wMonth, st.wDay, dayOfWeek[st.wDayOfWeek],
st.wHour, st.wMinute, st.wSecond);
break;
case WM_PAINT: //ウィンドウ描画
GetClientRect(hWnd, &rt);
hdc = BeginPaint(hWnd, &ps);
DrawText(hdc, buf, -1, &rt, DT_WORDBREAK);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY: //ウィンドウの破棄
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
このコードはアプリ起動時の日時を表示します。
ちなみに現在の日時を設定するにはSetLocalTime
関数を使用します。
使い方は日時をセットしたSYSTEMTIME構造体変数を引数にポインタで渡すだけです。
(wDayOfWeek
メンバの値は無視されます)
当然ですがシステム(Windows)の日時の設定が変更されるので不用意に使用するべきではありません。
また、管理者権限で実行する必要があります。
ローカル日時(日本時間)ではなくUTC時間(協定世界時)を取得/設定する場合はGetSystemTime
関数、SetSystemTime
関数を使用します。
日本と協定世界時との時差は+9時間ですから、日本時間に-9時間した時間が取得/設定されます。
時間の計測
GetTickCount関数
時間の計測にはGetTickCount
関数を使用します。
- DWORD GetTickCount();
- システムが起動してからの経過時間を取得する。
(ミリ秒)
これもC言語でのclock関数とほぼ同じです。
計測開始時の値を変数に保存しておき、計測終了時の時間からマイナスすることで経過時間を計算します。
#include <windows.h>
#include <strsafe.h>
//ウィンドウの生成等は省略
#define BUFFERSIZE 64
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static const WCHAR* txt[] = {
L"ストップウォッチアプリ\n画面クリックで計測開始。",
L"計測中...\n画面クリックで計測終了。",
L"%dミリ秒でした。\n画面クリックで再度計測開始。"
};
static WCHAR buf[BUFFERSIZE];
static RECT rt;
static DWORD startTime;
static int state;
HDC hdc;
PAINTSTRUCT ps;
switch (message)
{
case WM_CREATE:
StringCchPrintf(buf, BUFFERSIZE, txt[0]);
break;
case WM_LBUTTONDOWN:
switch (state)
{
case 0:
state = 1;
startTime = GetTickCount();
StringCchPrintf(buf, BUFFERSIZE, txt[state]);
break;
case 1:
state = 2;
StringCchPrintf(buf, BUFFERSIZE, txt[state],
GetTickCount() - startTime);
break;
case 2:
state = 1;
startTime = GetTickCount();
StringCchPrintf(buf, BUFFERSIZE, txt[state]);
break;
}
InvalidateRect(hWnd, NULL, TRUE);
break;
case WM_SIZE: //ウィンドウサイズ変更
GetClientRect(hWnd, &rt);
break;
case WM_PAINT: //ウィンドウ描画
hdc = BeginPaint(hWnd, &ps);
DrawText(hdc, buf, -1, &rt, DT_WORDBREAK);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY: //ウィンドウの破棄
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
実行結果です。
画面をクリック(正確にはマウスダウン)することで計測を開始/停止します。
ウィンドウクラススタイルでダブルクリックメッセージを通知するようにしていると、素早くクリックしたときに上手く動作しないのでCS_DBLCLKS
フラグを外してください。
//ウィンドウクラスの登録
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;// | CS_DBLCLKS;
//↑これ
//以降省略
GetTickCount64関数
GetTickCount
関数で取得できる値は、システムの起動から約49.7日が経過すると0に戻ります。
これは時間を32ビットで管理していて、約49.7日で扱えるデータ範囲の限界値を超えるためです。
この問題を回避するためにGetTickCount64
関数があります。
これは名前の通り値を64ビットで管理します。
戻り値はULONGLONG型です。
(64ビット符号なし整数。計算上は数億年の時間を保存できる)
使い方はGetTickCount関数と同じです。
ただしこの関数を使用できるのはWindows Vista以降です。
timeGetTime関数
GetTickCount
関数やC言語のclock
関数は手軽に使用できますが、あまり精度がよくありません。
単位はミリ秒ですが、実際には10ミリ秒程度の精度だそうです。
より精度の高い計測が必要な場合はtimeGetTime
関数を使用することができます。
これはシステム起動からの経過時間を1ミリ秒の精度で取得できます。
- DWORD timeGetTime();
- システムが起動してからの経過時間を取得する。
(ミリ秒)
この関数はwinmm.libというライブラリに定義されていて、標準ではリンクされていません。
コードの先頭に「#pragma comment(lib, "winmm.lib")
」と記述してリンクするか、リンカーオプションでリンクする必要があります。
#pragma comment(lib, "winmm.lib")
#include <windows.h>
使い方はGetTickCount関数と同じです。
QueryPerformanceCounter関数
QueryPerformanceCounter
関数は、より高精度な時間を取得します。
この関数は1ミリ秒よりもさらに細かい時間を扱います。
- BOOL QueryPerformanceCounter(
LARGE_INTEGER *lpPerformanceCount
); - 高分解能パフォーマンスカウンターの現在の値をlpPerformanceCountに格納する。
成功した場合は0以外を、失敗した場合は0を返す。
LARGE_INTEGERというのは共用体で、64ビット符号付き整数値を扱います。
この共用体のQuadPart
というメンバが実際の値を保存しています。
この関数が取得する値の精度はコンピューターの処理能力により変わります。
これを時間に変換するためにQueryPerformanceFrequency
関数を使用します。
- BOOL QueryPerformanceFrequency(
LARGE_INTEGER *lpFrequency
); - 高分解能パフォーマンスカウンターの周波数をlpFrequencyに格納する。
QueryPerformanceCounterで得られる値をQueryPerformanceFrequencyで得られる値で割ることで、秒単位に変換することができます。
ちなみにこれらの関数は失敗した時に0を返しますが、Windows XP以降であれば常に成功するそうです。
#include <windows.h>
#include <strsafe.h>
#define BUFFERSIZE 64
//ウィンドウの生成等は省略
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static const WCHAR* txt[] = {
L"ストップウォッチアプリ\n画面クリックで計測開始。",
L"計測中...\n画面クリックで計測終了。",
L"%f秒でした。\n画面クリックで再度計測開始。"
};
static WCHAR buf[BUFFERSIZE];
static RECT rt;
static LARGE_INTEGER qpf, startTime;
static int state;
HDC hdc;
PAINTSTRUCT ps;
LARGE_INTEGER endTime;
switch (message)
{
case WM_CREATE:
//高分解能パフォーマンスカウンターの周波数を
//最初に取得しておく
QueryPerformanceFrequency(&qpf);
StringCchPrintf(buf, BUFFERSIZE, txt[0]);
break;
case WM_LBUTTONDOWN:
switch (state)
{
case 0:
state = 1;
QueryPerformanceCounter(&startTime);
StringCchPrintf(buf, BUFFERSIZE, txt[state]);
break;
case 1:
state = 2;
QueryPerformanceCounter(&endTime);
StringCchPrintf(buf, BUFFERSIZE, txt[state],
(double)(endTime.QuadPart - startTime.QuadPart) / qpf.QuadPart);
break;
case 2:
state = 1;
QueryPerformanceCounter(&startTime);
StringCchPrintf(buf, BUFFERSIZE, txt[state]);
break;
}
InvalidateRect(hWnd, NULL, TRUE);
break;
case WM_SIZE:
GetClientRect(hWnd, &rt);
break;
case WM_PAINT: //ウィンドウ描画
hdc = BeginPaint(hWnd, &ps);
DrawText(hdc, buf, -1, &rt, DT_WORDBREAK);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY: //ウィンドウの破棄
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
このコードの実行結果です。
小数を表示するため、書式指定文字列が%d
から%f
に代わっていることに注意してください。
ミリ秒が欲しい場合は単純に1000を掛ければOKです。