C言語で現在時刻を扱ったり、プログラムの実行時間を測定したりしたい場合、標準ライブラリのtime関数を理解しておくことがとても重要です。
本記事では、初心者の方でも迷わないように、time関数の基本から、現在時刻の取得方法、経過時間の測定方法、そして注意点やよくある疑問まで、図解とサンプルコードを交えながら丁寧に解説します。
C言語のtime関数とは
time関数の基本概要と役割
time関数は、C言語の標準ライブラリで提供されている、現在の「カレンダー時刻」を取得するための関数です。
time関数は次のようなイメージで動作します。

time関数は、1970年1月1日0時0分0秒(UTC)からの経過秒数を整数値として返します。
この値を使うことで、現在時刻を表現したり、2つの時刻の差から経過時間を計算したりできます。
代表的な用途として、次のような場面で利用されます。
- 現在の日付や時刻を画面に表示したいとき
- ログファイルに日時を記録したいとき
- 処理にどれくらい時間がかかったか計測したいとき
time_t型とは何か
time関数の戻り値にはtime_t型が使われます。
これは、カレンダー時刻を表現するための整数型です。

重要なポイントは、time_tの中身のビット幅や符号付きかどうかは処理系によって異なるという点です。
多くの環境では次のいずれかになっています。
- 32bit符号付き整数
- 64bit符号付き整数
そのため、プログラム中でlongなどに勝手にキャストして使うと、環境によっては桁あふれや符号の違いによる問題が発生する可能性があります。
基本的にはtime_tのまま扱い、出力するときだけ注意して表示形式を選ぶことが大切です。
time関数を使うためのヘッダファイル
time関数やtime_t型を使うには、必ず#include <time.h>を記述する必要があります。
#include <stdio.h>
#include <time.h> /* time関数, time_t, struct tm などを使うためのヘッダ */
int main(void) {
time_t now; /* 現在時刻を格納する変数 */
now = time(NULL); /* 現在時刻を取得 */
printf("now = %ld\n", (long)now);
return 0;
}
上のコードでは、time.hをインクルードすることで、time関数やtime_t型が使えるようになります。
time関数で現在時刻を取得する
time関数の基本的な使い方
time関数の基本的な宣言は次のようになっています。
time_t time(time_t *timer);
この関数には2通りの使い方があります。
1つ目は、引数にNULLを渡す方法です。
この場合、time関数は現在時刻を返り値として返すだけです。
time_t now = time(NULL); /* 現在時刻を取得 */
2つ目は、time_t型の変数のポインタを渡す方法です。
この場合、time関数は*timerにも現在時刻を書き込み、同じ値を返り値として返します。
time_t now;
time(&now); /* nowに現在時刻が書き込まれる。戻り値も同じ値 */

実務では、簡潔に書けるtime(NULL)の形がよく使われます。
現在時刻をprintfで数値として表示する例
現在時刻をそのまま秒数として表示するだけなら、とてもシンプルです。
#include <stdio.h>
#include <time.h>
int main(void) {
/* 現在時刻を格納するtime_t型の変数 */
time_t now;
/* 現在時刻(1970-01-01からの経過秒数)を取得 */
now = time(NULL);
/* time_tは環境によってサイズが異なるので、longにキャストして出力する例 */
printf("現在時刻(UNIX時刻): %ld 秒\n", (long)now);
return 0;
}
現在時刻(UNIX時刻): 1733960625 秒
ここで表示されている数値は、1970年1月1日0時0分0秒(UTC)からの経過秒数です。
このままでは人間には読みにくいので、次の節でstruct tmを使って「年月日・時刻」に変換していきます。
timeとlocaltimeで日時(年・月・日・時刻)に変換する方法
人間が読める形式にするには、time_t → struct tmという変換を行います。
その代表的な関数がlocaltimeです。
localtimeは、ローカルタイム(PCのタイムゾーンに合わせた時刻)に変換してくれます。

