閉じる

【C言語】 strftimeの使い方と書式一覧|YYYY/MM/DD表示を一発で

C言語で現在日時を「2025/12/12」のように整った形で表示したい場面はとても多いです。

そのときに便利なのがstrftime関数です。

本記事では、time関数やtm構造体との関係から、YYYY/MM/DD形式を一発で表示する具体的なサンプルまで、図解とコードを交えながら詳しく解説します。

書式指定子の一覧表も掲載しますので、コピペしながら実践的に使えるようになります。

C言語のstrftimeとは

strftimeの基本概要と特徴

C言語のstrftime関数は、日付や時刻を表す構造体(tm構造体)から、指定したフォーマットの文字列を生成するための関数です。

標準ライブラリのtime.hに定義されており、OS や環境に依存せずに利用できます。

ポイントを文章で整理すると、次のような特徴があります。

strftimeは、内部的に扱われる「機械向きの時間情報」を「人間が読みやすい文字列」に変換するための仕上げ役であり、特にログ出力や画面表示で頻繁に使用されます。

time関数・localtime関数との関係

strftimeは単独では使わず、通常はtime関数localtime関数などと組み合わせて利用します。

まずtime関数で現在時刻をtime_t型として取得し、そのtime_tlocaltime関数でstruct tmに変換します。

このstruct tmを、最後にstrftimeで文字列に整形する、という流れになります。

この3つの関数は、次のような役割分担をしています。

  • time関数: 現在時刻を「秒数」などの内部表現で取得します。
  • localtime関数: 内部表現の時刻を「年」「月」「日」などの構造体に分解します。
  • strftime関数:分解された時間情報を、指定した書式の文字列に変換します。

この流れを理解しておくと、後のサンプルコードも読みやすくなります。

日付フォーマットを文字列に変換する仕組み

strftimeは、"%Y/%m/%d"のような書式文字列を1文字ずつ読みながら、'%'で始まる部分を書式指定子として解釈し、tm構造体の対応するメンバーで置き換えていきます。

たとえば、次のような変換が行われます。

  • %Y → 西暦4桁(例: 2025)
  • %m → 月(2桁、0埋め、01〜12)
  • %d → 日(2桁、0埋め、01〜31)

この結果、"%Y/%m/%d""2025/12/12"という文字列に展開されます。

strftimeは内部でバッファに結果を書き込むため、バッファサイズの指定や戻り値の確認が重要になります。

この点については後ほど詳しく説明します。

strftimeの基本的な使い方

strftimeの関数プロトタイプと引数

まず、公式なプロトタイプを確認します。

time.hに以下のように宣言されています。

C言語
size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);

各引数の役割は次の通りです。

  1. s
    出力先となる文字列バッファへのポインタです。ここに、整形された日付文字列が書き込まれます。
  2. max
    バッファのサイズを文字数(終端の'\0'も含む)で指定します。オーバーフローを防ぐために必須です。
  3. format
    書式指定子を含むフォーマット文字列です。たとえば"%Y/%m/%d"のように指定します。
  4. tm
    日付・時刻情報を格納したstruct tmへのポインタです。通常はlocaltimegmtimeで取得した値を渡します。

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

0の場合は、バッファサイズが足りなかったか、環境によっては一部の書式を処理できなかったことを意味します。

tm構造体の準備手順

strftimeを使うためには、入力となるstruct tmを用意する必要があります。

一般的なケースでは、timelocaltimeを組み合わせて現在時刻のtmを取得します。

代表的な取得パターンは次の通りです。

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

int main(void) {
    time_t now;              // 現在時刻を格納する変数(time_t型)
    struct tm *ptm;          // 分解された時刻を指すポインタ

    time(&now);              // 現在時刻を取得(エポックからの秒数など)
    ptm = localtime(&now);   // ローカル時刻のstruct tm*を取得

    // ptm->tm_year, ptm->tm_mon, ptm->tm_mday などが使えるようになる
    printf("Year since 1900: %d\n", ptm->tm_year);
    printf("Month (0-11): %d\n", ptm->tm_mon);
    printf("Day (1-31): %d\n", ptm->tm_mday);

    return 0;
}

上記では、tm構造体の実体はライブラリ内部にあり、localtimeはそのアドレスだけを返しています。

構造体のメンバーは以下のような意味を持ちます。

