日時を自分の好きな形に整形するには、C言語の strftime が便利です。
本記事では、「どの指定子で何が出るのか」を初心者の方にも分かりやすく、用途別に整理して紹介します。
現場でよく使う実例や、ロケールやタイムゾーンに関する注意点、戻り値やバッファサイズの扱いまで、基礎から一気に確認できます。
C言語の strftime 基本
strftimeとは
strftime は、struct tm で表した日時を、指定したフォーマット文字列に従って可読な文字列へ変換する関数です。
任意の並びや区切り記号で年月日時分秒を組み立てられるため、ログやファイル名、画面表示などに広く使われます。
ロケール(地域設定)に依存して、曜日名や月名が現地表記になる点も特徴です。
事前準備
strftime を使うには #include <time.h> が必要です。
曜日名や月名を日本語などのロケールで表示したい場合は #include <locale.h> により setlocale(LC_TIME, "...") を設定します。
#include <stdio.h>
#include <time.h>
#include <locale.h>
int main(void) {
// LC_TIME を環境依存の既定にする。日本語環境なら月名や曜日が日本語になる場合があります。
// 一貫した英語表記にしたい場合は setlocale(LC_TIME, "C") を使います。
setlocale(LC_TIME, "");
time_t now = time(NULL); // 現在のUNIX時刻(秒)
struct tm *lt = localtime(&now); // ローカルタイムへ変換
char buf[128];
// 年-月-日 時:分:秒 のよくある形式
if (strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt) > 0) {
printf("Now: %s\n", buf);
} else {
puts("バッファが不足しました");
}
return 0;
}
Now: 2025-03-01 14:05:09
実際の出力は実行時刻とロケールに依存します。
関数の形と引数
strftime の宣言は以下です。
#include <time.h>
size_t strftime(char *restrict s, size_t max,
const char *restrict format,
const struct tm *restrict tm);
s: 出力を書き込むバッファmax: バッファのサイズ(終端のヌル文字も含む最大長)format: 書式文字列。%Yや%mなどの書式指定子を含むtm: 変換元の日時(struct tm)
戻り値は、書き込まれた文字数(終端のヌル文字は含まない)です。
結果が収まらなかった場合は 0 を返します。
基本例
日時を固定して再現性のある出力を得る例です。
ロケールは英語表記の "C" に固定しています。
#include <stdio.h>
#include <time.h>
#include <locale.h>
int main(void) {
setlocale(LC_TIME, "C"); // 英語の月名/曜日で固定
// 2025-03-01 14:05:09 土曜日 を構築
struct tm t = {0};
t.tm_year = 2025 - 1900; // 1900年からの年数
t.tm_mon = 3 - 1; // 0起算の月
t.tm_mday = 1; // 日
t.tm_hour = 14; // 時(24時間)
t.tm_min = 5; // 分
t.tm_sec = 9; // 秒
t.tm_wday = 6; // 0=Sun, 6=Sat (任意設定。mktimeで正規化も可)
t.tm_yday = 59; // 0起算の通し日 2025は平年、3/1は60日目→0起算で59
t.tm_isdst = -1; // 夏時間情報不明なら -1
char buf[128];
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &t);
printf("ISO風 : %s\n", buf);
strftime(buf, sizeof(buf), "%A, %B %d, %Y", &t);
printf("英語での長書式: %s\n", buf);
strftime(buf, sizeof(buf), "%x %X (%a)", &t);
printf("ロケール依存 : %s\n", buf);
return 0;
}
ISO風 : 2025-03-01 14:05:09
英語での長書式: Saturday, March 01, 2025
ロケール依存 : 03/01/25 14:05:09 (Sat)
%A と %B はロケール依存で曜日・月名を文字列化します。
バッファサイズと戻り値
出力がバッファに収まらないと戻り値は0になります。
必要なサイズは strftime では直接取得できないため、十分に大きなバッファを用意するか、while で拡張する戦略をとります。
#include <stdio.h>
#include <time.h>
#include <locale.h>
#include <stdlib.h>
int main(void) {
setlocale(LC_TIME, "C");
time_t now = time(NULL);
struct tm *lt = localtime(&now);
size_t cap = 8; // わざと小さく
char *buf = malloc(cap);
const char *fmt = "%Y-%m-%d %H:%M:%S %Z";
while (1) {
size_t n = strftime(buf, cap, fmt, lt);
if (n > 0) { // 正常に収まった
printf("OK(%zu bytes): %s\n", n, buf);
break;
}
// 収まらなかった。容量を増やして再挑戦
cap *= 2;
char *nbuf = realloc(buf, cap);
if (!nbuf) { perror("realloc"); free(buf); return 1; }
buf = nbuf;
}
free(buf);
return 0;
}
OK(24 bytes): 2025-03-01 14:05:09 JST
書式が空文字列の場合、妥当な結果でも戻り値は0になります。
このため、空書式かどうかを自分で管理して判定すると安全です。
書式指定子一覧
年の書式指定子
よく使う指定子
以下は年に関する代表的な指定子です。
例は 2025-03-01 14:05:09 を英語ロケール(C)で想定したものです。
| 指定子 | 意味 | 例 | 規格 |
|---|---|---|---|
| %Y | 西暦(4桁以上、負年も可) | 2025 | C標準 |
| %y | 西暦下2桁(00-99) | 25 | C標準 |
| %C | 世紀(年/100の整数部、00-99) | 20 | POSIX拡張 |
| %G | ISO週番号に対応する年(週基準) | 2025 | POSIX拡張 |
| %g | ISO週番号に対応する年の下2桁 | 25 | POSIX拡張 |
注意点
%Y と %y は同じ年を異なる桁数で表すだけで、うるう年判定などの論理は関与しません。
%G/%g は ISO週単位の年で、年末年始で暦年とずれることがあります。移植性には注意してください。
月の書式指定子
よく使う指定子
| 指定子 | 意味 | 例 | 規格 |
|---|---|---|---|
| %m | 月の数字(01-12) | 03 | C標準 |
| %b | 月名の短縮形 | Mar | C標準 |
| %B | 月名の完全形 | March | C標準 |
| %h | %b と同じ | Mar | POSIX拡張 |
注意点
%b/%B はロケール依存です。日本語ロケールで「3月」と出るかは環境のロケール設定に依存します。
日の書式指定子
よく使う指定子
| 指定子 | 意味 | 例 | 規格 |
|---|---|---|---|
| %d | 月内通し日(01-31) | 01 | C標準 |
| %e | 月内通し日(スペース詰め、 1-31) | 1 | POSIX拡張 |
注意点
先頭ゼロの有無を調整したい時に %e が便利ですが、Windowsなど一部実装では未対応の版があります。
通し日の書式指定子
指定子と説明
| 指定子 | 意味 | 例 | 規格 |
|---|---|---|---|
| %j | 年内通し日(001-366) | 060 | C標準 |
注意点
%j はうるう年で範囲が最大366になります。
日付全体の書式指定子
よく使う指定子
| 指定子 | 意味 | 例 | 規格 |
|---|---|---|---|
| %x | ロケール依存の日付 | 03/01/25 | C標準 |
| %F | ISO 8601形式 日付(YYYY-MM-DD) | 2025-03-01 | POSIX拡張 |
| %D | mm/dd/yy | 03/01/25 | POSIX拡張 |
注意点
可搬性重視なら %F が簡潔で読みやすいですが、古い実装では未対応の可能性があります。
代替として %Y-%m-%d を使えば確実です。
書式指定子一覧
時の書式指定子
よく使う指定子
| 指定子 | 意味 | 例 | 規格 |
|---|---|---|---|
| %H | 時(00-23) | 14 | C標準 |
| %I | 時(01-12) | 02 | C標準 |
注意点
12時間表記の %I は %p と組み合わせるのが一般的です。
分の書式指定子
指定子と説明
| 指定子 | 意味 | 例 | 規格 |
|---|---|---|---|
| %M | 分(00-59) | 05 | C標準 |
秒の書式指定子
指定子と説明
| 指定子 | 意味 | 例 | 規格 |
|---|---|---|---|
| %S | 秒(00-60) | 09 | C標準 |
注意点
うるう秒のため理論上は60があり得ますが、実装やOSがうるう秒を扱わない場合もあります。
午前午後の書式指定子
指定子と説明
| 指定子 | 意味 | 例 | 規格 |
|---|---|---|---|
| %p | 午前午後の指示子(ロケール依存) | PM | C標準 |
| %P | %p の小文字形 | pm | POSIX拡張 |
注意点
一部言語では午前午後という文化がないため、%p の表現はロケールによって非対称だったり、空文字の場合もあります。
時刻全体の書式指定子
よく使う指定子
| 指定子 | 意味 | 例 | 規格 |
|---|---|---|---|
| %X | ロケール依存の時刻 | 14:05:09 | C標準 |
| %T | HH:MM:SS | 14:05:09 | POSIX拡張 |
| %R | HH:MM | 14:05 | POSIX拡張 |
| %r | 12時間書式 | 02:05:09 PM | POSIX拡張 |
注意点
移植性重視なら %H:%M[:%S] を自前で組むと確実です。
曜日・月名・その他の書式指定子
曜日の書式指定子
指定子と説明
| 指定子 | 意味 | 例 | 規格 |
|---|---|---|---|
| %a | 曜日名の短縮形 | Sat | C標準 |
| %A | 曜日名の完全形 | Saturday | C標準 |
注意点
ロケール依存。setlocale(LC_TIME, "") を設定するとOSのロケールに従います。
月名の書式指定子
指定子と説明
| 指定子 | 意味 | 例 | 規格 |
|---|---|---|---|
| %b | 月名の短縮形 | Mar | C標準 |
| %B | 月名の完全形 | March | C標準 |
| %h | %b と同じ | Mar | POSIX拡張 |
曜日番号の書式指定子
指定子と説明
| 指定子 | 意味 | 範囲 | 規格 |
|---|---|---|---|
| %w | 曜日番号(日曜=0) | 0-6 | C標準 |
| %u | 曜日番号(月曜=1) | 1-7 | POSIX拡張 |
注意点
曜日の基準が異なるため、アルゴリズムで使う際は混同に注意します。
週番号の書式指定子
指定子と説明
| 指定子 | 意味 | 規格 |
|---|---|---|
| %U | 週番号。日曜始まり。年初の最初の日曜を含む週が第1週。範囲00-53 | C標準 |
| %W | 週番号。月曜始まり。年初の最初の月曜を含む週が第1週。範囲00-53 | C標準 |
| %V | ISO 8601の週番号。月曜始まり。第1週は1月4日を含む週。範囲01-53 | POSIX拡張 |
| %G | ISO週番号に対応する年 | POSIX拡張 |
| %g | ISO週番号に対応する年の下2桁 | POSIX拡張 |
例(2025-03-01 土曜)
%U は 08、%W も 08、%V は 09、%G は 2025、%g は 25 となります。
注意点
ISO週番号は年末年始で年がまたがる場合があります。
請求週やISO準拠の週次集計には %V/%G を、汎用的な週番号には %U/%W を使うなど、用途で使い分けると良いです。
日時まとめの書式指定子
指定子と説明
| 指定子 | 意味 | 例 | 規格 |
|---|---|---|---|
| %c | ロケール依存の日時(長書式) | Sat Mar 1 14:05:09 2025 | C標準 |
注意点
出力はロケール定義に大きく依存します。ログなど機械処理が前提なら %Y-%m-%d %H:%M:%S を推奨します。
タイムゾーンの書式指定子
指定子と説明
| 指定子 | 意味 | 例 | 規格 |
|---|---|---|---|
| %Z | タイムゾーン名または略称 | JST, UTC 等 | C標準 |
| %z | UTCからの数値オフセット(+hhmm) | +0900, +0000 等 | POSIX拡張 |
動作例
#include <stdio.h>
#include <time.h>
#include <locale.h>
int main(void) {
setlocale(LC_TIME, "C");
time_t now = time(NULL);
struct tm *lt = localtime(&now);
char buf1[64], buf2[64];
strftime(buf1, sizeof(buf1), "%Y-%m-%d %H:%M:%S %Z", lt);
strftime(buf2, sizeof(buf2), "%Y-%m-%d %H:%M:%S %z", lt);
printf("Zone name : %s\n", buf1);
printf("UTC offset: %s\n", buf2);
return 0;
}
Zone name : 2025-03-01 14:05:09 JST
UTC offset: 2025-03-01 14:05:09 +0900
%Z/%z の対応は実装依存です。
特に %z はPOSIX拡張で、古いWindows Cランタイムでは未対応のことがあります。
パーセントそのもの
指定子と説明
| 指定子 | 意味 | 例 | 規格 |
|---|---|---|---|
| %% | 文字 % を出力 | % | C標準 |
ロケールで表示が変わる指定子
%a、%A、%b、%B、%c、%p、%x、%X、%Z はロケール依存です。
必要に応じて以下を使い分けます。
- ロケールに依存しない安定出力が必要な場合は、“C” ロケールを明示するか、数値指定子を組み合わせて自前でフォーマットします。
- POSIXロケール拡張では、
E/O修飾子により暦法や数字体系の代替表現を出すことがありますが、移植性は低めです。
ロケール設定の例
#include <locale.h>
// システムの既定ロケール
setlocale(LC_TIME, "");
// 英語固定
setlocale(LC_TIME, "C");
// 日本語(UTF-8)の一例。OSに該当ロケールがインストールされている必要があります。
setlocale(LC_TIME, "ja_JP.UTF-8");
実行結果の例(ロケール別)
LC_TIME="C" => Sat Mar 1 14:05:09 2025
LC_TIME="ja_JP.UTF-8" => 2025年03月01日 14時05分09秒 土曜日
実際の表示は各ロケール定義に依存します。
上記は一例です。
まとめ
strftime は「tm構造体を自在に成形する道具」です。
年なら %Y・%y、月なら %m・%B、日なら %d・%j、時刻なら %H/%M/%S、日時まとめなら %F・%T・%c のように、用途に応じて組み合わせるのが基本です。
ロケール依存の指定子は見た目の豊かさが得られる一方で、機械処理やログの安定性を重視する場合は数値指定子の組み合わせが堅実です。
また、バッファサイズ不足時は戻り値が0になる点を必ず考慮しましょう。
最後に、POSIX拡張の指定子(例: %F, %T, %z など)は移植性に差があるため、ターゲット環境の対応状況を確認してから採用するのが安心です。
