閉じる

【C言語】ctimeとは?time_tを「〇年〇月〇日〇時〇分」に変換する基本を解説

C言語で現在時刻を扱うとき、time_t型やctime関数は必ずといってよいほど登場します。

ただ、初心者の方にとっては「数値の時刻」と「人間が読める日付文字列」のつながりがイメージしづらいところです。

本記事では、ctimeでtime_tを読みやすい文字列に変換する基本から、「〇年〇月〇日〇時〇分」形式に整える実践的な方法まで、順を追って丁寧に解説します。

ctimeとは何か

ctimeの基本仕様と役割

C言語のctime関数は、time_t型で表された時刻」を「人間が読める1行の文字列」に変換する標準ライブラリ関数です。

time_tは秒数による機械的な表現ですが、そのままでは人が読みづらいため、ctimeで例えば次のような文字列に変換します。

変換例 Wed Nov 22 13:45:10 2025\n\0

このように、曜日・月名・日・時刻・年が1行の英語表記で並んだ文字列が得られます。

ctimeは「とりあえず時刻を表示したい」ときに最も手軽に使える関数ですが、出力形式の自由度は高くありません。

後半で、より柔軟な方法との違いも説明します。

time_tとカレンダー時刻(年月日・時刻)の関係

C言語では、内部的な時刻表現人間向けのカレンダー表現を分けて考えます。

  • 内部表現: time_t
    多くの環境では、ある基準時刻からの経過秒数を表す整数型です。基準時刻は一般的に1970-01-01 00:00:00(UTC)です。
  • カレンダー表現: 年・月・日・時・分・秒など
    struct tm構造体で表現されます。

概念的な関係は次のようになります。

time_t(秒数) → (変換) → struct tm(年月日・時刻の各フィールド) → (整形) → 「2025年11月22日13時45分」などの文字列

ctimeは、この「time_t → 文字列」の変換を一度に行う関数です。

ただし出力形式は固定で、英語表記の日付になります。

C標準ライブラリでのctimeの宣言と使い方

ctime関数はtime.hヘッダで宣言されています。

標準的な宣言は次の通りです。

C言語
#include <time.h>

char *ctime(const time_t *timer);

主なポイントは次の通りです。

  • 引数はtime_t型へのポインタconst time_t *timerです。
  • 戻り値は「静的な内部バッファ」へのポインタであり、プログラマがfreeで解放してはいけません
  • 失敗時にはNULLを返します。

典型的な使い方は、次のようにtimeで現在時刻をtime_tとして取得し、ctimeで文字列に変換して標準出力に表示する形です。

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

int main(void) {
    time_t now;             // 現在時刻を格納する変数
    char *str_time;         // 文字列へのポインタ

    time(&now);             // 現在時刻を取得
    str_time = ctime(&now); // time_t → 文字列へ変換

    if (str_time == NULL) {
        // 変換に失敗した場合のエラーメッセージ
        printf("ctime failed.\n");
        return 1;
    }

    // 変換結果を表示
    printf("%s", str_time);

    return 0;
}

このプログラムは環境によって異なりますが、例えば次のような出力になります。

実行結果
Sat Nov 22 13:45:10 2025

ここまでがctimeの基本的な役割と位置づけです。

ctimeを使ったtime_tからの文字列変換

time関数で現在時刻(time_t)を取得する手順

現在時刻を取得するには、time関数を使います。

宣言はtime.hにあります。

C言語
#include <time.h>

time_t time(time_t *timer);

使い方は2通りあります。

  1. time_t変数を用意し、そのアドレスを渡す方法
  2. NULLを渡し、戻り値のtime_tを受け取る方法

初心者の方には、「変数を用意してアドレスを渡す」方法の方が、ポインタと戻り値の関係を理解しやすいと思います。

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

int main(void) {
    time_t now;

    // 現在時刻をtime_tとして取得する
    if (time(&now) == (time_t)-1) {
        // 取得に失敗した場合
        printf("time failed.\n");
        return 1;
    }

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

    return 0;
}
実行結果
time_t値: 1769118310

この数値は、基準時刻からの秒数を表しており、人間にとってはそのままでは分かりづらいので、次にctimeで読みやすくします。

ctimeでtime_tを「文字列」に変換する基本コード例

timeで取得したtime_tを、そのままctimeに渡す基本的なコード例を示します。

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