メンバー名意味
tm_yearint1900年からの年数(例: 2025年なら125)
tm_monint月(0〜11、0が1月、11が12月)
tm_mdayint日(1〜31)
tm_hourint時(0〜23)
tm_minint分(0〜59)
tm_secint秒(0〜60、うるう秒を考慮)
tm_wdayint曜日(0〜6、0が日曜日)
tm_ydayint年内の通算日(0〜365、0が1月1日)
tm_isdstintサマータイムフラグ(>0:夏時間、0:非適用など)

自分で任意の日付を構築してstrftimeに渡したい場合は、手動でtm構造体を埋めることもできます。

ただし、その場合はmktimeで正規化するのが安全です。

現在日時を取得してYYYY/MM/DDで表示するサンプルコード

ここではもっともよく使うパターンとして、現在日時を取得し、YYYY/MM/DD形式で表示するサンプルコードを示します。

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

int main(void) {
    time_t now;                 // 現在時刻を格納するための変数
    struct tm *ptm;             // 分解された時刻(ローカル時刻)へのポインタ
    char buf[64];               // 出力用文字列バッファ

    // 現在時刻をtime_t型で取得
    time(&now);

    // time_tをローカル時間のstruct tm*に変換
    ptm = localtime(&now);
    if (ptm == NULL) {
        // 変換に失敗した場合の簡易エラーメッセージ
        fprintf(stderr, "localtime failed\n");
        return 1;
    }

    // "%Y/%m/%d" 形式で日付を文字列に変換
    // 例: "2025/12/12"
    if (strftime(buf, sizeof(buf), "%Y/%m/%d", ptm) == 0) {
        // 0 が返った場合は、バッファ不足などで失敗
        fprintf(stderr, "strftime failed (buffer too small?)\n");
        return 1;
    }

    // 結果を表示
    printf("Today: %s\n", buf);

    return 0;
}

上記は実行環境により出力内容が変わりますが、形式としては次のようになります。

実行結果
Today: 2025/12/12

このサンプルでは、YYYY/MM/DD形式の文字列を一発で生成しています。

"%Y/%m/%d"という書式指定がその役割を担っています。

バッファサイズと戻り値の扱い方

strftimeを安全に使うには、バッファサイズと戻り値を正しく扱うことが非常に重要です。

関数は、次のような条件で動作します。

  • 書式展開後の文字列がmax−1文字以下であれば、その文字列をsに書き込み、終端に'\0'を付けて返します。
  • 文字列がmax−1文字を超える場合、文字列をsに書き込まず、戻り値として0を返します。

したがって、戻り値が0かどうかを必ずチェックするのが望ましいです。

次のような書き方をしておくと安全です。

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

int main(void) {
    time_t now;
    struct tm *ptm;
    char buf[32];  // わざと小さめのバッファ

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

    // 日付と時刻を含む長めのフォーマット
    size_t len = strftime(buf, sizeof(buf), "%Y/%m/%d %H:%M:%S %Z", ptm);

    if (len == 0) {
        // バッファが小さすぎる可能性が高い
        fprintf(stderr, "strftime failed: buffer too small (size=%zu)\n", sizeof(buf));
        return 1;
    }

    printf("Now: %s (length=%zu)\n", buf, len);

    return 0;
}

このように、十分なサイズのバッファを確保しつつ、戻り値が0かどうかをチェックすることで、安全で堅牢なコードになります。

特に、ログフォーマットに多くの情報を詰め込むときは、バッファサイズに余裕を持たせることが重要です。

strftimeの書式指定子一覧

日付(年月日)の主な書式指定子

strftimeには多数の書式指定子がありますが、まずは年月日に関する代表的なものを整理します。

環境により対応状況が異なる場合がありますが、POSIXなどで標準的に使われるものを中心に挙げます。

書式指定子出力例説明
%Y20254桁の西暦(0000〜9999)
%y25西暦の下2桁(00〜99)
%m12月(2桁、0埋め、01〜12)
%-m12 (環境依存)月(0埋めなし、1〜12、GNU拡張など)
%d03日(2桁、0埋め、01〜31)
%-d3 (環境依存)日(0埋めなし、1〜31、GNU拡張など)
%e3日(先頭が空白で埋められる、” 1″〜”31″)
%j337年内通算日(001〜366)

