プログラムで現在時刻を扱うとき、秒単位のUNIX時刻は扱いやすく、ログや乱数の初期化などさまざまな用途に向いています。
本記事では、C言語の<time.h>に含まれるtime
関数を使って現在のUNIX時刻(秒)を取得する最も基本的で確実な方法を、初心者の方にもわかりやすく解説します。
C言語のUNIX時刻(秒)の基本
UNIX時刻(秒)とは何か
UNIX時刻とは、1970-01-01 00:00:00 UTCからの経過秒数を整数で表現したものです。
タイムゾーンの影響を受けず、世界中で同じ数値になるため、計算や比較が簡単です。
閏秒は通常考慮せず、秒が一定間隔で進む前提の扱いが一般的です。
ここがポイント
UNIX時刻は「世界共通の秒カウンタ」です。
人間が読む日時ではなく、機械が扱いやすい整数の時間表現だと理解してください。
基準時刻は1970-01-01
UNIX時刻の基準(エポック)は1970-01-01 00:00:00 UTCです。
例えば、UNIX時刻が0
ならこの瞬間、1
なら1秒後を表します。
ローカルタイム(日本時間など)の考慮は不要で、すべてUTC基準でカウントされます。
補足
ローカルタイムに変換したい場合は別の関数が必要です。
本記事では取得のみに焦点を当てます。
C言語のtime関数とは
C言語のtime
関数は次のシグネチャを持ちます。
time_t time(time_t *tloc);
現在のUNIX時刻(秒)をtime_t
型で返す関数です。
引数tloc
に非NULLのポインタを渡すと、そのアドレスにも同じ値を書き込みます。
覚えておくこと
- 返り値は現在時刻の秒数です。
- 取得に失敗した場合は
(time_t)-1
を返します。 - ヘッダ
#include <time.h>
が必要です。
time関数で現在のUNIX時刻(秒)を取得する
必要なヘッダ
基本的には#include <time.h>
で十分です。
取得した値を表示するには#include <stdio.h>
、安全に整数表示するには#include <inttypes.h>
が役立ちます。
サンプルの前提
以降のサンプルでは%jd
と(intmax_t)
キャストで表示します。
これは環境に依存して幅が異なるtime_t
を安全に表示する一般的な方法です。
もっとも簡単な呼び出し(time(NULL))
time(NULL)
とすると、現在のUNIX時刻を単純に返します。
まずは最小限のコードで値を表示してみます。
// 最小サンプル: 現在のUNIX時刻(秒)を取得して表示
#include <stdio.h> // printf
#include <time.h> // time, time_t
#include <inttypes.h> // intmax_t, %jd
int main(void) {
// 現在のUNIX時刻(秒)を取得。失敗時は (time_t)-1 が返る
time_t now = time(NULL);
if (now == (time_t)-1) {
// C標準では errno の規定はありません。一般的な失敗メッセージを出します。
fputs("time関数の呼び出しに失敗しました\n", stderr);
return 1;
}
// time_t は環境依存の幅なので intmax_t にキャストし %jd で安全に表示します
printf("%jd\n", (intmax_t)now);
return 0;
}
実行結果の例(環境や実行時刻により変わります):
1739421193
補足
出力はただの整数です。
人間が読みやすい日付文字列ではありませんが、比較や計算に非常に向いています。
変数に結果を受け取る(time(&変数))
ポインタを渡すと、引数の変数に結果を書き込み、かつ同じ値を返します。
両者が一致することを簡単に確かめてみましょう。
// time(&変数) で変数へも結果を格納し、返り値でも同じ値を得るサンプル
#include <stdio.h>
#include <time.h>
#include <inttypes.h>
int main(void) {
time_t now;
// now へ格納され、返り値 ret にも同じUNIX時刻が返ります
time_t ret = time(&now);
if (ret == (time_t)-1) {
fputs("timeが失敗しました\n", stderr);
return 1;
}
printf("now=%jd, ret=%jd\n", (intmax_t)now, (intmax_t)ret);
return 0;
}
now=1739421193, ret=1739421193
使い分け
time(NULL)
: 単に返り値だけ使いたい場合に簡潔です。time(&変数)
: 変数にも格納しつつ同じ値を返してほしい場合に便利です。
エラーの確認
標準Cではtime
の失敗時に(time_t)-1
が返ると定められています。
実運用で現在時刻の取得が本当に失敗するのは稀ですが、チェックは入れておくのがよい習慣です。
// エラーチェックの定石: 返り値が (time_t)-1 かどうかを確認する
#include <stdio.h>
#include <time.h>
#include <inttypes.h>
int main(void) {
time_t t = time(NULL);
if (t == (time_t)-1) {
// POSIX環境では errno が設定される場合もありますが、標準Cでは未規定です
fputs("現在時刻の取得に失敗しました\n", stderr);
return 1;
}
printf("unix_time=%jd\n", (intmax_t)t);
return 0;
}
unix_time=1739421193
注意として、理論上(time_t)-1
は「1969-12-31 23:59:59 UTC」を表す有効な時刻でもあります。
ただし「現在時刻」を取得する用途では事実上同値になることはありません。
現代の環境での実用上はこのチェックで十分です。
初心者向けの注意点
time_tの型は環境依存の整数
time_t
は環境により幅(32/64ビットなど)が異なる整数型です。
ゆえにprintfで直接%ld
や%lld
と決め打ちせず、(intmax_t)
へキャストして%jd
で表示すると安全です。
32ビット環境ではいわゆる「2038年問題」がありますが、近年の多くの環境では64ビット化されており当面の心配は少なくなっています。
環境の例として、次のような傾向があります(代表例であり、実際はツールチェインとOSに依存します)。
代表的な環境 | time_tのサイズ | 備考 |
---|---|---|
64ビットLinux(glibc) | 64ビット | デフォルトで2038年以降も扱えます |
32ビットLinux(古いglibc) | 32ビット | 2038年問題があります |
Windows(Visual C++ 2015以降) | 64ビット | time_t は64ビットが既定です |
表示の実務的コツ
- 一番簡単:
printf("%jd", (intmax_t)t)
- プロジェクト共通ヘルパを用意し、表示方法を統一すると混乱を防げます。
取得できるのは秒単位のみ
time関数で得られるのは秒単位の精度だけです。
より高精度(ミリ秒やナノ秒)が必要な場合は、C11のtimespec_get
やPOSIXのclock_gettime
、歴史的にはgettimeofday
など別APIを検討してください。
本記事では詳細は扱いません。
連続で呼ぶと同じ秒になることがある
とても短い間隔でtime
を連続呼び出しすると、同じ秒を返すことが普通にあります。
同一秒内のイベント識別に使う場合は、1回だけ取得して使い回す、カウンタやPIDを併用するなどの工夫をすると安全です。
// 連続で呼ぶと同じ秒が返る可能性のデモ
#include <stdio.h>
#include <time.h>
#include <inttypes.h>
int main(void) {
for (int i = 0; i < 5; ++i) {
time_t t = time(NULL);
if (t == (time_t)-1) {
fputs("timeに失敗しました\n", stderr);
return 1;
}
printf("%d: %jd\n", i, (intmax_t)t);
// あえて待たないので、同一秒が続く可能性があります
}
return 0;
}
0: 1739421193
1: 1739421193
2: 1739421193
3: 1739421194
4: 1739421194
取得したUNIX時刻(秒)のシンプルな使い道
乱数の初期化
もっとも手軽な用途はsrand
への種(seed)として使うことです。
毎回違うタイミングでプログラムを起動すれば乱数列が変わります。
// UNIX時刻(秒)で rand() のシードを初期化する例
#include <stdio.h>
#include <stdlib.h> // srand, rand
#include <time.h> // time
#include <inttypes.h> // intmax_t
int main(void) {
time_t now = time(NULL);
if (now == (time_t)-1) {
fputs("時刻取得に失敗しました\n", stderr);
return 1;
}
// srand は unsigned int を受け取るのでキャストします
srand((unsigned int)now);
// 適当に3つ乱数を出力
printf("seed=%jd\n", (intmax_t)now);
printf("rand #1=%d\n", rand());
printf("rand #2=%d\n", rand());
printf("rand #3=%d\n", rand());
return 0;
}
seed=1739421193
rand #1=1804289383
rand #2=846930886
rand #3=1681692777
注意として、乱数の安全性が重要な用途(暗号など)ではrand
は不適切です。
暗号論的に安全な乱数ソースの利用を検討してください。
ログや一時ファイル名のタイムスタンプ
ファイル名にUNIX時刻を埋め込めば、ソートしやすく衝突も起きにくい命名ができます。
同一秒内に複数生成する場合は、カウンタやプロセスIDを併用するとより安全です。
// UNIX時刻を使ってログファイル名を作る例
#include <stdio.h>
#include <time.h>
#include <inttypes.h>
int main(void) {
time_t now = time(NULL);
if (now == (time_t)-1) {
fputs("時刻取得に失敗しました\n", stderr);
return 1;
}
char filename[64];
// 例: app_1739421193.log のような名前を作る
// time_t の表示は intmax_t 経由で安全に
snprintf(filename, sizeof filename, "app_%jd.log", (intmax_t)now);
printf("log filename: %s\n", filename);
return 0;
}
log filename: app_1739421193.log
補足として、同一秒に複数作るならapp_%jd_%d.log
のようにカウンタやPIDを追加すると衝突を避けられます。
まとめ
本記事では、C言語で現在のUNIX時刻(秒)をtime
関数で取得する基本を解説しました。
UNIX時刻はUTC基準で1970-01-01からの経過秒であり、time(NULL)
またはtime(&変数)
で簡単に得られます。
エラーチェックは(time_t)-1
との比較を行い、表示には(intmax_t)
と%jd
を使うと環境差に強くなります。
得られる精度は秒である点と、連続呼び出しで同じ値になりうる点に注意してください。
実用面では乱数の初期化やログファイル名のタイムスタンプなどにすぐ活用できます。
まずは最小サンプルをコンパイルし、数値が変わることを確認するところから始めてみてください。