C言語で現在時刻を扱う場合、最初に出会う関数の1つがctimeです。
とても簡単に現在時刻を文字列として取得できますが、挙動のクセや制限を理解しておかないと後で困る場面も出てきます。
本記事では、ctimeの基本的な使い方から注意点、より柔軟なフォーマット方法までを、サンプルコード付きでわかりやすく解説します。
ctimeとは
ctimeの基本概要とできること
ctime関数は、C言語標準ライブラリで提供されている日付時刻を文字列表現に変換するための関数です。
ヘッダファイル<time.h>で宣言されています。
time関数などで取得したtime_t型の値(UNIX時間などと呼ばれる、ある時刻からの経過秒数)を、人間が読める形式の文字列に変換してくれます。

ctimeでできることのイメージ
ctimeは、次のような用途に向いています。
- ログに簡易的なタイムスタンプを付ける
- デバッグ用に現在時刻を手軽に表示する
- 時刻の中身の細かい構造を意識せず、とりあえず今の日時を文字列で欲しい場合
反対に、年月日や時分秒を自由な形式で表示したい場合には不向きであり、その場合は後述するstrftimeなどを使うことになります。
time関数とctimeの関係
ctimeは単体では使わず、ほぼ必ずtime関数とセットで使う関数です。
time関数が「現在時刻を表すtime_t値」を返すctime関数が「time_tを文字列に変換」する
という関係です。

このように、time関数がデータ取得、ctimeが表示用変換を担当していると理解するとわかりやすいです。
ctimeが返す文字列の形式
ctimeが返す文字列は、形式が固定されています。
標準規格ではおおよそ次のようなフォーマットと定められています。
"Wed Jun 26 10:25:48 2025\n\0"
一般的な形式を要素ごとに見ると、次のようになります。

代表的な部分を表にまとめると次のようになります。
| 部分 | 例 | 意味 |
|---|---|---|
| 曜日(3文字) | Wed | Wed, Thu, Fri など |
| 月(3文字) | Jun | Jan, Feb, …, Dec |
| 日(2桁相当) | 26 | 1〜31(先頭に空白が入ることも) |
| 時刻 | 10:25:48 | 24時間表記 |
| 年(4桁) | 2025 | 西暦 |
| 改行文字 | \n | 行末に必ず含まれる |
| 終端ヌル文字 | \0 | C文字列終端 |
重要なポイントは、文字列の末尾に必ず改行\nが入ることです。
標準出力にそのままprintfで出すと、ちょうど1行としてきれいに表示されますが、ログファイルの行構造を自分で制御したい場合や、別の文字列と連結したい場合には邪魔になることがあります。
この改行の扱いは、後ほど詳しく解説します。
ctimeの基本的な使い方
time_tで現在時刻を取得する手順
ctimeを使う前に、まず現在時刻を表すtime_t型の値を取得する必要があります。
これはtime関数で行います。
time関数の基本
time関数は<time.h>に定義されており、宣言は次のようになっています。
time_t time(time_t *timer);
主な使い方は2通りあります。
- 引数に
NULLを渡し、戻り値だけを利用する - 引数に
time_t型変数のアドレスを渡し、戻り値と引数先の両方に値を書き込ませる
もっともシンプルな例を示します。
#include <stdio.h>
#include <time.h>
int main(void) {
// 現在時刻を表すtime_t型の値を取得
time_t now = time(NULL); // 引数にNULLを渡すのが一般的
// timeが失敗した場合は(time_t)-1が返る
if (now == (time_t)-1) {
printf("現在時刻の取得に失敗しました。\n");
return 1;
}
printf("time_tとしての現在時刻: %ld\n", (long)now);
return 0;
}
出力例(環境や実行時刻に依存します):
time_tとしての現在時刻: 1730000000
ここで表示されている数値は「ある基準時刻(通常は1970-01-01 00:00:00 UTC)からの経過秒数」です。
この値自体は人間には読みにくいので、次にctimeで文字列に変換します。
ctimeでtime_tから文字列に変換する手順
ctimeのプロトタイプは次のようになっています。
char *ctime(const time_t *timer);
引数としてtime_t型の値へのポインタを受け取り、その時刻を表す文字列へのポインタを返します。
使い方は非常にシンプルで、time関数で取得したtime_t値のアドレスを渡すだけです。
ctime呼び出しの流れ
time_t now = time(NULL);で現在時刻を取得char *s = ctime(&now);で文字列ポインタを取得printf("%s", s);で表示
この3ステップを押さえておけば、基本的な使い方としては十分です。
ctimeの基本サンプルコードと出力例
ctimeを実際に使う、もっとも典型的なサンプルコードを示します。
#include <stdio.h>
#include <time.h>
int main(void) {
// 1. 現在時刻をtime_t型で取得
time_t now = time(NULL);
if (now == (time_t)-1) {
printf("現在時刻の取得に失敗しました。\n");
return 1;
}
// 2. time_tから人間が読める形式の文字列へ変換
// ctimeの引数はtime_tへのポインタなので、&nowを渡します
char *timestr = ctime(&now);
// 3. ctimeが失敗した場合はNULLを返すのでチェックしておきます
if (timestr == NULL) {
printf("ctimeによる変換に失敗しました。\n");
return 1;
}
// 4. 変換された文字列をそのまま表示
// 文字列の末尾には改行(\n)が含まれているため、printfの書式に\nは不要です
printf("現在時刻: %s", timestr);
return 0;
}
現在時刻: Wed Jun 26 10:25:48 2025
ここでprintfの書式に改行\nを入れていないにもかかわらず、1行としてきれいに表示されています。
これは、ctimeが返した文字列の末尾にすでに改行文字が含まれているためです。
ctime利用時の注意点と制限事項
ctimeは便利な反面、いくつかの重要な注意点があります。
特に、改行を含むこと、スレッドセーフでないこと、タイムゾーンの影響を受けることは必ず押さえておきましょう。
改行文字が含まれる点と削除方法
すでに述べた通り、ctimeが返す文字列の末尾には\nが含まれています。
ログやメッセージの一部として連結する場合、この改行が余計になることがよくあります。