YYYY/MM/DD形式で表示したい場合は、基本的に"%Y/%m/%d"を覚えておけば十分です。

0埋めをしたくない場合などは、環境が対応していれば"%Y/%-m/%-d"のように書くこともありますが、ポータビリティを考えると"%Y/%m/%d"を使う方が無難です。

時刻(時分秒)の主な書式指定子

次に、時刻(時分秒)に関する代表的な書式指定子です。

書式指定子出力例説明
%H0924時間表記の時(0埋め、00〜23)
%I0912時間表記の時(0埋め、01〜12)
%M05分(0埋め、00〜59)
%S07秒(0埋め、00〜60)
%T09:05:0724時間表記の時刻、%H:%M:%Sと同じ
%R09:0524時間表記の時刻、%H:%Mと同じ
%pAM午前/午後(ロケール依存、”AM”/”PM”など)

これらを組み合わせることで、次のような表現が可能です。

  • "%H:%M:%S""09:05:07"
  • "%Y/%m/%d %H:%M""2025/12/03 09:05"
  • "%H時%M分%S秒""09時05分07秒"

日本語を含めたフォーマットも、そのまま文字列中に日本語を記述するだけで利用できます。

曜日・月名などの文字列表現

strftimeは、曜日名や月名を文字列として出力することもできます。

これらはロケールに依存するため、システムのロケール設定次第で日本語や英語になります。

書式指定子出力例(ロケールによる)説明
%aWed / 水曜日の省略形
%AWednesday / 水曜日曜日の完全な名前
%bDec / 12月月の省略形
%BDecember / 12月月の完全な名前
%hDec%bと同じ(互換用)

日本語ロケールが有効な環境では、"%Y年%m月%d日(%a)"などと指定すると、"2025年12月03日(水)"のように曜日が日本語で出力される場合があります。

ロケール設定については後述します。

ロケールに依存する書式の注意点

strftimeの書式指定子の中には、ロケールに依存して書式そのものが変わるものがあります。

代表的なものは次の通りです。

書式指定子意味例(ロケールによって変化)
%xロケール固有の日付表記“2025/12/03” や “12/03/25” など
%Xロケール固有の時刻表記“09:05:07” や “9:05:07 AM” など
%cロケール固有の日付と時刻の表記“2025年12月3日(水) 9時05分07秒” など
%p午前/午後を表す文字列“AM”/”PM” や “午前”/”午後” など

これらを使用すると、環境ごとに異なる文字列が生成されます。

そのため、ログフォーマットやファイル名など、環境に依存せず一定の形式にしたい場合は、%Y, %m, %d, %H などの「固定フォーマット」を積極的に使うことが重要です。

必要に応じて、setlocale関数でロケールを明示的に指定することもできます。

たとえば日本語ロケールを有効にしてからstrftimeを使う例は次のようになります。

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

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

    // 日本語ロケール(例: UTF-8)を有効化
    // 実際に使えるロケール名は環境依存です
    setlocale(LC_TIME, "ja_JP.UTF-8");

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

    // ロケール依存の書式を試す
    strftime(buf, sizeof(buf), "%c", ptm);
    printf("Locale-dependent: %s\n", buf);

    return 0;
}

ロケール設定はシステム環境と密接に関連しますので、ミドルウェアやライブラリ側のロケール設定との干渉にも注意が必要です。

YYYY/MM/DD形式での表示テクニック

YYYY/MM/DDを一発で表示する書式指定例

YYYY/MM/DD形式の出力は、strftimeでは次のフォーマット1つだけで実現できます。

"%Y/%m/%d"

これを使った最小限のサンプルは以下のようになります。

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

int main(void) {
    time_t now;
    struct tm *ptm;
    char date[16];  // "YYYY/MM/DD" は 10文字なので16あれば十分

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

    // YYYY/MM/DD形式の文字列を生成
    if (strftime(date, sizeof(date), "%Y/%m/%d", ptm) == 0) {
        fprintf(stderr, "strftime failed\n");
        return 1;
    }

    printf("Date: %s\n", date);

    return 0;
}

このように、1つの書式文字列で希望の形式を一発で生成できるため、日付表示の定番パターンとして多用されます。

0埋め・2桁表示を揃えるフォーマット

strftimeの多くの数値系書式指定子は、デフォルトで0埋めを行います。

