閉じる

【C言語】strftime書式指定子一覧|日付時刻フォーマット超入門

C言語で日付や時刻を扱う際、人間が読みやすい文字列に整形するのに欠かせないのがstrftime関数です。

本記事では、C言語標準ライブラリのstrftimeを中心に、書式指定子の一覧と実用的なフォーマット例を、図解とサンプルコードを交えながら丁寧に解説します。

ログ出力やファイル名の生成など、実務ですぐ使える形を目指します。

C言語のstrftimeとは

strftimeとは

strftimeは、C言語標準ライブラリ<time.h>に含まれる日付時刻整形関数です。

構造体struct tmの内容を、指定したフォーマットに基づいて文字列に変換します。

基本的な関数プロトタイプは次のようになっています。

C言語
size_t strftime(
    char       *s,      // 出力先バッファ
    size_t      max,    // バッファの最大サイズ(終端'\0'を含む)
    const char *format, // 書式指定文字列
    const struct tm *tm // 日付・時刻の情報
);

戻り値は、sに書き込まれた文字数(終端の'\0'は含まない)です。

指定サイズに収まらず失敗した場合は0が返ります。

C言語における日付時刻フォーマットの基本

C言語で日付や時刻を扱う典型的な流れは、次の3段階です。

  1. time_tとして現在時刻を取得
  2. localtimegmtimestruct tmに変換
  3. strftime人間が読むための文字列に整形

次のサンプルで全体像を確認します。

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

int main(void) {
    time_t now;                // 現在時刻(秒)を保持する型
    struct tm *local_time;     // ローカル時刻を表す構造体(pointer)
    char buf[64];              // 出力用バッファ

    // 現在時刻を取得
    now = time(NULL);
    if (now == (time_t)-1) {
        perror("time failed");
        return 1;
    }

    // time_t → struct tm(ローカル時刻)に変換
    local_time = localtime(&now);
    if (local_time == NULL) {
        perror("localtime failed");
        return 1;
    }

    // struct tm → 文字列に変換
    // 例: "2025-12-12 15:30:45" のような形式
    if (strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", local_time) == 0) {
        // 0が返った場合はバッファサイズ不足などの可能性
        fprintf(stderr, "strftime failed or buffer too small\n");
        return 1;
    }

    printf("現在時刻: %s\n", buf);
    return 0;
}

想定される実行結果例は次のようになります。

実行結果
現在時刻: 2025-12-12 15:30:45

ここで使っている%Y%mなどがstrftimeの書式指定子です。

これらを自由に組み合わせることで、任意のフォーマットの日時文字列を生成できます。

struct tmとstrftimeの関係

struct tmは、日付時刻を構成要素に分解した構造体です。

主なフィールドと意味を整理しておきます。

フィールド意味代表値の例
tm_year1900年からの年数2025年なら125
tm_mon月(0〜11)1月なら0、12月なら11
tm_mday日(1〜31)1〜31
tm_hour時(0〜23)0〜23
tm_min分(0〜59)0〜59
tm_sec秒(0〜60、うるう秒考慮)0〜60
tm_wday曜日(0〜6、日曜が0)0(日)〜6(土)
tm_yday年内通算日(0〜365、1月1日が0)0〜365
tm_isdst夏時間フラグ(>0:有効、0:無効,<0:不明)実装依存

strftimeは、このstruct tmの値を読み取り、書式指定子ごとに解釈して文字列にします

そのため、struct tmの内容が正しくセットされていることが重要です。

strftime書式指定子一覧

日付フォーマット指定子

日付の基本的な部分(年・月・日)を表す指定子は頻出です。

よく使うものを中心にまとめます。

指定子意味出力例(2025年12月12日)
%Y西暦(4桁)2025
%y西暦の下2桁25
%m月(2桁、01〜12)12
%-mなど0埋めしない月(実装依存、後述)12
%d日(2桁、01〜31)12
%e日(前に空白を入れる、1桁日は先頭空白)“12”または” 3″
%j年内通算日(001〜366)346