int main(void) {
    time_t now;        // 現在時刻(内部表現)
    char *str_time;    // 文字列表現へのポインタ

    // 現在時刻を取得
    if (time(&now) == (time_t)-1) {
        printf("time failed.\n");
        return 1;
    }

    // time_t → 文字列へ変換
    str_time = ctime(&now);

    // ctimeは失敗するとNULLを返す可能性がある
    if (str_time == NULL) {
        printf("ctime failed.\n");
        return 1;
    }

    // 結果を表示
    printf("現在時刻(文字列): %s", str_time);

    // str_timeは内部バッファを指しているので、freeしてはいけない
    // free(str_time);  // ← これは絶対にNG

    return 0;
}
実行結果
現在時刻(文字列): Sat Nov 22 13:45:10 2025

ここまでで「time → ctime → printf」の基本パターンが理解できれば、時刻表示の第一歩はクリアです。

ctimeが返す文字列の形式

ctimeが返す文字列の形式は、C標準規格でほぼ決まっています。

典型的には次のようになります。

Www Mmm dd hh:mm:ss yyyy\n\0

各部分の意味は次の通りです。

部分意味
Www曜日の英語略称Sun, Mon, Tue, Wed, Thu, Fri, Sat
Mmm月の英語略称Jan, Feb, Mar, …, Dec
dd日(2桁)01〜31
hh時(2桁・24時間制)00〜23
mm分(2桁)00〜59
ss秒(2桁)00〜60(うるう秒)
yyyy西暦年(4桁)2025など
\n改行
\0文字列終端

具体的には、次のような文字列になります。

  • Sat Nov 22 13:45:10 2025\n\0

この形式は固定で、日本語の「年」「月」「日」などは入りません

そのため、日本語形式にしたい場合は、後述のstrftimeなどを使う必要があります。

文字列末尾の改行文字(\n)に注意するポイント

ctimeの戻り値の文字列には末尾に\n(改行)が含まれています

つまり、実際の配列内容はおおよそ次のようになっています。

"Sat Nov 22 13:45:10 2025\n\0"

そのため、printfで次のように書くと、改行が2回入ってしまいます。

C言語
printf("%s\n", str_time);  // str_timeの中にすでに\nがある

プログラム例でprintf("%s", str_time);と書いていたのは、このためです。

よくある混乱として「なんとなく空行が1行多い」などの現象があります。

原因がctimeの末尾の改行であることを覚えておくと、デバッグしやすくなります。

改行を削除して使いたい場合は、末尾の\n\0で置き換える処理を自分で行います。

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

int main(void) {
    time_t now;
    char *str_time;

    time(&now);
    str_time = ctime(&now);

    if (str_time == NULL) {
        printf("ctime failed.\n");
        return 1;
    }

    // 末尾の'\n'を探して'\0'に置き換える
    char *p = strchr(str_time, '\n');  // 改行文字の位置を探す
    if (p != NULL) {
        *p = '\0';                     // 終端文字に置き換える
    }

    printf("改行なし: %s\n", str_time);

    return 0;
}
実行結果
改行なし: Sat Nov 22 13:45:10 2025

このように改行を消せば、他の文字列と連結しやすくなります

「〇年〇月〇日〇時〇分」形式に整形する方法

struct tmへの変換(localtime)とctimeとの違い

ctime「time_t → 1つの固定形式の文字列」に変換してくれますが、出力形式の自由度がありません。

一方で、struct tmに変換すると「年・月・日・時・分・秒などを個別のメンバとして扱える」ようになります。

struct tmへの変換にはlocaltimeなどの関数を使います。

C言語
#include <time.h>

struct tm *localtime(const time_t *timer);

localtimeの主な役割は、time_tを「ローカル時刻のカレンダー表現」に変換することです。

戻り値は静的なstruct tmへのポインタです。

struct tmの主要なメンバは次の通りです。

フィールド意味値の範囲
tm_year西暦年から1900を引いた値例えば2025年なら125
tm_mon月(0〜11)0が1月、11が12月
tm_mday日(1〜31)
tm_hour時(0〜23)
tm_min分(0〜59)
tm_sec秒(0〜60)

たとえば、「2025年11月22日13時45分10秒」の場合、struct tmは概ね次のような値を持ちます。

  • tm_year = 2025 - 1900 = 125
  • tm_mon = 10(0始まりなので11月は10)
  • tm_mday = 22
  • tm_hour = 13
  • tm_min = 45
  • tm_sec = 10

ctimeは内部で「time_t → struct tm → 文字列」という流れをまとめて行っているイメージを持つと分かりやすいです。

struct tmから年月日・時刻を取り出す方法

