C言語で日付や時刻を扱う際、人間が読みやすい文字列に整形するのに欠かせないのがstrftime関数です。
本記事では、C言語標準ライブラリのstrftimeを中心に、書式指定子の一覧と実用的なフォーマット例を、図解とサンプルコードを交えながら丁寧に解説します。
ログ出力やファイル名の生成など、実務ですぐ使える形を目指します。
C言語のstrftimeとは
strftimeとは

strftimeは、C言語標準ライブラリ<time.h>に含まれる日付時刻整形関数です。
構造体struct tmの内容を、指定したフォーマットに基づいて文字列に変換します。
基本的な関数プロトタイプは次のようになっています。
size_t strftime(
char *s, // 出力先バッファ
size_t max, // バッファの最大サイズ(終端'\0'を含む)
const char *format, // 書式指定文字列
const struct tm *tm // 日付・時刻の情報
);
戻り値は、sに書き込まれた文字数(終端の'\0'は含まない)です。
指定サイズに収まらず失敗した場合は0が返ります。
C言語における日付時刻フォーマットの基本
C言語で日付や時刻を扱う典型的な流れは、次の3段階です。
time_tとして現在時刻を取得localtimeやgmtimeでstruct tmに変換strftimeで人間が読むための文字列に整形

次のサンプルで全体像を確認します。
#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_year | 1900年からの年数 | 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”など |
%R | 24時間表記の時:分(%H:%Mと同じ) | 15:04 |
%T | 24時間表記の時:分:秒(%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 |
%r | 12時間表記の時刻(ロケール依存) | 03:04:05 PM など |
%R | 24時間の時:分(%H:%M) | 15:04 |
%T | 24時間の時:分:秒(%H:%M:%S) | 15:04:05 |
ロケールに依存しない安定した形式を使いたい場合は、%Fや%Tなど、形式が明示されている指定子の利用が適しています。
Unix系拡張フォーマット指定子

多くのUnix系環境やglibcでは、標準Cにはない拡張書式指定子が使えることがあります。
代表的なものを挙げます。
| 指定子 | 意味(非標準の可能性あり) | 例 |
|---|---|---|
%s | Unixエポック(1970-01-01 00:00:00 UTC)からの秒数 | 1734000000 など |
%z | UTCからの時差(±hhmm) | +0900 |
%Z | タイムゾーン名 | JST、UTC など |
これらは処理系依存であり、移植性を重視する場合は避けるか、使用環境を限定して利用する必要があります。
改行や%記号のエスケープ指定子
strftimeで%そのものや、改行などの制御文字を表現したい場合があります。
そのときに使う指定子が次のものです。
| 指定子 | 意味 | 備考 |
|---|---|---|
%% | %文字そのものを出力 | エスケープとして最重要 |
%n | 改行文字(実装依存で空白の場合も) | POSIXでは改行推奨 |
%t | タブ文字(実装依存で空白の場合も) | 非対応環境もあり得る |
単に%を出したい場合は%%を必ず使うようにします。
"進捗: 50%% 完了"といった文字列を作る場面がよくあります。
非標準・実装依存の書式指定子に注意

strftimeの書式指定子は、C言語標準で定められているものと、処理系やOSが拡張として提供しているものがあります。
特に次の点に注意が必要です。
- 標準Cのみを前提とする場合は、標準規格で定義された指定子に限定する
%sや%zなどは、環境によっては使えない- 0埋めを抑制する
%-dなどの「フラグ付き指定」は多くが非標準
移植性のあるコードを目指すなら、環境依存の指定子は極力避けるか、コメントで「このコードはglibc前提」などと明記するとよいです。
strftimeと日付時刻フォーマットの実用テクニック
よく使う日付フォーマット例

実際の開発では、目的に応じてフォーマットを使い分けることが重要です。
代表的な日付フォーマットを、簡単なサンプルコードとともに紹介します。
#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」で保存すれば問題ありません。
ログ向け時刻フォーマット例

ログメッセージでは、ソートしやすく、タイムゾーンが明確で、ロケールに依存しないフォーマットが推奨されます。
#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に便利)
#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だけでは時刻の計算はできません。
時刻の計算や変換にはmktimeやlocaltime、gmtimeとの連携が必要です。
localtime…time_tをローカル時刻のstruct tmに変換gmtime…time_tをUTCのstruct tmに変換mktime…ローカルstruct tmをtime_tに変換し、フィールドの正規化も行う
次のサンプルでは、任意の日付文字列を読み取り、1日後の日付を出力します。
#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の中身は未定義です。
典型的なミスは、「戻り値をチェックしない」ことです。
// 悪い例: 戻り値を確認していない
strftime(buf, 16, "%Y-%m-%d %H:%M:%S", lt);
printf("%s\n", buf); // バッファが小さいと危険
必ず戻り値を確認し、0だった場合の処理を用意しましょう。
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の返すポインタの扱い
localtimeやgmtimeは、静的領域のstruct tmへのポインタを返します。
そのため、複数回呼び出すと同じ領域が上書きされます。
struct tm *a = localtime(&t1);
struct tm *b = localtime(&t2);
// aとbは同じ領域を指している可能性が高い
必要であればstruct tmを自前で用意し、localtime_r(POSIX)などのスレッドセーフ版を使うか、内容を別の構造体にコピーして使うようにします。
ロケール依存形式の誤用
%cなどロケール依存の指定子を、システム間でやり取りするデータ形式に使うと、環境によって解釈できなくなります。
ログや設定ファイル、通信データにはロケール非依存の形式を使うのが安全です。
まとめ
strftimeは、C言語で日付時刻を扱ううえで欠かせないフォーマット関数です。
struct tmを元に、%Yや%mなどの書式指定子を組み合わせることで、用途に応じた日時文字列を自在に生成できます。
標準的な指定子に加え、ロケール依存性や実装依存の拡張にも注意を払えば、ログ出力からファイル名生成、システム間連携まで、堅牢で読みやすい日付時刻フォーマットを実現できます。