改行の削除方法
改行を削除したい場合、もっとも単純な方法は末尾の\nを\0に書き換えることです。
以下にサンプルコードを示します。
#include <stdio.h>
#include <string.h>
#include <time.h>
int main(void) {
time_t now = time(NULL);
if (now == (time_t)-1) {
printf("現在時刻の取得に失敗しました。\n");
return 1;
}
char *timestr = ctime(&now);
if (timestr == NULL) {
printf("ctimeによる変換に失敗しました。\n");
return 1;
}
// 文字列の長さを取得
size_t len = strlen(timestr);
if (len > 0 && timestr[len - 1] == '\n') {
// 末尾の改行を終端文字に置き換える
timestr[len - 1] = '\0';
}
printf("改行削除後の現在時刻: %s\n", timestr);
return 0;
}
改行削除後の現在時刻: Wed Jun 26 10:25:48 2025
この方法で末尾の改行だけを安全に消すことができます。
ctimeが返すポインタは書き換えてよいバッファなので、末尾1文字程度を変更することは問題ありません。
スレッドセーフでない点とリスク
ctimeはスレッドセーフではありません。
これは内部で静的なバッファを共有しているためです。

どんな問題が起こるか
ctimeはおおよそ次のような動作をします。
- ライブラリ内部の静的バッファに文字列を生成
- そのバッファへのポインタを返す
したがって、連続してctimeを呼ぶと、前回の結果が新しい結果で上書きされることになります。
さらに、マルチスレッド環境では、別スレッドのctime呼び出しが、今参照している文字列を突然書き換えてしまう危険があります。
このような理由から、マルチスレッドプログラムでは、スレッドローカルなバッファやctime_r(スレッド対応版)などを利用する必要があります。
ただし、ctime_rはPOSIX系の拡張であり、C標準の関数ではないことに注意してください。
単一スレッドでの取り扱い
単一スレッドのプログラムであっても、次の点に気をつける必要があります。
- ctimeが返すポインタを複数保存しておいても、指している中身は常に最後に呼び出した結果になる
- 必要であれば、自前でバッファを用意し、
strcpyやstrncpyでコピーしてから使う
例として、結果を自分のバッファにコピーするコードを示します。
#include <stdio.h>
#include <string.h>
#include <time.h>
int main(void) {
time_t now = time(NULL);
if (now == (time_t)-1) {
printf("現在時刻の取得に失敗しました。\n");
return 1;
}
char *timestr = ctime(&now);
if (timestr == NULL) {
printf("ctimeによる変換に失敗しました。\n");
return 1;
}
// 自前のバッファにコピー(バッファサイズは十分な大きさを確保)
char buf[64];
strncpy(buf, timestr, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0'; // 念のため終端を保証
printf("コピーした時刻文字列: %s", buf);
return 0;
}
コピーした時刻文字列: Wed Jun 26 10:25:48 2025
このように、自前のバッファにコピーしておけば、複数回のctime呼び出しによる上書きの影響を受けません。
タイムゾーンとロケールによる影響
ctimeが生成する文字列は、ローカルタイムゾーン(システムのローカル時刻)に基づいています。
また、曜日名や月名といった英字表記は、実装によってはロケール設定(地域/言語設定)の影響を受けることがあります。

タイムゾーンの影響
例えば、日本時間(JST, UTC+9)の環境と、アメリカ東部時間(EST/EDT)の環境では、同じtime_t値に対してctimeが返す文字列の時刻部分が異なります。
- JST環境の例:
Wed Jun 26 19:25:48 2025 - EST環境の例:
Wed Jun 26 05:25:48 2025
ctimeはローカル時刻で表示すると覚えておくと良いです。
ロケールの影響
多くの実装では、ctimeの出力は英語の略称(例: Mon, Tue, Jan, Feb)で固定ですが、実装や設定によってはロケールに基づき、異なる言語表現になる場合があります。
国際化対応が必要なアプリケーションであれば、ロケール設定と時刻表示の関係を意識しておく必要があります。
現在時刻のフォーマットを自由に行う方法
ctimeではフォーマットを変更できない理由
ctimeは非常に手軽な関数ですが、フォーマットを自由に変更することはできません。
決められた形式以外で出力したい場合、ctimeだけでは対応できないのです。

ctimeは「簡易表示用」、自由なフォーマットは別の仕組みで行うと割り切ることが大切です。
localtimeとtm構造体を使った時刻分解
任意のフォーマットで表示するには、一度時刻を「年」「月」「日」「時」「分」「秒」などの要素に分解する必要があります。
これを行うのがlocaltime関数とstruct tmです。
struct tmの概要
struct tmは、分解された時刻を表す構造体で、主なメンバは次の通りです。
| メンバ | 意味 | 例 |
|---|---|---|
| tm_year | 1900年からの年数 | 2025年 → 125 |
| tm_mon | 月(0〜11) | 1月 → 0 |
| tm_mday | 日(1〜31) | 26日 → 26 |
| tm_hour | 時(0〜23) | 10時 → 10 |
| tm_min | 分(0〜59) | 25分 → 25 |
| tm_sec | 秒(0〜60, うるう秒込み) | 48秒 → 48 |
| tm_wday | 曜日(0〜6, 日曜が0) | 水曜 → 3 |
| tm_yday | 年初からの日数(0〜365) | |
| tm_isdst | 夏時間なら正、そうでなければ0 |
localtimeの使い方
localtimeは、time_t値をローカルタイムに基づくstruct tm*に変換する関数です。
宣言は次の通りです。
struct tm *localtime(const time_t *timer);
ctimeと同様に内部の静的なstruct tm領域へのポインタを返すため、スレッドセーフではありませんが、ここでは基本的な使い方に絞って紹介します。
#include <stdio.h>
#include <time.h>
int main(void) {
time_t now = time(NULL);
if (now == (time_t)-1) {
printf("現在時刻の取得に失敗しました。\n");
return 1;
}
// time_t → struct tm(ローカル時間)へ変換
struct tm *lt = localtime(&now);
if (lt == NULL) {
printf("localtimeによる変換に失敗しました。\n");
return 1;
}
// struct tmの中身を直接参照して表示してみる例
printf("年: %d\n", lt->tm_year + 1900);
printf("月: %d\n", lt->tm_mon + 1);
printf("日: %d\n", lt->tm_mday);
printf("時: %d\n", lt->tm_hour);
printf("分: %d\n", lt->tm_min);
printf("秒: %d\n", lt->tm_sec);
return 0;
}
年: 2025
月: 6
日: 26
時: 10
分: 25
秒: 48
このように、localtime + struct tmで、時刻の各要素を個別に扱えるようになります。
strftimeで日付時刻フォーマットを指定する方法
時刻を好きな形式の文字列に整形するには、strftime関数を使います。
これはフォーマット文字列を指定して、struct tmの内容を文字列化する関数です。
strftimeの宣言
size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);
s: 出力先バッファmax: バッファのサイズ(バイト数)format: フォーマット文字列tm: 分解済み時刻(通常はlocaltimeやgmtimeで取得)
戻り値は生成された文字列の長さ(終端ヌル文字を除く)で、0が返った場合はバッファが足りなかったなどのエラーを意味します。
よく使うフォーマット指定子
代表的な指定子を表にまとめます。
| 指定子 | 意味 | 例 |
|---|---|---|
| %Y | 西暦4桁 | 2025 |
| %y | 西暦下2桁 | 25 |
| %m | 月(01〜12) | 06 |
| %d | 日(01〜31) | 26 |
| %H | 時(00〜23) | 10 |
| %M | 分(00〜59) | 25 |
| %S | 秒(00〜60) | 48 |
| %a | 曜日略称 | Wed |
| %A | 曜日フル | Wednesday (ロケール依存) |
| %b | 月略称 | Jun |
| %B | 月フル | June (ロケール依存) |
| %F | ISO8601形式(YYYY-MM-DD) | 2025-06-26 |
| %T | 時刻(HH:MM:SS) | 10:25:48 |
実用的なサンプルコード
localtimeとstrftimeを組み合わせて、よくあるフォーマットで表示してみます。
#include <stdio.h>
#include <time.h>
int main(void) {
time_t now = time(NULL);
if (now == (time_t)-1) {
printf("現在時刻の取得に失敗しました。\n");
return 1;
}
// time_t → struct tm(ローカル時間)
struct tm *lt = localtime(&now);
if (lt == NULL) {
printf("localtimeによる変換に失敗しました。\n");
return 1;
}
char buf[64];
// 例1: 「YYYY-MM-DD HH:MM:SS」形式
if (strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt) == 0) {
printf("strftimeに失敗しました。(バッファ不足など)\n");
return 1;
}
printf("現在時刻(1): %s\n", buf);
// 例2: 「YYYY/MM/DD(曜日) HH:MM」形式
if (strftime(buf, sizeof(buf), "%Y/%m/%d(%a) %H:%M", lt) == 0) {
printf("strftimeに失敗しました。(バッファ不足など)\n");
return 1;
}
printf("現在時刻(2): %s\n", buf);
// 例3: ISO8601に近い形式
if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", lt) == 0) {
printf("strftimeに失敗しました。(バッファ不足など)\n");
return 1;
}
printf("現在時刻(3): %s\n", buf);
return 0;
}
現在時刻(1): 2025-06-26 10:25:48
現在時刻(2): 2025/06/26(Wed) 10:25
現在時刻(3): 2025-06-26T10:25:48
このように、strftimeを使えばほぼ自由なフォーマットで日時を整形できることがわかります。
ctimeとstrftimeの使い分けと選び方
最後に、ctimeとstrftimeをどう使い分けるべきかを整理します。

