日時の取得

C言語では現在の日時を取得するための標準関数、およびデータ型が用意されています。
これらは多少クセがあるので、その使い方を説明します。

日時に関する関数やデータ型を使用するには<time.h>をインクルードする必要があります。

time関数とtime_t型

現在の日時を取得するにはtime関数を使用します。

time_t time(
 time_t *destTime
)
現在の時刻を取得してdestTimeに格納する。
戻り値は取得した時刻。

アドレスやポインタについてはポインタを参照してください。

time関数で得られる時刻はtime_t型の変数に格納されます。
引数はtime_t型のポインタ変数を指定するほか、NULLを指定することもできます。
戻り値も取得した時刻なので、そのままtime_t型変数に代入できます。


#include <stdio.h>
#include <time.h>

int main()
{
	time_t t1;
    time_t t2;

    //どちらでも良い
	time(&t1);
	t2 = time(NULL);
}

なお、得られる時間はシステム(パソコン等)が使用している時刻です。

localtime関数とtm構造体

time関数で得られる値は「1970年1月1日の0時0分0秒からの秒数」です。
これはUNIX時間と言います。

そのままでは扱いにくいので「○月○日、○時○分」という形式に変換します。
それにはlocaltime関数とtm構造体を使用します。

struct tm *localtime(
 const time_t *sourceTime
)
sourceTimeをローカルタイムゾーンに修正する。
戻り値は修正した時刻を格納するtm構造体へのポインタ。

#include <stdio.h>
#include <time.h>

int main()
{
	time_t t = time(NULL);
	
	struct tm *local = localtime(&t);
}

VisualStudio既定の設定ではlocaltime関数を使用するとエラーとなります。
VisualStudioでは後述するlocaltime_s関数を使用します。

ローカルタイムというのは国ごとの時差を考慮した時間のことです。
日本なら協定世界時(UTC)に9時間をプラスした時刻となります。

localtime関数は時刻を秒数ではなくtm構造体で返します。
tm構造体のメンバは以下です。

tm_year
1900年からの年
tm_mon
1月からの月
(0~11)
tm_mday
tm_hour
tm_min
tm_sec
tm_wday
日曜日からの日数
(0~6)
tm_yday
1月1日からの日数
(0~365)
tm_isdst
夏時間フラグ
夏時間を採用しているときは正の値
採用していないときは0
この情報が得られないときは負の値

具体的には以下のように使用します。


#include <stdio.h>
#include <time.h>

int main()
{
	time_t t = time(NULL);
	struct tm *local = localtime(&t);

	printf("%04d/", local->tm_year + 1900);
	printf("%02d/", local->tm_mon + 1);
	printf("%02d", local->tm_mday);

	printf(" ");

	printf("%02d:", local->tm_hour);
	printf("%02d:", local->tm_min);
	printf("%02d\n", local->tm_sec);

	char* const wdays[] = { "日", "月", "火", "水", "木", "金", "土" };

	printf("今日は%s曜日\n", wdays[local->tm_wday]);

	getchar();
}
2012/01/23 01:23:45
今日は月曜日

localtime関数はtime_t型変数のアドレスを引数に指定します。
得られるのはtm構造体のポインタなので、メンバにアクセスするにはアロー演算子を使用します。

tm構造体のメンバ変数のtm_yearは年を表しますが、これは1900年から数えた年なので、現在の暦に変換するには1900をプラスします。
tm_monは月を表しますが、1月を「0」とするので、これも現在の暦に変換するために1をプラスします。

上記以外のメンバはそのままの形で使用できますが、曜日を表すメンバ変数tm_wdayは日曜日を「0」とした「0~6」の値なので、曜日を文字で表示するには数値を見て適切に変換する必要があります。

localtime_s、localtime_r関数

localtime関数にはより安全性を高めたlocaltime_s関数が存在します。
(localtime関数はマルチスレッドプログラムから使用すると安全ではない場合があるそうです)

localtime_s関数はC言語(C11)での仕様とVisualStudioの実装が異なっています。
gccというコンパイラにはlocaltime_s関数がなく、localtime_r関数が存在します。
どちらかというとgccのlocaltime_r関数の方がC言語の仕様に近いです。

VisualStudio

まずはVisualStudioでの使用方法から。

errno_t localtime_s(
 struct tm* const tmDest,
 time_t const* const sourceTime
);
sourceTimeをローカルタイムゾーンに修正し、tmDestに格納する。
正常終了すると0を、エラーが発生した場合は0以外を返す。