localtimeで取得したstruct tmから、各フィールドを取り出して、自分でprintfなどで整形することもできます。

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

int main(void) {
    time_t now;
    struct tm *lt;  // local timeへのポインタ

    time(&now);

    // time_t → struct tm(ローカル時間)に変換
    lt = localtime(&now);

    if (lt == NULL) {
        printf("localtime failed.\n");
        return 1;
    }

    // tm_yearは1900年からの年数、tm_monは0〜11(月)
    int year  = lt->tm_year + 1900; // 西暦年に変換
    int month = lt->tm_mon + 1;     // 1〜12に変換
    int day   = lt->tm_mday;
    int hour  = lt->tm_hour;
    int min   = lt->tm_min;
    int sec   = lt->tm_sec;

    // 日本語で年月日時分秒を表示
    printf("%d年%d月%d日 %d時%d分%d秒\n",
           year, month, day, hour, min, sec);

    return 0;
}
実行結果
2025年11月22日 13時45分10秒

このようにstruct tmを使えば、日本語を含む任意の形式で表示が可能になります。

ただし、より複雑な書式(ゼロ埋め、曜日名、24時間・12時間など)をきれいに書くためには、次に説明するstrftimeを使う方が便利です。

strftimeで「〇年〇月〇日〇時〇分」にフォーマットする

strftime関数は、struct tmから指定した書式に従った文字列を生成する関数です。

宣言は次の通りです。

C言語
#include <time.h>

size_t strftime(char *s, size_t maxsize,
                const char *format,
                const struct tm *timeptr);

主な引数の意味は次のようになります。

  • s: 出力先の文字配列
  • maxsize: 配列sのサイズ(バイト数)
  • format: 書式文字列(例:"%Y年%m月%d日 %H時%M分")
  • timeptr: 変換元のstruct tmへのポインタ

「〇年〇月〇日〇時〇分」形式に整形する具体例を示します。

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

int main(void) {
    time_t now;
    struct tm *lt;
    char buf[64]; // 出力バッファ(十分なサイズを確保)

    time(&now);

    // time_t → struct tmへ変換(ローカル時刻)
    lt = localtime(&now);
    if (lt == NULL) {
        printf("localtime failed.\n");
        return 1;
    }

    // 「YYYY年MM月DD日 HH時MM分」形式で整形
    // %Y: 4桁の西暦年、%m: 2桁の月、%d: 2桁の日
    // %H: 2桁の時(24時間制)、%M: 2桁の分
    if (strftime(buf, sizeof(buf),
                 "%Y年%m月%d日 %H時%M分",
                 lt) == 0) {
        // バッファサイズ不足などで0が返る
        printf("strftime failed.\n");
        return 1;
    }

    printf("整形結果: %s\n", buf);

    return 0;
}
実行結果
整形結果: 2025年11月22日 13時45分

このようにstrftimeを使うと、「〇年〇月〇日〇時〇分」といった日本語形式が簡単に作れます

よく使う書式指定子の例を簡単にまとめておきます。

指定子意味
%Y西暦年(4桁)2025
%m月(2桁)01〜12
%d日(2桁)01〜31
%H時(24時間制・2桁)00〜23
%M分(2桁)00〜59
%S秒(2桁)00〜60

和暦・24時間表示など出力形式をカスタマイズするコツ

strftimeを応用すると、出力形式を自由にカスタマイズできます。

ここでは、いくつか代表的なカスタマイズ方法を紹介します。

24時間表示と12時間表示

strftimeでは、次のように24時間制と12時間制を使い分けられます。

  • 24時間制: %H(00〜23)
  • 12時間制: %I(01〜12)と%p(AM/PM)

たとえば「13時45分」を「午後01時45分」のように表示したい場合、地域設定(ロケール)によりますが、次のような書式が考えられます。

C言語
strftime(buf, sizeof(buf), "%p %I時%M分", lt);

ただし%cst-code>%pで日本語の「午前」「午後」が出るかどうかはロケール設定に依存します

環境によっては「AM」「PM」になることもあります。

曜日を含めた表示

strftimeには曜日用の指定子もあります。

  • %a: 曜日の省略名(例: Sun, Mon)
  • %A: 曜日の完全名(例: Sunday)

日本語の「(土)」のような形式にしたい場合は、自分で配列を用意するのが確実です。

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