最も基本的でよく使う組み合わせとして、次のようなフォーマットが挙げられます。

  • "%Y-%m-%d"ISO 8601風の標準的な日付(例: 2025-12-12)
  • "%Y/%m/%d" → スラッシュ区切り(例: 2025/12/12)
  • "%m/%d/%Y" → 米国式(例: 12/12/2025)

時刻フォーマット指定子

時刻(時・分・秒)に関する主な指定子は次のとおりです。

指定子意味出力例(15時04分05秒)
%H時(24時間表記、00〜23)15
%I時(12時間表記、01〜12)03
%M分(00〜59)04
%S秒(00〜60)05
%p午前/午後(ロケール依存)“AM”または”PM”など
%R24時間表記の時:分(%H:%Mと同じ)15:04
%T24時間表記の時:分:秒(%H:%M:%S)15:04:05

24時間表記が基本であり、ログやシステム出力では%H:%M:%Sまたは%Tがよく使われます。

12時間表記が必要な場合は%I:%M:%S %pのようにします。

曜日・月名フォーマット指定子

strftimeは、文字による曜日や月名も出力できます。

これらはロケール(地域設定)に依存します。

日本ロケールなら日本語、英語ロケールなら英語になる可能性があります。

指定子意味例(ロケールにより変化)
%a曜日の省略名“Sun”、”日” など
%A曜日の完全な名前“Sunday”、”日曜日” など
%b月の省略名“Dec”、”12月” など
%B月の完全な名前“December”、”12月” など
%u曜日(1〜7、月曜=1)1〜7
%w曜日(0〜6、日曜=0)0〜6

日本語ロケールであれば"%Y年%m月%d日(%a)"のようにして「2025年12月12日(金)」といった形式を作ることができます。

日付と時刻の組み合わせ指定子

日付と時刻をよく使われる形式にまとめた「ショートカット指定子」もあります。

指定子意味
%c日付と時刻(ロケール依存の標準形式)“Fri Dec 12 15:04:05 2025” など
%x日付(ロケール依存の通常形式)“12/12/25” など
%X時刻(ロケール依存の通常形式)“15:04:05” など
%D日付%m/%d/%yと同等12/12/25
%F日付%Y-%m-%dと同等2025-12-12
%r12時間表記の時刻(ロケール依存)03:04:05 PM など
%R24時間の時:分(%H:%M)15:04
%T24時間の時:分:秒(%H:%M:%S)15:04:05

ロケールに依存しない安定した形式を使いたい場合は、%F%Tなど、形式が明示されている指定子の利用が適しています。

Unix系拡張フォーマット指定子

多くのUnix系環境やglibcでは、標準Cにはない拡張書式指定子が使えることがあります。

代表的なものを挙げます。

指定子意味(非標準の可能性あり)
%sUnixエポック(1970-01-01 00:00:00 UTC)からの秒数1734000000 など
%zUTCからの時差(±hhmm)+0900
%Zタイムゾーン名JST、UTC など

これらは処理系依存であり、移植性を重視する場合は避けるか、使用環境を限定して利用する必要があります。

改行や%記号のエスケープ指定子

strftime%そのものや、改行などの制御文字を表現したい場合があります。

そのときに使う指定子が次のものです。

指定子意味備考
%%%文字そのものを出力エスケープとして最重要
%n改行文字(実装依存で空白の場合も)POSIXでは改行推奨
%tタブ文字(実装依存で空白の場合も)非対応環境もあり得る

単に%を出したい場合は%%を必ず使うようにします。

"進捗: 50%% 完了"といった文字列を作る場面がよくあります。

非標準・実装依存の書式指定子に注意

strftimeの書式指定子は、C言語標準で定められているものと、処理系やOSが拡張として提供しているものがあります

特に次の点に注意が必要です。

  • 標準Cのみを前提とする場合は、標準規格で定義された指定子に限定する
  • %s%zなどは、環境によっては使えない
  • 0埋めを抑制する%-dなどの「フラグ付き指定」は多くが非標準

移植性のあるコードを目指すなら、環境依存の指定子は極力避けるか、コメントで「このコードはglibc前提」などと明記するとよいです。

strftimeと日付時刻フォーマットの実用テクニック

よく使う日付フォーマット例

実際の開発では、目的に応じてフォーマットを使い分けることが重要です。

