閉じる

【C言語】struct tm構造体の使い方|現在時刻取得から表示まで

C言語で現在時刻を扱う場合、必ず登場するのがtime_t型とstruct tm構造体です。

この記事では、現在時刻の取得から、struct tmへの変換、そして任意フォーマットでの表示までを、図解とサンプルコードを交えながら丁寧に解説します。

標準Cライブラリのみで実装できるため、環境を問わず活用することができます。

C言語で現在時刻を取得する基本

time関数でtime_t型の現在時刻を取得する

まず、C言語で現在時刻の「元」となる値を取得するには、time関数を利用します。

この関数はUNIX時間(エポック秒)をtime_t型で返す関数です。

time関数の基本的な使い方

time関数はtime_t型の値を返します。

引数にNULLを渡すのが最もシンプルな使い方です。

C言語
#include <stdio.h>
#include <time.h>  // time_t, time

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

    // 現在時刻(エポック秒)を取得します
    now = time(NULL);  // 引数NULLで戻り値のみ利用

    // 取得できた秒数をそのまま表示します
    printf("time_t now = %ld\n", (long)now);

    return 0;
}
実行結果
実行例(出力の一例):
time_t now = 1733990000

この値は「1970年1月1日00:00:00(UTC)からの経過秒数」であり、まだ年や月の形には分解されていません

ここからstruct tmへの変換を行い、人間に読みやすい「年/月/日/時:分:秒」の形式で扱っていきます。

引数にポインタを渡すパターン

timeにはtime_t *を渡すこともできます。

戻り値と同時に、引数で渡した変数にも値を格納してくれます。

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

int main(void) {
    time_t now;

    // &nowを渡すことで、戻り値と同じ値がnowにも格納されます
    if (time(&now) == (time_t)-1) {  // 取得失敗チェック
        fprintf(stderr, "time関数の呼び出しに失敗しました\n");
        return 1;
    }

    printf("time_t now = %ld\n", (long)now);

    return 0;
}
実行結果
実行例(出力の一例):
time_t now = 1733990001

time_tとUNIX時間(エポック秒)の関係を理解する

time_tが表す値の意味を理解しておくと、struct tmとの変換や、時刻計算がイメージしやすくなります。

time_tとエポック秒

time_tは、エポック(epoch)と呼ばれる基準時刻からの経過時間(秒)を表す整数型です。

標準C仕様ではtime_tの正確な型(符号付きかどうか、ビット幅)は規定していませんが、多くのUNIX系システムでは符号付きの64ビット整数で実装されていることが多いです。

エポックは通常、次のように定義されています。

  • 基準時刻: 1970年1月1日00時00分00秒(UTC)
  • time_tの値: そこからの経過秒数

例えば、time_t0ならちょうどエポック時刻を、60なら1分後を意味します。

time_tを直接使う場合と限界

time_tは数値なので、差を取ることで経過時間(秒)を求めることができます。

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

int main(void) {
    time_t start, end;

    start = time(NULL);
    // ここで何らかの処理を行う想定で、簡易的にsleepで時間を進めます
    printf("3秒待機します...\n");
    // 環境によってはsleepのヘッダが異なるため、説明用サンプルです
    // Windowsなら Sleep(3000); (windows.h)
    // POSIXなら sleep(3); (unistd.h)

    // 実際の経過秒数を計算するために、もう一度取得します
    end = time(NULL);

    printf("経過秒数 = %ld秒\n", (long)(end - start));

    return 0;
}
実行結果
実行例(出力の一例):
3秒待機します...
経過秒数 = 3秒

しかし「現在は何年何月何日か」といった情報はtime_tだけでは分かりません。

そのため、struct tmという分解された時刻表現に変換して扱う必要があります。

struct tm構造体の基礎とメンバ

struct tm構造体とは(時刻の分解表現)

struct tmは、年月日・時分秒などに分解された日時情報を格納するための構造体です。

標準ヘッダtime.hで定義されています。

struct tmの定義イメージ

実際の定義は処理系により細かい差がありますが、標準的なstruct tmは次のような構造になっています。

C言語
struct tm {
    int tm_sec;   // 秒 (0〜60: うるう秒を考慮)
    int tm_min;   // 分 (0〜59)
    int tm_hour;  // 時 (0〜23)
    int tm_mday;  // 日 (1〜31)
    int tm_mon;   // 月 (0〜11) → 0が1月、11が12月
    int tm_year;  // 年 (1900年からの経過年数) → 2024年なら124
    int tm_wday;  // 曜日 (0〜6) → 0が日曜、6が土曜
    int tm_yday;  // 年内通算日 (0〜365) → 0が1月1日
    int tm_isdst; // サマータイム適用なら正、そうでなければ0、情報なしは負
};