#include <stdio.h>
#include <time.h>
int main(void) {
time_t now; /* 現在時刻(秒数)用 */
struct tm *local_time; /* 分解した時刻(ローカルタイム)へのポインタ */
/* 現在のカレンダー時刻を取得 */
now = time(NULL);
/* time_t → struct tm へ変換(ローカルタイム) */
local_time = localtime(&now);
/* 念のためNULLチェック(変換に失敗した場合) */
if (local_time == NULL) {
printf("localtimeの変換に失敗しました。\n");
return 1;
}
/* struct tmのメンバから「年・月・日・時刻」を取り出して表示 */
printf("現在のローカル時刻は %d年 %d月 %d日 %d時 %d分 %d秒 です。\n",
local_time->tm_year + 1900, /* tm_yearは1900年からの年数 */
local_time->tm_mon + 1, /* tm_monは0〜11なので+1する */
local_time->tm_mday, /* 日(1〜31) */
local_time->tm_hour, /* 時(0〜23) */
local_time->tm_min, /* 分(0〜59) */
local_time->tm_sec); /* 秒(0〜60: うるう秒を考慮) */
return 0;
}
現在のローカル時刻は 2025年 12月 12日 10時 23分 45秒 です。
struct tm構造体には、主に次のようなメンバが用意されています。
| メンバ名 | 意味 | 値の範囲など |
|---|---|---|
| tm_year | 西暦1900年からの年数 | 例: 2025年なら125 |
| tm_mon | 月(0から始まる) | 0〜11 (0が1月, 11が12月) |
| tm_mday | 日 | 1〜31 |
| tm_hour | 時 | 0〜23 |
| tm_min | 分 | 0〜59 |
| tm_sec | 秒 | 0〜60(うるう秒を考慮するため最大60) |
| tm_wday | 曜日(0:日曜〜6:土曜) | 0〜6 |
| tm_yday | 1月1日からの通算日数(0始まり) | 0〜365 |
| tm_isdst | サマータイムの有無 | 正:有効, 0:無効, 負:情報なし |
年と月にオフセットを加える必要がある点が特につまずきやすいので、忘れないようにしてください。
timeとgmtimeでUTCの時刻に変換する方法
localtimeはローカルタイムに変換しましたが、世界共通の基準時刻(UTC)で扱いたい場合はgmtimeを使います。

#include <stdio.h>
#include <time.h>
int main(void) {
time_t now;
struct tm *utc_time;
now = time(NULL);
/* UTC(協定世界時)としての時刻に変換 */
utc_time = gmtime(&now);
if (utc_time == NULL) {
printf("gmtimeの変換に失敗しました。\n");
return 1;
}
printf("現在のUTC時刻は %d-%02d-%02d %02d:%02d:%02d です。\n",
utc_time->tm_year + 1900,
utc_time->tm_mon + 1,
utc_time->tm_mday,
utc_time->tm_hour,
utc_time->tm_min,
utc_time->tm_sec);
return 0;
}
現在のUTC時刻は 2025-12-12 01:23:45 です。
日本(UTC+9)などのタイムゾーンでは、localtimeとgmtimeで「時刻」に差が出ます。
ログや通信プロトコルなど、世界中で同じ時刻基準を使いたいときにはUTCが利用されます。
asctimeとctimeで文字列として現在時刻を表示する
時刻を1本の文字列にまとめて表現したいとき、asctimeとctimeを使うと手軽です。

#include <stdio.h>
#include <time.h>
int main(void) {
time_t now;
struct tm *local_time;
char *str1;
char *str2;
now = time(NULL);
/* 1) localtime + asctime の組み合わせ */
local_time = localtime(&now);
if (local_time == NULL) {
printf("localtimeに失敗しました。\n");
return 1;
}
/* struct tm を「Sun Sep 16 01:03:52 1973\n\0」のような文字列に変換 */
str1 = asctime(local_time);
if (str1 == NULL) {
printf("asctimeに失敗しました。\n");
return 1;
}
/* 2) time_t から ctime で直接文字列に変換 */
str2 = ctime(&now);
if (str2 == NULL) {
printf("ctimeに失敗しました。\n");
return 1;
}
printf("asctimeの結果: %s", str1);
printf("ctimeの結果 : %s", str2);
return 0;
}
asctimeの結果: Fri Dec 12 10:23:45 2025
ctimeの結果 : Fri Dec 12 10:23:45 2025
これらの文字列には末尾に改行('\n')が含まれている点に注意してください。
そのため、printfで表示するときには、追加の改行を付けなくてもすでに1行として整形されています。
ただし、asctimeとctimeはスレッドセーフではない実装が多いことや、フォーマットが固定で変えられないなどの制約があるため、実務ではstrftime関数を使って自分でフォーマットを指定する方法もよく使われます。
time関数で経過時間を測定する
time関数で処理時間(経過秒数)を計測する基本手順
time関数は、2つの時刻の差をとることで経過時間(秒)を求めることができます。
基本的な流れは次のようになります。