#include <stdio.h>
#include <time.h>

int main()
{
	time_t t = time(NULL);
	struct tm local;
	errno_t err = localtime_s(&local, &t);

	if (err != 0)
	{
		printf("エラー発生\n");
		getchar();
		return 0;
	}

	printf("%04d/", local.tm_year + 1900);

	//以降は省略
}

localtime_s関数の第一引数はtm構造体のアドレスを指定します。
第二引数はtime_t型変数のアドレスを指定します。

戻り値はerrno_t型(実体は整数型)で、0以外が返ってくるとエラーが発生したことになります。

このコードのtm構造体はポインタ変数から通常の変数に変更されていることに注意してください。
ポインタ変数ではないので、メンバへのアクセスはアロー演算子ではなくドット演算子になっています。

gcc

次に、gccでの使用例を示します。

struct tm *localtime_r(
 const time_t *restrict timer,
 struct tm *restrict tp
);
timerをローカルタイムゾーンに修正し、tpに格納する。
戻り値はtp。

#include <stdio.h>
#include <time.h>

int main()
{
	time_t t = time(NULL);
	struct tm local;
	localtime_r(&t, &local);

	printf("%04d/", local.tm_year + 1900);

	//以降は省略

関数名、引数の順序、戻り値がそれぞれ異なりますが、同じ結果を得ることができます。

strftime関数

先ほどのサンプルコードではtm構造体のメンバをprintf関数で加工して日時を表示していましたが、strftime関数を使用して日時を文字列にすることもできます。

size_t strftime(
 char *strDest,
 size_t maxsize,
 const char *format,
 const struct tm *timeptr
);
文字列strDest(サイズはmaxsize)に、format文字列に従って日時timeptrを整形する。
戻り値は書き込んだサイズ。

これはprintf関数に似ており、書式指定文字列(変換指定子)に従ってtm構造体の情報を文字列に整形します。
ただしprintf関数の変換指定子ではなく独自のものを使用します。
具体的には以下のように使用します。


#include <stdio.h>
#include <time.h>

int main()
{
	time_t t = time(NULL);
	struct tm *local = localtime(&t);

	char buf[128];
	strftime(buf,
		sizeof(buf),
		"%Y/%m/%d %H:%M:%S %A",
		local);

	printf("%s", buf);
	getchar();
}
2012/01/23 01:23:45 Monday

日時の表示のいくつかはロケールに依存するので、必要に応じてsetlocale関数を使用します。
第一引数はLC_TIMEを指定します。
(VisualStudioではタイムゾーンの表示にLC_CTYPEの指定が必要でした。面倒ならLC_ALLでOK)


#include <stdio.h>
#include <time.h>
#include <locale.h>

int main()
{
	setlocale(LC_TIME, "");

    //または

	setlocale(LC_ALL, "");
}

変換指定子には以下があります。
表現形式は「2012年1月23日 1時23分45秒」を変換した場合を例示しています。
(2012年=平成24年)

変換指定子 説明 VC++
%Y 年の4桁表現 2012
%EY 年の代替表現(ロケール依存) 平成24年 ×
%y 年の下2桁 12
%Oy 年の下2桁の代替表現(ロケール依存) 十二 ×
%Ey 年の元号表現(ロケール依存) 24 ×
%C 年の上2桁 20
%EC 年の元号 平成 ×
%G ISO8601に基く、その週を含む年
(年最初の木曜日を含む週がその年の最初の週)
2012
%g ISO8601に基く、その週を含む年の下2桁
(年最初の木曜日を含む週がその年の最初の週)
12
%b 月の省略形(ロケール依存) Jan
%h 月名の省略形(ロケール依存) 1月
%b %bと同義 1月
%B 月名(ロケール依存) January
%m 月の数値表現(01~12) 1
%Om 月の代替表現(ロケール依存) ×
%j 日を年初からの通しで表現(001~366) 023
%d 日の数値表現(01~31) 23
%Od 日の代替表現(ロケール依存) 二十三 ×
%e 日の数値表現(1~31)
一桁の場合は先頭に空白が付加される
23
%Oe 日の代替表現(ロケール依存)
一桁の場合は先頭に空白が付加される
二十三 ×
%U 週番号(00~53) 04
%OU 週番号の代替表現(ロケール依存) ×
%W 週番号(00~53) 04
%OW 週番号の代替表現(ロケール依存) ×
%V ISO8601に基く週番号(01~53)
(年最初の木曜日を含む週がその年の最初の週)
04
%OV ISO8601に基く週番号の代替表現(ロケール依存)
(年最初の木曜日を含む週がその年の最初の週)
×
曜日
%a 曜日名の省略形
%A 曜日名 月曜日
%w 曜日の数値表現(0~6)
(日曜日が0)
1
%Ow 曜日の代替表現(ロケール依存) ×
%u ISO8601に基く曜日の数値表現
(月曜日が1)
1
%Ou ISO8601に基く曜日の代替表現(ロケール依存)
(月曜日が1)
×
時間
%H 時の24時間表記(00~23) 01
%OH 時の24時間表記の代替表現(ロケール依存) ×
%I 時の12時間表記(01~12)
%OI 時の12時間表記の代替表現(ロケール依存) ×
%M 分(00~59) 23
%OM 分の代替表現(ロケール依存) 二三 ×
%S 秒(00~59) 45
%H 秒の代替表現(ロケール依存) 四五 ×
その他
%% %を書き込む %
%n 改行文字
%t タブ文字
%c 日付と時刻(ロケール依存) 2012/01/23 1:23:45
%Ec 日付と時刻の代替表現(ロケール依存) 平成24年01月23日 01時23分45秒 ×
%x 日付(ロケール依存) 2012/01/23
%Ex 日付の代替表現(ロケール依存) 平成24年01月23日 ×
%X 時刻(ロケール依存) 1:23:45
%EX 時刻の代替表現(ロケール依存) 01時23分45秒5 ×
%D 「%m/%d/%y」と同等 01/23/12
%F 「%Y-%m-%d」と同等 2012-01-23
%r 時刻の12時間表記(ロケール依存) 1:23:45
%R 「%H:%M」と同等 01:23
%T 「%H:%M:%S」と同等 01:23:45
%p 午前または午後(ロケール依存) 午前
%z UTCからのオフセット(時差) +0900
%Z タイムゾーン名 東京(標準時)

ctime関数、asctime関数

strftime関数を使用すれば時間を好きな形式で文字列化可能ですが、固定の形式で良い場合にはctime関数またはasctime関数が使用できます。
このふたつは引数がtime_t型かtm構造体かの違いだけで、出力される文字列は同じです。


#include <stdio.h>
#include <time.h>

int main()
{
	time_t t = time(NULL);

	//どちらも同じ
	const char* nowStr1 = ctime(&t);
	const char* nowStr2 = asctime(localtime(&t));

	printf("%s", nowStr1);

	getchar();
}
Sat Feb  3 12:34:56 2001
char* ctime(
 const time_t* sourceTime
)
sourceTimeを文字列に変換し、そのポインタを返す。
char* asctime(
 const struct tm* tmSource
)
tmSourceを文字列に変換し、そのポインタを返す。

返される文字列の形式は以下のようになっています。


//曜日 月  日 時:分:秒 年\n\0
Sat Feb  3 12:34:56 2001

文字数はNULL文字を含めて26文字で固定です。
最後のNULL文字の手前に改行が入っていることに注意してください。

ctime_r、ctime_s、asctime_r、asctime_s関数

ctime関数とasctime関数にもセキュリティ強化版があります。
これもVisual Studioでは_s系を、その他のコンパイラでは_r系を使用します。

ctime_s関数、asctime_s関数


#include <stdio.h>
#include <time.h>

int main()
{
	char nowTime[26];
	time_t t = time(NULL);

	struct tm local;
	localtime_s(&local, &t);
	
	//どちらも同じ
	ctime_s(nowTime, 26, &t);
	asctime_s(nowTime, 26, &local);

	printf("%s", nowTime);

	getchar();
}
errno_t ctime_s(
 char *buffer,
 size_t bufferSize,
 const time_t *sourceTime
);
sourceTimeを文字列に変換してbufferに格納する。
正常終了すると0を、エラーが発生した場合は0以外を返す。
errno_t ctime_s(
 char *buffer,
 size_t bufferSize,
 const struct tm *tmSource
);
tmSourceを文字列に変換してbufferに格納する。
正常終了すると0を、エラーが発生した場合は0以外を返す。

ctime_r、asctime_r関数


#include <stdio.h>
#include <time.h>

int main()
{
	char nowTime[26];
	time_t t = time(NULL);

	struct tm local;
	localtime_r(&t, &local);
	
	//どちらも同じ
	ctime_r(&t, nowTime);
	asctime_r(&local, nowTime);

	printf("%s", nowTime);

	getchar();
}
char *ctime_r(
 const time_t *sourceTime
 char *buffer,
);
sourceTimeを文字列に変換してbufferに格納する。
戻り値はbuffer(ポインタ)。
char *ctime_r(
 const struct tm *tmSource
 char *buffer,
);
tmSourceを文字列に変換してbufferに格納する。
戻り値はbuffer(ポインタ)。

引数の数、順序、戻り値が異なりますが使い方は基本的に同じです。
得られる文字列もctime関数、asctime関数と同じです。

mktime関数

日時は現在のものを取得するほか、tm構造体に好きな値を書き込んで変換することもできます。
変換にはmktime関数を使用します。

time_t mktime(
 struct tm *timeptr
);
tm構造体timeptrをtime_t型に変換して返す。

mktime関数はtm構造体をtime_t型に変換するための関数ですが、tm構造体を正規化することもできます。


#include <stdio.h>
#include <time.h>

int main()
{
	struct tm tmTime = { 0 };

	//2001年2月3日 12:34:56を設定
	tmTime.tm_year = 2001 - 1900;
	tmTime.tm_mon = 2 - 1;
	tmTime.tm_mday = 3;

	tmTime.tm_hour = 12;
	tmTime.tm_min = 34;
	tmTime.tm_sec = 56;

	//tm構造体をtime_t型に変換
	time_t t = mktime(&tmTime);

	char buf[128];
	size_t s = strftime(buf,
		sizeof(buf),
		"%Y/%m/%d %H:%M:%S %A",		
		&tmTime);

	printf("%s", buf);
    getchar();
}
2001/02/03 12:34:56 Saturday

tm構造体の「年月日」を設定してmktime関数を実行することで、メンバ変数tm_wdaytm_ydayは自動的に適切な値に再計算されます。

difftime関数

difftime関数は二つの時刻の差を秒数で返します。

double difftime(
 time_t timeEnd,
 time_t timeStart
);
timeStartからtimeEndの時間差を秒数で返す。

#include <stdio.h>
#include <time.h>

int main()
{
	struct tm tmTimeA = { 0 };
	//2001年2月3日 12:34:56を設定
	tmTimeA.tm_year = 2001 - 1900;
	tmTimeA.tm_mon = 2 - 1;
	tmTimeA.tm_mday = 3;
	tmTimeA.tm_hour = 12;
	tmTimeA.tm_min = 34;
	tmTimeA.tm_sec = 56;

	struct tm tmTimeB = { 0 };
	//2001年2月4日 12:34:56を設定
	tmTimeB.tm_year = 2001 - 1900;
	tmTimeB.tm_mon = 2 - 1;
	tmTimeB.tm_mday = 4;
	tmTimeB.tm_hour = 12;
	tmTimeB.tm_min = 34;
	tmTimeB.tm_sec = 56;

	double diff = difftime(
		mktime(&tmTimeB),
		mktime(&tmTimeA));
	printf("%f", diff);
    getchar();
}
86400.000000

引数にはtime_t型を指定するため、mktime関数の戻り値をそのまま指定しています。
両者の時間差はちょうど丸一日に設定しているので「60秒×60分×24時間」で86400秒となります。

clock関数

プログラムを開始してからの時間を取得するにはclock関数を使用します。


#include <time.h>
#include <stdio.h>

int main(void)
{
	clock_t start, end;

	printf("Enterキーを押下してください。\n");

	start = clock();

	getchar();

	end = clock();

	printf("%f秒でした。", (double)(end - start) / CLOCKS_PER_SEC);

	getchar();
}
Enterキーを押下してください。

1.837000秒でした。

clock関数はclock_tというデータ型で値を返します。
具体的な精度は環境によって異なります。
clock_t型の値を秒数に変換するためにはCLOCKS_PER_SECという定数で割る必要があります。

上記のコードはgetchar関数で入力待ちを発生させ、Enterキーが押下されるまでの時間を計測しています。
ある処理の前後でclock関数を実行することで、その処理にかかった時間を計測することができます。

コードの簡略化のために標準入力のクリア処理は行っていないので、Enterキー以外を入力した場合は即座にプログラムが終了してしまうので注意してください。