年や月の値が人間の感覚とは少しずれている点が、実務でよくつまずくポイントです。

struct tmの主なメンバ一覧と意味

主なメンバを表にまとめておきます。

メンバ名値の範囲・意味例(2024年12月31日 23:59:59 の場合)
tm_year1900年からの経過年数2024年 → 124
tm_mon月(0〜11、0が1月)12月 → 11
tm_mday日(1〜31)31日 → 31
tm_hour時(0〜23)23時 → 23
tm_min分(0〜59)59分 → 59
tm_sec秒(0〜60、うるう秒考慮のため60まで)59秒 → 59
tm_wday曜日(0〜6、0が日曜)火曜なら2、金曜なら5など
tm_yday年内通算日(0〜365、0が1月1日)12月31日(平年) → 364
tm_isdstサマータイム適用フラグ(>0:適用、0:非適用、<0:情報なし)日本では多くの場合0

例えば2024-01-01 00:00:00struct tmは次のようになります。

  • tm_year = 124 (1900 + 124 = 2024)
  • tm_mon = 0 (1月)
  • tm_mday = 1 (1日)
  • tm_hour = 0
  • tm_min = 0
  • tm_sec = 0
  • tm_wday = 1 (月曜)
  • tm_yday = 0 (年内通算0日目)
  • tm_isdst = 0 (日本などサマータイムなしの地域)

struct tmを使うメリットと注意点

struct tmを使うメリット

struct tmを使うことで、次のような利点があります。

1つ目は、人間に分かりやすい「年・月・日・時・分・秒」で扱えることです。

画面表示やログ出力、ユーザ入力との対応が容易になります。

2つ目は、カレンダー計算やフォーマット出力との相性が良いことです。

標準ライブラリにはstrftimeのようにstruct tmを直接受け取り、好みのフォーマットに変換してくれる関数が用意されています。

struct tmを使う際の注意点

一方で、いくつか注意しておくべき罠があります。

  • 年と月のオフセットに注意
    年は「1900年からのオフセット」、月は「0始まり(0が1月)」という仕様になっています。printfで人間向けに表示する際は、年に1900を足し、月に1を足す必要があります。
  • スレッドセーフでない関数の戻り値ポインタに注意
    後述するlocaltimegmtimeは、内部に保持している静的なstruct tm領域へのポインタを返すため、スレッド環境では書き換え競合が起こる可能性があります。スレッドセーフな代替関数(localtime_rなど)が提供されている環境では、そちらの使用も検討すべきです。
  • フィールドを手動で変更したあとにはmktimeによる正規化が必要
    例えばtm_mdayに31を代入しても、すべての月に31日があるわけではありません。struct tmを編集してからmktimeを呼び出すことで、カレンダー上有効な日付に正規化させることが一般的です。

struct tmへの変換と時刻フォーマット表示

localtime関数でtime_tからstruct tmへ変換する

time_t型のエポック秒を、ローカル時刻(タイムゾーンを考慮した時刻)のstruct tmに変換するには、localtime関数を使用します。

localtimeの基本使用例

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

int main(void) {
    time_t now;
    struct tm *lt;  // local time

    // 現在時刻を取得
    now = time(NULL);

    // time_tからローカル時刻のstruct tmへ変換
    lt = localtime(&now);
    if (lt == NULL) {
        fprintf(stderr, "localtimeに失敗しました\n");
        return 1;
    }

    // 年月日・時刻を表示
    // 年: 1900を加算、月: 1を加算する点に注意
    printf("ローカル時刻: %04d-%02d-%02d %02d:%02d:%02d\n",
           lt->tm_year + 1900,
           lt->tm_mon + 1,
           lt->tm_mday,
           lt->tm_hour,
           lt->tm_min,
           lt->tm_sec);

    return 0;
}
実行結果
実行例(出力の一例):
ローカル時刻: 2024-12-12 15:30:45

localtime使用時の注意(静的領域)

localtime内部で静的なstruct tmオブジェクトを保持し、そのアドレスを返す仕様です。

そのため、以下の点に注意が必要です。

  • 別のlocaltimegmtimeの呼び出しで上書きされる
    取得したポインタを長期的に保持したい場合は、自分でstruct tm変数を用意して*ltをコピーしておくと安全です。
  • スレッド環境ではスレッドセーフでないため、localtime_rlocaltime_sといった再入可能版の利用を検討します(環境依存)。

gmtime関数でUTCのstruct tmを取得する

gmtime関数は、ローカルタイムではなくUTC(協定世界時)のstruct tmを返す関数です。