int main(void) {
    time_t now;
    struct tm *lt;
    char buf[64];
    const char *week_jp[] = {"日", "月", "火", "水", "木", "金", "土"};

    time(&now);
    lt = localtime(&now);

    if (lt == NULL) {
        printf("localtime failed.\n");
        return 1;
    }

    // 基本部分をstrftimeで作る
    if (strftime(buf, sizeof(buf),
                 "%Y年%m月%d日 %H時%M分",
                 lt) == 0) {
        printf("strftime failed.\n");
        return 1;
    }

    // tm_wdayは0(日)〜6(土)
    int wday = lt->tm_wday;

    // 曜日を付けて表示(例: 2025年11月22日 13時45分(土))
    printf("%s(%s)\n", buf, week_jp[wday]);

    return 0;
}
実行結果
2025年11月22日 13時45分(土)

曜日のような「ロケール依存の要素」は、自分でテーブルを持つとコントロールしやすいです。

和暦表示の考え方

標準のstrftimeには、汎用的な「和暦」サポートはありません。

和暦表示をしたい場合は、自分で西暦と元号の対応を計算する必要があります

簡略的な例として、令和のみ対応するコードイメージを示します(実務で使う場合は元号の境界などを厳密に扱う必要があります)。

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

int main(void) {
    time_t now;
    struct tm *lt;
    int year, month, day;
    int reiwa_year;  // 令和何年かを格納

    time(&now);
    lt = localtime(&now);

    if (lt == NULL) {
        printf("localtime failed.\n");
        return 1;
    }

    year  = lt->tm_year + 1900;
    month = lt->tm_mon + 1;
    day   = lt->tm_mday;

    // 非常に単純化した令和判定(2019年5月1日以降を令和とする)
    if (year > 2019 ||
       (year == 2019 && (month > 5 || (month == 5 && day >= 1)))) {
        reiwa_year = year - 2018; // 2019年が令和元年
        printf("令和%d年%d月%d日\n", reiwa_year, month, day);
    } else {
        // 令和以前は単純に西暦で表示(簡略例)
        printf("%d年%d月%d日(令和以前)\n", year, month, day);
    }

    return 0;
}
実行結果
令和7年11月22日

このように和暦は「西暦→元号」の自前ロジックが必要であることに注意してください。

ctime使用時の注意点とよくある落とし穴

返されるポインタと内部バッファの扱い

ctimeの戻り値は「ライブラリ内部で静的に確保されたバッファ」へのポインタです。

これには次のような重要な性質があります。

  1. プログラマがfreeしてはいけない
    バッファは自分でmallocしたものではないため、解放してはいけません。解放すると未定義動作となります。
  2. 次にctimeを呼び出すと同じバッファに上書きされる
    複数回ctimeを呼び出すと、以前の結果は新しい結果で上書きされます。

例として、2つのtime_tctimeで変換し、それぞれの文字列を保存したつもりが、実は同じ内容になってしまうケースを考えてみます。

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

int main(void) {
    time_t t1 = 0;          // 1970-01-01 00:00:00(UTC)付近
    time_t t2 = 24 * 60 * 60; // 1日後

    char *s1 = ctime(&t1);
    char *s2 = ctime(&t2);

    // s1とs2は同じ内部バッファを指している可能性が高い
    printf("s1: %s", s1);
    printf("s2: %s", s2);

    return 0;
}

出力例(環境によりますが):

実行結果
s1: Fri Jan  2 00:00:00 1970
s2: Fri Jan  2 00:00:00 1970

このように、s1とs2が同じ内容になってしまうことがあります。

必要に応じて自分のバッファにstrcpyなどでコピーしてから使うのが安全です。

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

int main(void) {
    time_t t1 = 0;
    time_t t2 = 24 * 60 * 60;

    char buf1[32];
    char buf2[32];

    // ctimeの結果を自前のバッファにコピーする
    strncpy(buf1, ctime(&t1), sizeof(buf1) - 1);
    buf1[sizeof(buf1) - 1] = '\0';

    strncpy(buf2, ctime(&t2), sizeof(buf2) - 1);
    buf2[sizeof(buf2) - 1] = '\0';

    printf("buf1: %s", buf1);
    printf("buf2: %s", buf2);

    return 0;
}

内部バッファのライフタイムと所有権を意識することが、ctimeを安全に扱う鍵です。

スレッド安全性

ctimelocaltimeは、多くの処理系でスレッド安全ではありません

理由はどちらも内部で静的なバッファを使っており、複数スレッドから同時に利用するとデータが競合する可能性があるからです。

スレッド安全な代替関数として、POSIX環境などでは次の関数が用意されていることがあります。

  • ctime_r: ctimeのスレッド安全版
  • localtime_r: localtimeのスレッド安全版