- 処理の前に
start = time(NULL);で開始時刻を取得する - 計測したい処理を実行する
- 処理の後に
end = time(NULL);で終了時刻を取得する end - startで経過秒数を計算する
この方法は秒単位のざっくりとした処理時間を知るのに向いています。
開始時刻と終了時刻の差分を計算する方法
経過時間を求める一番シンプルな形は、time_t同士の差をとることです。
#include <stdio.h>
#include <time.h>
int main(void) {
time_t start, end; /* 開始時刻と終了時刻 */
long elapsed; /* 経過時間(秒)を入れる変数 */
/* 計測開始時刻を取得 */
start = time(NULL);
/* 時間のかかる処理(ここではダミーとしてループを回す) */
for (volatile long i = 0; i < 500000000L; i++) {
/* 何もしないループ。volatileで最適化されにくくする */
}
/* 計測終了時刻を取得 */
end = time(NULL);
/* time_t同士の差をlongにキャストして保存 */
elapsed = (long)(end - start);
printf("処理にかかった時間: %ld 秒\n", elapsed);
return 0;
}
処理にかかった時間: 2 秒
ここでは簡単のために(long)(end - start)としていますが、time_tがlongより大きい型の環境では桁あふれのリスクがあります。
より安全に計算する方法として次節のdifftimeを使う方法があります。
difftime関数を使った経過時間の安全な求め方
difftime関数は、2つのtime_tの差をdouble型で返すための標準ライブラリ関数です。
宣言は次の通りです。
double difftime(time_t time1, time_t time0);
この関数はtime1 − time0を秒単位で計算し、その結果をdouble型で返します。
内部で型の違いや符号などを考慮してくれるため、直接の減算よりも安全で移植性が高いです。
#include <stdio.h>
#include <time.h>
int main(void) {
time_t start, end;
double elapsed; /* difftimeの戻り値を受け取る */
start = time(NULL);
/* ダミーの重い処理 */
for (volatile long i = 0; i < 500000000L; i++) {
/* 何もしない */
}
end = time(NULL);
/* end - start を安全に計算 */
elapsed = difftime(end, start);
printf("処理にかかった時間: %.0f 秒\n", elapsed);
return 0;
}
処理にかかった時間: 2 秒
表示フォーマットを%.2fなどにすれば、小数点以下も表示できますが、time関数自体が秒単位でしか時刻を返さないため、通常は整数秒として扱われます。
簡単な処理時間計測サンプルコード
ここまで紹介した内容をまとめて、処理の前後で時間を測定し、開始時刻・終了時刻・経過時間を分かりやすく表示するサンプルを示します。

#include <stdio.h>
#include <time.h>
int main(void) {
time_t start, end; /* 開始時刻と終了時刻 */
struct tm *tm_start, *tm_end;
double elapsed;
/* 計測開始時刻を取得 */
start = time(NULL);
tm_start = localtime(&start);
if (tm_start == NULL) {
printf("localtime(開始時刻)に失敗しました。\n");
return 1;
}
printf("処理開始: %d-%02d-%02d %02d:%02d:%02d\n",
tm_start->tm_year + 1900,
tm_start->tm_mon + 1,
tm_start->tm_mday,
tm_start->tm_hour,
tm_start->tm_min,
tm_start->tm_sec);
/* ここに計測したい処理を書く */
for (volatile long i = 0; i < 800000000L; i++) {
/* ダミー処理 */
}
/* 計測終了時刻を取得 */
end = time(NULL);
tm_end = localtime(&end);
if (tm_end == NULL) {
printf("localtime(終了時刻)に失敗しました。\n");
return 1;
}
printf("処理終了: %d-%02d-%02d %02d:%02d:%02d\n",
tm_end->tm_year + 1900,
tm_end->tm_mon + 1,
tm_end->tm_mday,
tm_end->tm_hour,
tm_end->tm_min,
tm_end->tm_sec);
/* 経過時間をdifftimeで安全に計算 */
elapsed = difftime(end, start);
printf("処理にかかった時間: %.0f 秒\n", elapsed);
return 0;
}
処理開始: 2025-12-12 10:23:40
処理終了: 2025-12-12 10:23:43
処理にかかった時間: 3 秒
このように、開始時刻と終了時刻の両方を表示しておくと、ログとしても役立ちますし、処理がいつごろ実行されたのかを後から確認しやすくなります。
time関数を使う際の注意点とよくある疑問
time関数の戻り値が-1になる場合の原因と対処
time関数は、時刻の取得に失敗した場合には(time_t)-1を返すと規定されています。
これは、OS側から現在時刻が取得できなかった場合などに発生します。
#include <stdio.h>
#include <time.h>
int main(void) {
time_t now = time(NULL);
if (now == (time_t)-1) {
printf("time関数で現在時刻を取得できませんでした。\n");
return 1;
}
printf("現在時刻の取得に成功しました。\n");
return 0;
}
現在時刻の取得に成功しました。
一般的なデスクトップ環境やサーバ環境では、time関数が失敗するケースはほとんどありません。
しかし、組み込み機器や特殊なOSでは、システムクロックが未設定だったり、RTC(リアルタイムクロック)が存在しないなどの理由で失敗することがあります。
そのため、堅牢なプログラムを書くときには戻り値が(time_t)-1でないか確認することが推奨されます。
time_tの上限と2038年問題
多くの32bit環境では、time_tが32bitの符号付き整数として実装されています。
この場合、表現できる最大値は約2,147,483,647秒であり、これは 2038年1月19日 03時14分07秒(UTC)に相当します。

