C言語を用いたソフトウェア開発において、現在時刻を取得し、ログの出力やファイル管理に活用する場面は多々あります。

標準ライブラリであるtime.hを活用することで、プラットフォームに依存しない汎用的な時刻取得処理を実装することが可能です。

本記事では、初心者の方でも理解しやすいように、time関数やlocaltime関数を用いた基本的な実装から、書式設定の方法まで詳しく解説します。

C言語における時刻管理の基本概念

C言語で時刻を扱う際、まず理解しておくべきなのがエポックタイム(Unix時間)という概念です。

これは、1970年1月1日0時0分0秒からの経過秒数を数値で表したものです。

多くのシステムでは、この経過秒数をtime_tという型で管理しています。

しかし、単なる秒数だけでは人間が直感的に日付を理解することが難しいため、構造体に変換して利用するのが一般的です。

C言語には、この変換を担うstruct tmという構造体が定義されています。

この構造体を利用することで、年、月、日、時、分、秒といった個別の要素にアクセスできるようになります。

time関数による現在時刻(秒数)の取得

現在時刻を取得する最も基本的な関数がtime関数です。

この関数を使用するには、time.hヘッダーファイルをインクルードする必要があります。

関数の引数にNULLを渡すことで、現在のエポックタイムを取得できます。

以下のサンプルコードで、その動作を確認してみましょう。

C言語
#include <stdio.h>
#include <time.h>

int main(void) {
    // time_t型の変数を用意
    time_t now;

    // 現在時刻を取得して変数に格納
    now = time(NULL);

    // 取得した秒数を表示
    printf("現在のエポックタイム: %ld\n", (long)now);

    return 0;
}
実行結果
現在のエポックタイム: 1716678912

このように、数値としての時刻を取得することができました。

しかし、これだけでは「何年何月何日」であるか判別しにくいため、次に紹介する関数で変換を行います。

localtime関数で人間が読める形式に変換する

time関数で取得したtime_t型の値を、現地のタイムゾーンに合わせた構造体に変換するのがlocaltime関数です。

この関数は、引数としてtime_t型のポインタを受け取り、struct tm型のポインタを返します。

localtime関数が返すポインタは、静的な領域を指しているため、値を保持し続けたい場合は別の変数にコピーする必要があります。

ここで、変換後の構造体であるstruct tmの主要なメンバを確認しておきましょう。

メンバ名内容値の範囲
tm_year1900年からの経過年数整数
tm_mon1月からの経過月数0〜11
tm_mday1ヶ月の中の日数1〜31
tm_hour0時からの経過時間0〜23
tm_min0分からの経過分0〜59
tm_sec0秒からの経過秒0〜60(閏秒考慮)

ここで注意が必要なのは、「年」は1900を足し、「月」は1を足す必要があるという点です。

この仕様を忘れると、意図しない日付が表示されてしまうため注意してください。

具体的な実装例

実際に構造体を利用して、現在の日時を整形して表示するプログラムを記述します。

C言語
#include <stdio.h>
#include <time.h>

int main(void) {
    time_t t = time(NULL);
    struct tm *now = localtime(&t);

    // 各要素を調整して表示
    printf("現在は %d年%d月%d日 %02d時%02d分%02d秒 です。\n",
           now->tm_year + 1900, // 1900を足す
           now->tm_mon + 1,      // 1を足す
           now->tm_mday,
           now->tm_hour,
           now->tm_min,
           now->tm_sec);

    return 0;
}
実行結果
現在は 2026年5月26日 08時15分30秒 です。

strftime関数による高度なフォーマット出力

先ほどの例ではprintfを使用して手動でフォーマットしましたが、より柔軟に文字列へ変換したい場合はstrftime関数が便利です。

この関数を使用すると、日付や時刻を特定の書式(例えば「YYYY/MM/DD」など)に簡単に変換できます。

バッファサイズを指定できるため、安全に文字列処理を行えるのがメリットです。

C言語
#include <stdio.h>
#include <time.h>

int main(void) {
    time_t t = time(NULL);
    struct tm *now = localtime(&t);
    char buf[64];

    // 書式を指定して文字列に変換
    strftime(buf, sizeof(buf), "%Y/%m/%d %H:%M:%S", now);

    printf("整形後の時刻: %s\n", buf);

    return 0;
}

strftimeでよく使われる書式指定子をいくつか紹介します。

  • %Y:4桁の西暦
  • %m:月(01-12)
  • %d:日(01-31)
  • %H:時(00-23)
  • %M:分(00-59)
  • %S:秒(00-59)
  • %A:曜日の名称(Sundayなど)

高精度な時刻取得:timespec_get関数

秒単位ではなく、ミリ秒やナノ秒単位の精度が求められるシステムでは、C11規格で導入されたtimespec_get関数を使用します。

この関数はstruct timespecという構造体を使用し、秒単位とナノ秒単位のデータを保持します。

特に処理時間の計測を行うベンチマークなどの実装に役立ちます。

C言語
#include <stdio.h>
#include <time.h>

int main(void) {
    struct timespec ts;

    // 高精度時刻の取得
    if (timespec_get(&ts, TIME_UTC) != 0) {
        printf("秒: %ld\n", ts.tv_sec);
        printf("ナノ秒: %ld\n", ts.tv_nsec);
    }

    return 0;
}

実行環境がC11以降をサポートしている場合、より精密な制御が可能になります。

ただし、古いコンパイラや組み込み環境ではサポートされていない場合があるため、環境確認が重要です。

スレッドセーフな時刻取得について

マルチスレッド環境でlocaltime関数を使用する場合、注意が必要です。

標準のlocaltimeは、内部的に共有の静的メモリを使用するため、複数のスレッドから同時に呼び出すとデータが書き換わる恐れがあります。

これを防ぐために、Windows環境ではlocaltime_s、POSIX準拠(Linux/UNIX等)の環境ではlocaltime_rを使用することが推奨されます。

安全なプログラムを作成するためには、動作環境に応じたスレッドセーフな関数の選択が不可欠です。

C言語
#include <stdio.h>
#include <time.h>

int main(void) {
    time_t t = time(NULL);
    struct tm result;

    // POSIX環境におけるスレッドセーフな呼び出し例
    // localtime_r(&t, &result); 

    // Windows環境での呼び出し例
    // localtime_s(&result, &t);

    return 0;
}

移植性を考慮する場合は、条件付きコンパイルを用いて環境ごとに適切な関数を使い分ける工夫が求められます。

まとめ

C言語で現在時刻を取得する方法について、基本的なtime関数から、書式設定に便利なstrftime関数、そして高精度な取得方法まで解説しました。

まずはtime_t型でエポックタイムを取得し、struct tmに変換するという一連の流れをマスターしましょう。

その際、「年」や「月」の数値調整を忘れないことが正確なプログラムを書くためのポイントです。

また、用途に応じてstrftimeによる柔軟なフォーマットや、timespec_getによる高精度な取得を使い分けてみてください。

マルチスレッド環境での開発では、スレッドセーフな関数の利用も常に意識するようにしましょう。

これらの知識を身に付けることで、実用的で堅牢な時刻処理をC言語で実装できるようになります。