日時の取得
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_wday
とtm_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キー以外を入力した場合は即座にプログラムが終了してしまうので注意してください。