これを2038年問題と呼びます。
2038年以降の時刻を扱おうとすると、time関数の戻り値やtime_tが正しく表現できなくなり、負の値になったり、日付が1970年付近に戻ってしまったりする可能性があります。
最近のLinuxや64bit環境では、time_tが64bit整数として実装され、この問題を回避していることが多くなっています。
しかし、古い32bit環境や組み込み機器では依然として注意が必要です。
time関数とclock関数の違い
time関数とよく比較されるのがclock関数です。
両者は用途が異なります。
| 関数名 | 主な用途 | 返すもの |
|---|---|---|
| time | カレンダー時刻の取得 | 1970-01-01からの経過「実時間」(秒単位) |
| clock | プログラムのCPU使用時間の取得 | CPU時間(クロック数) |
time関数は、「壁時計上の時間」を知るのに適しています。
一方、clock関数はプログラムがCPUをどれだけ使ったかを測るためのもので、マルチタスク環境では「待ち時間」を含まないこともあります。

処理時間を「秒単位」でざっくり測りたいだけなら、time関数で十分です。
ミリ秒レベルの精度が必要な場合やCPU時間を正確に測定したい場合は、環境に応じてclockまたはそれ以外の高精度タイマAPI(WindowsのQueryPerformanceCounter、POSIXのclock_gettimeなど)を利用します。
WindowsやLinuxでのtime関数の動作の違い
time関数自体はCの標準仕様に従っていますが、実際の動作はOSによって細かな違いがあります。

代表的な違いとして、次の点があります。
- time_tのビット幅
- 古いWindows(Visual C++の古いバージョン)では32bitのtime_tが使われている場合がある
- 64bit Windowsや新しい環境では64bit time_tがデフォルトになっていることも多い
- 多くの64bit Linuxではtime_tは64bit整数
- タイムゾーンとサマータイム(DST)
- localtimeなどの変換は、OSのタイムゾーン設定や環境変数(TZ)に依存する
- WindowsとLinuxでタイムゾーンの設定方法が異なるため、同じソースコードでも出力が微妙に違うことがある
とはいえ、time関数そのものの使い方はどの環境でもほぼ共通です。
移植性を高く保つためには、time_tの取り扱いを乱暴にキャストしない、difftimeを使う、などの工夫が大切です。
初心者がつまずきやすいポイントとデバッグのコツ
time関数や関連APIには、初心者が特につまずきやすいポイントがいくつかあります。
代表的なものと、その対処法・デバッグのコツをまとめます。

struct tmの年や月の値が「そのままでは」使えない
struct tmのtm_yearやtm_monは、そのまま西暦や月としては使えません。
- tm_year: 1900年からのオフセット →
tm_year + 1900で西暦 - tm_mon: 0が1月 →
tm_mon + 1で通常の月
表示がおかしいと感じたら、+1900や+1を忘れていないかを確認してみてください。
ローカルタイムとUTCの混同
localtimeとgmtimeを混同すると、9時間などの差が出て混乱しがちです。
ログを出すときは、どちらの基準で表示しているかをコメントやフォーマットに明示すると良いです。
asctime・ctimeの末尾の改行
asctimeとctimeは、末尾に改行'\n'を含んだ文字列を返します。
printf("%s\n", str);のようにさらに改行を付けると、空行が1行余分に入ることがあります。
デバッグ時に行数がおかしいと感じたら、末尾の改行を意識して確認してみてください。
処理時間が「0秒」になってしまう
time関数は秒単位なので、1秒未満の短い処理時間は0秒と表示されることがあります。
その場合は、次のような工夫が必要です。
- 意図的にループ回数を増やして、時間がかかるようにする
clockや高精度タイマAPIを使う
time関数で測るときは、ある程度時間のかかる処理を対象にするのがコツです。
まとめ
time関数は、現在時刻の取得と経過時間の測定という2つの基本的な役割を担う、C言語プログラミングにおける重要な機能です。
本記事では、time関数とtime_t型の基礎から、localtimeやgmtimeによる構造体への変換、asctimeやctimeによる文字列表示、difftimeを使った安全な経過時間の計算方法まで、一通りの流れを解説しました。
まずはサンプルコードをそのまま動かし、少しずつ表示形式や処理内容を変えながら、時刻と時間計測の扱いに慣れていくことをおすすめします。