使い方はlocaltimeとほとんど同じです。

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

int main(void) {
    time_t now;
    struct tm *utc;

    now = time(NULL);

    // UTCとしての構造体を取得
    utc = gmtime(&now);
    if (utc == NULL) {
        fprintf(stderr, "gmtimeに失敗しました\n");
        return 1;
    }

    printf("UTC時刻: %04d-%02d-%02d %02d:%02d:%02d\n",
           utc->tm_year + 1900,
           utc->tm_mon + 1,
           utc->tm_mday,
           utc->tm_hour,
           utc->tm_min,
           utc->tm_sec);

    return 0;
}
実行結果
実行例(出力の一例):
UTC時刻: 2024-12-12 06:30:45

ローカル時刻と比較することで、自分の環境がどのタイムゾーンで動いているかを確認することもできます。

strftime関数でstruct tmを任意の形式に文字列出力する

柔軟な日付時刻フォーマットが必要な場合は、strftime関数を使うと便利です。

struct tmからchar配列へ、指定フォーマットの文字列を出力してくれます。

代表的なフォーマット指定子

指定子意味
%Y4桁の年2024
%y2桁の年24
%m月(01〜12)12
%d日(01〜31)09
%H時(00〜23)15
%M分(00〜59)07
%S秒(00〜60)05
%a曜日の省略名Sun, Monなど
%A曜日の完全名Sundayなど
%b月名の省略名Jan, Febなど
%cロケール依存の日時形式例: Wed Dec…
%F%Y-%m-%d と同等2024-12-12
%T%H:%M:%S と同等15:30:45

strftimeの使用例

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

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

    if (lt == NULL) {
        fprintf(stderr, "localtimeに失敗しました\n");
        return 1;
    }

    char buf[128];  // フォーマット結果を格納するバッファ

    // "YYYY-MM-DD HH:MM:SS" 形式で出力
    if (strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt) == 0) {
        // 0が返る場合は、バッファが不足した可能性があります
        fprintf(stderr, "strftimeの出力がバッファに収まりませんでした\n");
        return 1;
    }

    printf("フォーマット済み現在時刻: %s\n", buf);

    return 0;
}
実行結果
実行例(出力の一例):
フォーマット済み現在時刻: 2024-12-12 15:30:45

フォーマット文字列を変えるだけで、様々な形式の日時文字列を簡単に出力できます

printfとstruct tmで手動フォーマット表示を行う

strftimeを使わずに、printfstruct tmの各メンバを自分で組み立てることもできます。

細かい制御が必要な場合には有効ですが、その分年や月のオフセットを忘れないよう注意が必要です。

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

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

    if (lt == NULL) {
        fprintf(stderr, "localtimeに失敗しました\n");
        return 1;
    }

    int year  = lt->tm_year + 1900; // 1900年を足す
    int month = lt->tm_mon + 1;     // 1月が0なので1を足す
    int day   = lt->tm_mday;
    int hour  = lt->tm_hour;
    int min   = lt->tm_min;
    int sec   = lt->tm_sec;

    printf("手動フォーマット: %04d/%02d/%02d %02d:%02d:%02d\n",
           year, month, day, hour, min, sec);

    return 0;
}
実行結果
実行例(出力の一例):
手動フォーマット: 2024/12/12 15:30:45

自前フォーマットの利点は、ロケールに依存しない完全な制御ができる点です。

一方で、ゼロ埋めや桁数を自分で指定する必要があるため、フォーマット文字列が煩雑になりやすいことがデメリットです。

現在時刻の取得から表示までの実装例

ここまでの内容を組み合わせて、「現在時刻の取得」から「構造体への変換」「フォーマット表示」までを一通り行う具体例を示します。

現在時刻を取得してstruct tmに変換するサンプルコード

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

/*
 * 現在のローカル時刻を取得し、
 * struct tm へ変換したうえで、
 * 複数の形式で表示するサンプルです。
 */