たとえば、1月は"01"と出力されます。

日付をきれいに2桁で揃えたい場合は、%m%dをそのまま使えばよく、特に追加設定は不要です。

一方、0埋めをしたくない場合は、環境によってはGNU拡張として%-m%-dが利用できます。

  • 0埋めあり(ポータブル)
    • "%Y/%m/%d""2025/01/05"
  • 0埋めなし(環境依存のGNU拡張など)
    • "%Y/%-m/%-d""2025/1/5"
  • 日本語を混在させる場合
    • "%Y年%m月%d日""2025年01月05日"
    • "%Y年%-m月%-d日""2025年1月5日" (環境依存)

ポータビリティを重視する場合は、0埋めを受け入れるか、0埋めなし表示がどうしても必要ならsnprintfなどと組み合わせて自力で整形するのが安全です。

日付と時刻を組み合わせたフォーマット例

strftimeの強みは、日付と時刻を自在に組み合わせられることです。

具体的な利用シーンごとに、代表的なフォーマット例を挙げます。

  1. ログ向け(読みやすさ重視)
    • フォーマット: "%Y/%m/%d %H:%M:%S"
    • 例: "2025/12/12 23:59:59"
    • 特徴: 日付と時刻をスペースで区切ることで視認性が高まります。
  2. ファイル名向け(記号を避ける)
    • フォーマット: "%Y%m%d_%H%M%S"
    • 例: "20251212_235959"
    • 特徴: スラッシュ'/'やコロン':'を含めず、ファイルシステムに優しい名前になります。
  3. 人向け日本語表示
    • フォーマット: "%Y年%m月%d日 %H時%M分"
    • 例: "2025年12月12日 23時59分"
    • 特徴: インターフェース上での日本語表示に適しています。

これらは、いずれも<YYYY/MM/DD>部分に“%Y/%m/%d”を組み合わせていることがわかります。

用途ごとに時刻部分の書式を変えつつ、日付部分は同じ形式を維持することで、一貫性のある表示を実現できます。

よくあるエラーとstrftimeの注意点

最後に、strftimeを使う際によくある落とし穴と注意点をまとめます。

バッファサイズ不足で出力されない

最も多い問題は、バッファサイズが小さくてstrftimeが0を返し、結果として空文字列のままになってしまうケースです。

このとき、printfなどで出力しても何も表示されません。

改善策としては、次の点を心がけます。

  • バッファは余裕を持って大きめに確保する
    たとえば日付と時刻を含む典型的なフォーマットなら、64〜128バイト程度あれば十分なことが多いです。
  • 戻り値が0でないか必ず確認する
    これにより、バッファ不足やその他の不具合を早期に検知できます。

localtimeの戻り値をチェックしない

localtimegmtimeは、失敗時にNULLを返す可能性があります。

NULLポインタをstrftimeに渡すと未定義動作になりますので、必ずチェックしましょう。

C言語
ptm = localtime(&now);
if (ptm == NULL) {
    // エラー処理
}

ロケール依存書式を仕様として固定してしまう

%x%cなどのロケール依存書式は、人間向けの一時的な表示には便利ですが、ログフォーマットやファイル名などの「仕様」として固定する用途には向きません。

環境によって形式が変わるため、後からパースしにくくなります。

このような用途では、“%Y/%m/%d %H:%M:%S” のような固定フォーマットを使用することが推奨されます。

スレッドセーフティ(localtimeとの組み合わせ)

一部の環境では、localtimeスレッドセーフではないことがあります。

そのため、マルチスレッド環境ではlocaltime_rgmtime_rなどのスレッドセーフなバージョンを使用し、自前のstruct tmに結果を書き込むと安心です。

strftime自体は、引数として渡されたstruct tmを読み取るだけなので、そのstruct tmをどう用意するかが重要になります。

まとめ

strftimeは、tm構造体から人間が読みやすい日付・時刻文字列を生成するための標準関数です。

time関数とlocaltime関数で現在時刻を取得し、“%Y/%m/%d”という書式指定子を使うことで、YYYY/MM/DD形式を一発で表示できます。

バッファサイズと戻り値のチェック、ロケール依存書式の扱いに注意しながら、ログ出力やファイル名、UI表示などに柔軟に活用してみてください。

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

URLをコピーしました!