代表的な日付フォーマットを、簡単なサンプルコードとともに紹介します。

C言語
#include <locale.h> // 1. これを追加
#include <stdio.h>
#include <time.h>

int main(void) {
    // 2. ロケールをシステムのデフォルト(日本語環境なら日本語)に設定
    setlocale(LC_ALL, "");
    // ※特定の環境(Linux等)で明示的に指定する場合は setlocale(LC_ALL,
    // "ja_JP.UTF-8"); など

    time_t now = time(NULL);
    struct tm* lt = localtime(&now);
    char buf[64];

    if (!lt) {
        perror("localtime");
        return 1;
    }

    // 1) ISO 8601風
    strftime(buf, sizeof(buf), "%Y-%m-%d", lt);
    printf("ISO形式日付: %s\n", buf);

    // 2) スラッシュ区切り
    strftime(buf, sizeof(buf), "%Y/%m/%d", lt);
    printf("スラッシュ区切り: %s\n", buf);

    // 3) 年月日+曜日
    // ロケール設定後は %a が「月」「火」などの日本語になります
    strftime(buf, sizeof(buf), "%Y年%m月%d日(%a)", lt);
    printf("日本語表記例: %s\n", buf);

    return 0;
}
実行結果
ISO形式日付: 2025-12-12
スラッシュ区切り: 2025/12/12
日本語表記例: 2025年12月12日(金)

日付だけのフォーマットは%Fで簡略化することもでき、"%F""%Y-%m-%d"と同じ意味になります。

文字化けが発生する場合

Windows (コマンドプロンプト) の場合:
ソースコードを「Shift-JIS (CP932)」で保存するか、コマンドプロンプト側を chcp 65001 (UTF-8モード) に変更してソースをUTF-8で保存する必要があります。

Linux / macOS の場合:
通常はソースコードを「UTF-8」で保存すれば問題ありません。

ログ向け時刻フォーマット例

ログメッセージでは、ソートしやすく、タイムゾーンが明確で、ロケールに依存しないフォーマットが推奨されます。

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

static void log_message(const char *level, const char *msg) {
    time_t now = time(NULL);
    struct tm *lt = localtime(&now);
    char ts[32];

    if (!lt) {
        // 時刻取得に失敗した場合は、タイムスタンプなしで出力
        fprintf(stderr, "[%s] %s\n", level, msg);
        return;
    }

    // ログ向けの安定したフォーマット: YYYY-MM-DD HH:MM:SS
    // %F = %Y-%m-%d, %T = %H:%M:%S
    if (strftime(ts, sizeof(ts), "%F %T", lt) == 0) {
        // バッファ不足などの場合も、タイムスタンプなしで出力
        fprintf(stderr, "[%s] %s\n", level, msg);
        return;
    }

    fprintf(stderr, "%s [%s] %s\n", ts, level, msg);
}

int main(void) {
    log_message("INFO",  "アプリケーションを開始しました");
    log_message("WARN",  "設定ファイルが見つかりません");
    log_message("ERROR", "接続に失敗しました");

    return 0;
}
実行結果
2025-12-12 15:04:05 [INFO] アプリケーションを開始しました
2025-12-12 15:04:05 [WARN] 設定ファイルが見つかりません
2025-12-12 15:04:05 [ERROR] 接続に失敗しました

日付と時刻の区切りにスペースを使う%F %T」形式は、視認性と機械処理の両方の観点でバランスがよいため、ログや監査記録に向いています。

ロケールに依存しないフォーマットのコツ

ロケール(地域設定)に依存すると、環境によってフォーマットが変わるため、ログやデータ交換には不向きです。

具体的には、次の指定子はロケール依存です。

  • %c%x%X
  • %a%A%b%B
  • %p など

ロケールに依存しない安定した形式を得るためのコツは、数値だけの指定子を組み合わせることです。

たとえば次のような形式です。

  • %Y-%m-%d (または%F)
  • %H:%M:%S (または%T)
  • %Y%m%d%H%M%S (ファイル名やIDに便利)
C言語
#include <stdio.h>
#include <time.h>