ctimeを選ぶ場面
ctimeは次のような場面で有効です。
- デバッグ用にとりあえず今の時刻を知りたいだけ
- フォーマットに特にこだわりがなく、標準的な英語表記で十分
- 単一スレッド、もしくは
printf("%s", ctime(&now));程度の一時的な利用
コード量を最小限にしつつ、簡単な表示をしたいときに使うと便利です。
strftimeを選ぶ場面
一方で、次のような場合にはlocaltime + strftimeを選ぶべきです。
- ログやファイル名などに決まったフォーマットで時刻を記録したい
- ユーザーインタフェースとして読みやすい日付形式が必要
- 国や地域、文化に応じて柔軟に表示形式を変えたい
- スレッドセーフ性を考慮し、自前バッファで管理したい
本番環境のアプリケーションやライブラリのAPI設計では、ほぼ必ずstrftime側を採用すべきと言えます。
ctimeはあくまで簡易確認用という位置付けにすると安全です。
まとめ
ctimeは、time_tから現在時刻を簡単に文字列へ変換できる便利な関数です。
time関数と組み合わせることで、数行のコードで「人が読める形式の現在時刻」を出力できます。
一方で、末尾に改行が入ること、スレッドセーフでないこと、フォーマットが固定であることといった制約があります。
実運用で自由な日時フォーマットが必要な場合は、localtimeでtm構造体に分解し、strftimeで任意の形式に整形する方法が有力です。
用途に応じて、ctimeを素早い確認用、strftimeを本格的な表示用として使い分けることで、安全かつ柔軟に日時を扱えるようになります。