これらは呼び出し側がバッファやstruct tmを用意して渡す形になっています。

例としてctime_rの使い方の雰囲気を示します(環境によってシグネチャが異なることがあります)。

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

int main(void) {
    time_t now;
    char buf[32]; // 呼び出し側がバッファを用意
    char *ret;

    time(&now);

    // ctime_r(スレッド安全版)の呼び出し例
    ret = ctime_r(&now, buf);
    if (ret == NULL) {
        printf("ctime_r failed.\n");
        return 1;
    }

    printf("結果: %s", buf);

    return 0;
}

マルチスレッドプログラムで時刻を扱う場合は、可能ならctime_rlocaltime_rなどの再入可能版を使うことを検討してください。

一方で、初心者がまず1スレッドの練習をする段階では、通常のctimelocaltimeで問題ありません。

UTCとローカル時刻の違い

時刻を扱うときに混乱しやすいポイントが、UTC(協定世界時)ローカル時刻の違いです。

  • timeが返すtime_tは、基本的にUTCを基準とした秒数です。
  • ctimeローカル時刻として文字列を生成します。
  • localtimeもローカル時刻のstruct tmを返します。
  • UTCのstruct tmが欲しい場合はgmtimeを使います。

UTCベースの扱いをしたい場合の例を示します。

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

int main(void) {
    time_t now;
    struct tm *utc;
    char buf[64];

    time(&now);

    // time_t → struct tm(UTC)に変換
    utc = gmtime(&now);
    if (utc == NULL) {
        printf("gmtime failed.\n");
        return 1;
    }

    // UTCで整形
    if (strftime(buf, sizeof(buf),
                 "%Y-%m-%d %H:%M:%S UTC",
                 utc) == 0) {
        printf("strftime failed.\n");
        return 1;
    }

    printf("UTC時刻: %s\n", buf);

    return 0;
}
実行結果
UTC時刻: 2025-11-22 04:45:10 UTC

日本(UTC+9)では、ローカル時刻とは9時間の差があることが分かります。

ctimeやlocaltimeは「ローカル時刻」、gmtimeは「UTC」という区別を頭に入れておくと、時間のズレで悩みにくくなります。

C言語初心者が混乱しやすいポイントの整理

最後に、C言語初心者がctimeや時間処理で混乱しやすいポイントを整理しておきます。

  1. time_tが何を表しているか分からない
    → 多くの環境で「1970-01-01 00:00:00(UTC)からの秒数」です。直接見て意味が分からなくても問題はなく、ctimelocaltime経由で人間向け形式に変換して使います。
  2. ctimeの戻り値をfreeしてしまう
    ctimeの戻り値はライブラリ内部の静的バッファです。絶対にfreeしないでください。必要ならstrcpyなどで自前の配列にコピーします。
  3. ctimeの文字列の末尾に改行が入っているのを忘れる
    → すでに\nが含まれているので、printf("%s\n", s);と書くと改行が2つになります。改行を取り除きたい場合はstrchr\nを探して\0に置き換えるなどします。
  4. struct tmの年と月の値に戸惑う
    tm_yearは「1900年からの年数」、tm_monは「0が1月」です。人が読む西暦と月にするにはyear = tm_year + 1900;month = tm_mon + 1;としてください。
  5. ctimeとstrftimeの役割の違いがあいまい
    ctimeは「おまかせで1種類の英語フォーマット」、strftimeは「自分で書式指定して自由に整形」と覚えると整理しやすいです。
  6. UTCとローカル時刻の違いを忘れる
    time/ctime/localtimeは「ローカル時刻」、gmtimeは「UTC」です。サーバとクライアント、ログ出力などで時刻がずれて見えるときは、この違いを疑ってください。

これらのポイントを意識しておけば、「time_t → ctime → struct tm → strftime」という流れを迷わず使いこなせるようになります。

まとめ

C言語で時刻を扱う際、time_tは内部表現として秒数を持ち、ctimeはそれを手軽に英語形式の文字列へ変換してくれる関数です。

「とりあえず現在時刻を表示したい」ならtimectimeの組み合わせだけで十分ですが、「〇年〇月〇日〇時〇分」など日本語形式で細かく制御したい場合は、localtimestruct tmに変換し、strftimeで書式を指定するのが基本です。

内部バッファの扱いやUTCとローカル時刻の違いなどの注意点を押さえながら、サンプルコードを実際に動かして理解を深めていけば、時刻処理は確実に身につきます。

数学・数値処理

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

URLをコピーしました!