int main(void) {
    time_t now = time(NULL);
    struct tm *gt = gmtime(&now);
    char buf[32];

    if (!gt) {
        perror("gmtime");
        return 1;
    }

    // ロケールに依存しないUTC表現
    // 例: 2025-12-12T06:04:05Z
    strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", gt);
    printf("UTC(ロケール非依存): %s\n", buf);

    return 0;
}

このように英数字と固定記号だけで構成されたフォーマットにしておくと、どの環境でも同じ形で比較・保存ができ、システム間連携にも適します。

strftimeとmktime・localtime・gmtimeの連携

strftimeだけでは時刻の計算はできません

時刻の計算や変換にはmktimelocaltimegmtimeとの連携が必要です。

  • localtimetime_tをローカル時刻のstruct tmに変換
  • gmtimetime_tをUTCのstruct tmに変換
  • mktime…ローカルstruct tmtime_tに変換し、フィールドの正規化も行う

次のサンプルでは、任意の日付文字列を読み取り、1日後の日付を出力します。

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

int main(void) {
    struct tm t = {0};  // 必ず0で初期化しておく
    char buf[32];

    // 例として「2025-12-12」をstruct tmに手動でセット
    t.tm_year = 2025 - 1900; // 西暦から1900を引く
    t.tm_mon  = 12 - 1;      // 月は0始まり
    t.tm_mday = 12;          // 日はそのまま

    // tm_hourなど未設定フィールドは0のまま

    // ローカル時間としてtime_tに変換(このとき正規化も行われる)
    time_t tt = mktime(&t);
    if (tt == (time_t)-1) {
        perror("mktime");
        return 1;
    }

    // 1日(24時間*60分*60秒)を加算
    tt += 24 * 60 * 60;

    // 再度ローカル時刻に変換
    struct tm *next = localtime(&tt);
    if (!next) {
        perror("localtime");
        return 1;
    }

    // 結果をフォーマット
    strftime(buf, sizeof(buf), "%F", next); // %F = %Y-%m-%d
    printf("翌日の日付: %s\n", buf);

    return 0;
}
実行結果
翌日の日付: 2025-12-13

このように、計算はtime_tの段階で行い、表示はstrftimeに任せる、という役割分担を意識すると、間違いの少ないコードを書けます。

よくあるエラーとデバッグのポイント

strftime周辺でよく起きるトラブルと、その対処方法をまとめます。

バッファサイズ不足で0が返る

strftimeは、バッファが足りないと0を返します。

このときbufの中身は未定義です。

典型的なミスは、「戻り値をチェックしない」ことです。

C言語
// 悪い例: 戻り値を確認していない
strftime(buf, 16, "%Y-%m-%d %H:%M:%S", lt);
printf("%s\n", buf);  // バッファが小さいと危険

必ず戻り値を確認し、0だった場合の処理を用意しましょう。

C言語
size_t n = strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt);
if (n == 0) {
    fprintf(stderr, "strftime failed: buffer too small?\n");
    // エラー処理を書く
}

localtime/gmtimeの返すポインタの扱い

localtimegmtimeは、静的領域のstruct tmへのポインタを返します。

そのため、複数回呼び出すと同じ領域が上書きされます。

C言語
struct tm *a = localtime(&t1);
struct tm *b = localtime(&t2);
// aとbは同じ領域を指している可能性が高い

必要であればstruct tmを自前で用意し、localtime_r(POSIX)などのスレッドセーフ版を使うか、内容を別の構造体にコピーして使うようにします。

ロケール依存形式の誤用

%cなどロケール依存の指定子を、システム間でやり取りするデータ形式に使うと、環境によって解釈できなくなります。

ログや設定ファイル、通信データにはロケール非依存の形式を使うのが安全です。

まとめ

strftimeは、C言語で日付時刻を扱ううえで欠かせないフォーマット関数です。

struct tmを元に、%Y%mなどの書式指定子を組み合わせることで、用途に応じた日時文字列を自在に生成できます。

標準的な指定子に加え、ロケール依存性や実装依存の拡張にも注意を払えば、ログ出力からファイル名生成、システム間連携まで、堅牢で読みやすい日付時刻フォーマットを実現できます。

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

URLをコピーしました!