C言語で扱う時刻の基本となるのがtime_t
です。
多くのOSやライブラリでは、UNIX時刻と呼ばれる「1970-01-01 00:00:00」からの経過秒数として実装されています。
この記事では、time_tとは何か、どのような性質があり、どう使うのかを、初心者の方にも分かりやすいように丁寧に解説します。
time_t型とは?C言語のUNIX時刻(エポック秒)の基本
time_tはUTCエポックからの経過秒
多くの環境では、time_t
は「UTCにおけるエポックからの経過秒」を表す算術型です。
ここでいうエポックとは、UNIX系OSで採用される基準時刻のことです。
1秒ごとに1ずつ増える単純なカウンタと考えられ、ローカルタイムのタイムゾーンや夏時間の情報は含みません。
なお厳密にはC言語規格(ISO C)はエポックや単位を固定していませんが、POSIX系の実装(LinuxやmacOSなど)では、実質的に「UNIX時刻の秒」を意味します。
簡単な取得例と秒の加算
次の例では現在のUNIX時刻を取得し、24時間後の時刻を計算して比較します。
表示のためにinttypes.h
のマクロを使い、time_t
を安全に整数として出力しています。
#include <stdio.h>
#include <time.h>
#include <inttypes.h>
// 説明: time_t の基本 - 現在のUNIX時刻を取得し、24時間後を計算して比較します。
// 注意: 出力は実行時の日時や環境に依存します。
int main(void) {
// 現在のUNIX時刻(エポック秒)を取得
time_t now = time(NULL);
if (now == (time_t)-1) {
// 取得に失敗した場合はエラーメッセージを表示して終了
fprintf(stderr, "time関数で現在時刻の取得に失敗しました\n");
return 1;
}
// 実装依存な性質を観察: サイズと符号
printf("sizeof(time_t) = %zu bytes\n", sizeof(time_t));
printf("time_t is %s\n", ((time_t)-1 < 0) ? "signed" : "unsigned");
// エポック秒としての数値を表示
printf("now (UNIX時刻) = %" PRIdMAX " 秒\n", (intmax_t)now);
// 24時間(86400秒)後の時刻を計算
const time_t one_day = (time_t)86400; // 単位は秒
time_t future = now + one_day;
printf("future = now + 86400 -> %" PRIdMAX " 秒\n", (intmax_t)future);
// 大小比較の例
if (future > now) {
puts("future は now より後の時刻です");
} else {
puts("future は now 以前の時刻です(オーバーフローの可能性)");
}
return 0;
}
sizeof(time_t) = 8 bytes
time_t is signed
now (UNIX時刻) = 1734349876 秒
future = now + 86400 -> 1734436276 秒
future は now より後の時刻です
上のとおり、time_tにはタイムゾーンは含まれず、単に「UTCにおける秒数」を保持しています。
表示や整形は別の変換が必要になります。
エポックは1970-01-01 00:00:00
UNIX時刻のエポックは1970-01-01 00:00:00 UTCです。
time_t
が0のときこの日時を表し、1ならその1秒後、-1なら1秒前(1969-12-31 23:59:59 UTC)を表します。
実装が符号付きであれば過去(1970年より前)も表現できますが、符号なし実装では負の値が使えないため過去を扱えない点に注意してください。
またPOSIXのUNIX時刻ではうるう秒を無視します。
そのため、UTCの厳密な秒数とは完全一致しない場合があります。
ローカル時刻ではなくUTC基準
time_t
はローカルタイムを直接表しません。
夏時間やタイムゾーンは、表示や構造体への変換時に適用されます。
例えば「日本時間の今」を文字列にしたいときは、localtime
等での変換が必要です。
反対に「UTCでの分解能が欲しい」場合はgmtime
等を用います(これらの詳細は別記事で解説します)。
time_tの特徴
実装依存の型
time_tは「算術型」ですが、その具体的な型や範囲は実装に依存します。
多くは整数型(long
やlong long
相当)ですが、規格上は浮動小数点であっても合法です。
実務では整数型として実装されるのが一般的で、1単位がほぼ常に「1秒」です。
動作中のプロセスから観察するには、上のサンプルのようにsizeof(time_t)
や、(time_t)-1 < 0
の判定が有用です。
サイズと範囲は環境で異なる
32ビット環境では4バイト、64ビット環境では8バイトのtime_t
が主流ですが、これは絶対ではありません。
とくに古いツールチェーンや組込み環境では差異が見られます。
代表的な例を挙げます。
あくまで「典型」であり、実機の値は必ず確認してください。
環境の例 | sizeof(time_t) | 典型的な符号 | 代表的な表現範囲(概算、UTC) |
---|---|---|---|
POSIX 32ビット(Linux i386などの旧環境) | 4 | 符号付き | 1901-12-13〜2038-01-19 03:14:07 |
POSIX 64ビット(Linux x86_64, AArch64, macOS) | 8 | 符号付き | 約±2.9e11年の広大な範囲 |
Windows(Visual C++ 2015以降の既定) | 8 | 符号付き | 約±2.9e11年の広大な範囲 |
旧Windowsや一部組込み | 4 | 実装依存 | 2038年問題に注意 |
32ビットの符号付きtime_t
では、2038-01-19 03:14:07 UTCでオーバーフローする「2038年問題」が発生します。
現在は多くの主要環境が64ビットtime_t
へ移行済みですが、ターゲット環境を必ず確認してください。
2038年問題の注意
32ビットのtime_t
が符号付きの場合、最大値は約2,147,483,647秒で頭打ちです。
上限付近での加算(例: 86400秒の加算)はオーバーフローを引き起こすため、比較や加算時は境界条件に注意が必要です。
符号付きかは環境依存
多くのデスクトップ・サーバ環境のtime_t
は符号付きですが、組込みなどで符号なしの可能性もあります。
符号なしの環境では、time_t
の減算が期待通りの負値にならず、アンダーフローして巨大な値に見えることがあります。
2つの時刻差を秒で安全に得たいときはdifftime
を用いる(別記事で解説)のが規格上の推奨です。
サイン判定の簡単な方法
上のサンプルのように((time_t)-1 < 0)
で判定できます。
これは移植性が高く、実行時に実装の性質を知るのに役立ちます。
単位は秒
time_tの単位は原則として「秒」です。
POSIXのUNIX時刻はうるう秒を挿入せず、連続した秒数としてカウントします。
そのため、UTCの正確な秒数とは稀に1秒のずれが生じ得ますが、一般的なアプリケーションでは問題になりません。
うるう秒取り扱いの補足
多くのOSは、うるう秒の瞬間を同じ秒を2回報告したり、スミアリングで均すなど、実装依存の挙動をとることがあります。
高精度タイムスタンプが必要な分野では、OSとライブラリの仕様確認が不可欠です。
time_tの基本的な使いどころと注意点
現在の時刻を保持する型
「今の時刻」をプログラム内で保持する最も標準的な型がtime_t
です。
time(NULL)
で現在のエポック秒を取得できます。
失敗時は(time_t)-1
が返るため、簡単でもよいのでエラーチェックを入れておくと安心です。
表示する際はinttypes.h
のPRIdMAX
と(intmax_t)
キャストが移植性の高い方法です。
大小比較と加減算で経過秒を扱う
秒単位の遅延や期限の計算は、time_t
に秒数を足し引きするだけで実現できます。
例えば1時間後はnow + 3600
、1日後はnow + 86400
です。
比較も通常の数値比較で構いません。
注意点として、リテラルの型に気を付けます。
大きな秒数を加えるときは(time_t)86400
のようにキャストしておくと意図が明確になり、型幅の違いによる警告やオーバーフローの可能性を下げられます。
また、2038年問題に近い領域では加算で溢れる可能性があるため、境界値のテストを行ってください。
タイムゾーンの表示は変換が必要
time_t
自体はタイムゾーンを持たないため、そのままでは「JSTで何年何月何日か」は分かりません。
ローカルタイムやUTCの年月日時分秒へ変換するには別の関数が必要です。
代表的には以下のような流れです。
- UTCの構造化時刻へ:
gmtime
でstruct tm
を得る - ローカルタイムへ:
localtime
でstruct tm
を得る
これらの詳細や落とし穴は別記事で扱います。
人が読める表示には文字列へ変換
画面に「YYYY-MM-DD HH:MM:SS」のように表示するにはtime_t
から文字列への変換が必要です。
簡易表示にはctime
、自由な書式にはstrftime
が使われます。
用途に応じて適切な変換関数を選択しましょう(こちらも別記事で詳述します)。
まとめ
time_tは、C言語で「UNIXエポックからの経過秒」を表す実装依存の算術型です。
エポックは1970-01-01 00:00:00 UTC、単位は秒で、タイムゾーン情報は含みません。
現在時刻の取得や期限計算は、time(NULL)
で取得したtime_t
に対して秒を加減し、大小比較するだけで簡単に扱えます。
一方で、型のサイズや符号、有効範囲は環境依存です。
特に32ビット環境では2038年問題に注意が必要です。
人間が読める形式やタイムゾーン表示には変換関数が必要で、文字列化や書式化は別のAPIで行います。
最初の一歩としては、現在のtime_t
を取得し、数秒や数日を加えて比較してみることが理解の近道です。
仕組みに慣れてきたら、ローカル時刻や書式化の関数へと段階的に進んでいきましょう。