日時の取得

現在の日付や時間の情報を得る

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

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

time関数とtime_t型

現在の日時を取得するにはtime関数を使用します。
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(&t1);

	time_t t2 = time(NULL);
}

localtime関数とtm構造体

time関数で得られるtime_t型は「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関数を使用します。

ローカルタイムというのは国ごとの時差を考慮した時間のことです。
日本ならUNIX時間に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構造体のメンバ変数の「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型変数のアドレスを指定します。

戻り値はエラーを示す番号で、0以外が返ってくるとエラーが発生したことになります。

このコードのtm構造体はポインタ変数から通常の変数に変更されていることに注意してください。
ポインタ変数ではないので、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構造体の情報を文字列に整形します。
具体的には以下のように使用します。

#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 タイムゾーン名 東京(標準時)

mktime関数

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

time_t mktime( struct tm *timeptr );
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_wday」と「tm_yday」は自動的に適切な値に再計算されます。
戻り値のtime_t型は必要がなければ受けとらなくても良いです。

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秒となります。