int main(void) {
    // 1. 現在時刻をtime_tとして取得
    time_t now = time(NULL);
    if (now == (time_t)-1) {
        fprintf(stderr, "time関数で現在時刻の取得に失敗しました\n");
        return 1;
    }

    // 2. time_tからローカル時刻のstruct tmへ変換
    struct tm *lt = localtime(&now);
    if (lt == NULL) {
        fprintf(stderr, "localtime関数での変換に失敗しました\n");
        return 1;
    }

    // 3. struct tmを用いて手動フォーマットで表示
    int year  = lt->tm_year + 1900;
    int month = lt->tm_mon + 1;
    int day   = lt->tm_mday;
    int hour  = lt->tm_hour;
    int min   = lt->tm_min;
    int sec   = lt->tm_sec;

    printf("手動フォーマット表示: %04d-%02d-%02d %02d:%02d:%02d\n",
           year, month, day, hour, min, sec);

    // 4. strftimeを使って別形式で表示
    char buf[128];

    // "YYYY/MM/DD (曜日) HH:MM:SS" 形式
    if (strftime(buf, sizeof(buf), "%Y/%m/%d (%a) %H:%M:%S", lt) == 0) {
        fprintf(stderr, "strftimeの出力がバッファサイズを超えました\n");
        return 1;
    }

    printf("strftime形式表示:   %s\n", buf);

    return 0;
}
実行結果
実行例(出力の一例):
手動フォーマット表示: 2024-12-12 15:30:45
strftime形式表示:   2024/12/12 (Thu) 15:30:45

この例では、time → localtime → struct tm → printf / strftimeという一連の流れを確認できます。

struct tmを使った日付時刻表示の具体例

struct tmを使うと、単に現在時刻を表示するだけでなく、別の形式やタイムゾーン、UTCとの比較など、さまざまな応用が可能です。

ローカル時刻とUTCを同時に表示する

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

int main(void) {
    time_t now = time(NULL);
    if (now == (time_t)-1) {
        fprintf(stderr, "timeの呼び出しに失敗しました\n");
        return 1;
    }

    struct tm *lt = localtime(&now); // ローカル時刻
    struct tm *ut = gmtime(&now);    // UTC時刻

    if (lt == NULL || ut == NULL) {
        fprintf(stderr, "localtimeまたはgmtimeの呼び出しに失敗しました\n");
        return 1;
    }

    char buf_local[64];
    char buf_utc[64];

    strftime(buf_local, sizeof(buf_local), "%Y-%m-%d %H:%M:%S", lt);
    strftime(buf_utc, sizeof(buf_utc),   "%Y-%m-%d %H:%M:%S", ut);

    printf("Local: %s\n", buf_local);
    printf("UTC  : %s\n", buf_utc);

    return 0;
}
実行結果
実行例(出力の一例):
Local: 2024-12-12 15:30:45
UTC  : 2024-12-12 06:30:45

このように、1つのtime_tからローカル時刻とUTC時刻の両方を得ることができ、ログや通信プロトコルなどで役立ちます。

ログ向けのタイムスタンプ文字列を作る

ログファイルでは、[YYYY-MM-DD HH:MM:SS]のようなタイムスタンプを付与することが多いです。

strftimeを使えば簡潔に実装できます。

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

void log_with_time(const char *message) {
    time_t now = time(NULL);
    struct tm *lt = localtime(&now);

    if (lt == NULL) {
        // 時刻取得に失敗した場合でもメッセージは出す
        printf("[unknown time] %s\n", message);
        return;
    }

    char ts[32];
    strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S", lt);

    printf("[%s] %s\n", ts, message);
}

int main(void) {
    log_with_time("プログラムを開始します");
    // ここで何らかの処理を行うと仮定
    log_with_time("処理が完了しました");

    return 0;
}
実行結果
実行例(出力の一例):
[2024-12-12 15:30:45] プログラムを開始します
[2024-12-12 15:30:45] 処理が完了しました

どの時点で何が起きたかが一目で分かるため、デバッグや運用時に非常に有用です。

C言語における時刻処理のポイントとまとめ

ここで、C言語におけるtime_tstruct tmを使った時刻処理のポイントを整理しておきます。

1つ目は、time_tは「エポックからの経過秒数」であり、人が読むにはstruct tmへの変換が必須であることです。

time関数でtime_tを取得し、localtimegmtimestruct tmに変換します。

2つ目は、struct tmの年と月がオフセット表現である点です。

tm_yearには1900を、tm_monには1を足して表示しなければなりません。

3つ目は、strftimeを活用すると、ログや画面表示に適した日付時刻の文字列を柔軟に生成できることです。

ロケール依存のフォーマット%cや、ISO8601風の%Fなどを組み合わせることで、用途に合った表現を簡単に得られます。

まとめ

本記事では、C言語における現在時刻取得からstruct tmを用いた表示までの一連の流れを解説しました。

timetime_tを取得し、localtimegmtimestruct tmへ変換し、最後にprintfstrftimeで文字列に整形するのが基本パターンです。

年と月のオフセットに注意しつつ、フォーマット文字列を工夫することで、実用的なログ出力や時刻表示を簡潔に実装できます。

実際の開発では、この記事のサンプルをベースに、自身のアプリケーションに合った形式へ発展させてみてください。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!