プログラム内で現在時刻を手早く表示したい時、ctime
は非常に便利です。
time_t
の値を、人間が読めるローカル時刻の文字列に即座に変換してくれます。
本記事では、C言語初心者の方に向けてctime
の基本、最小サンプル、表示のコツ、そして落とし穴と対処法までを丁寧に解説します。
C言語のctimeとは?time_tを日時文字列に変換
ctimeの役割と宣言
ctime
は、time_t
型の時刻(UNIX時刻、秒)を読みやすいローカル時刻の文字列へ変換する標準Cライブラリ関数です。
ヘッダは#include <time.h>
を使用します。
宣言
#include <time.h>
char *ctime(const time_t *timer);
- 引数
timer
はtime_t
へのポインタです(値そのものではありません)。 - 返り値は文字列へのポインタで、内部の静的領域を指します。書き換えや
free
はできません。 - 失敗時は
NULL
を返します。
出力の書式と例
出力は固定フォーマットで、概ね次の形になります。
書式
Www Mmm dd hh:mm:ss yyyy\n
(終端に'\0'
)
Wed Apr 16 14:23:05 2025\n
以下の表に各フィールドを示します。
フィールド | 例 | 説明 |
---|---|---|
Www | Wed | 曜日の英語3文字表記(例: Sun, Mon, Tue, …) |
Mmm | Apr | 月の英語3文字表記(例: Jan, Feb, Mar, …) |
dd | 16 | 日(2桁。1桁の日は先頭に空白が入ることがあります) |
hh:mm:ss | 14:23:05 | 時:分:秒(24時間表記) |
yyyy | 2025 | 西暦4桁 |
改行 | \n | 末尾に改行が入ります |
終端 | \0 | NUL終端(表示はされません) |
実際の文字列は合計26文字(改行と終端を含む)が一般的です。
利用シーン
手軽さが魅力のため、次のような用途でよく使われます。
ログやデバッグ出力に現在時刻を付けたい、プロトタイプで素早く時刻を確認したい、一時的なメッセージに人が読める形の時刻を表示したい、といったケースに向いています。
ただし、書式を自由にカスタマイズしたい場合はstrftime
の使用を検討してください。
ctimeの使い方
time_tの取得
現在のUNIX時刻はtime
で取得します。
ポインタを渡すかNULL
を渡す2通りがあります。
例: 代表的な2つの書き方
#include <time.h>
// 1) 返り値で受ける
time_t now = time(NULL); // 失敗時は (time_t)-1
// 2) 変数に格納してもらう
time_t now2;
if (time(&now2) == (time_t)-1) {
// エラー処理
}
どちらの場合も失敗時には(time_t)-1
であるかを確認します。
ctimeの呼び出しと返り値
必ず&
を付けてtime_t
のアドレスを渡す点に注意します。
返り値は静的領域を指すchar *
で、NULL
なら失敗です。
time_t now = time(NULL);
char *s = ctime(&now); // &を付ける
if (s == NULL) {
// 失敗時の処理
}
最小サンプル
コメントを多めに入れた、もっとも基本的なサンプルです。
#include <stdio.h>
#include <time.h>
int main(void) {
// 現在のUNIX時刻(秒)を取得
time_t now = time(NULL);
if (now == (time_t)-1) {
// 取得失敗はまれですが、念のためチェックします
perror("time failed");
return 1;
}
// time_t を読みやすい文字列へ変換
char *s = ctime(&now); // &を忘れない
if (s == NULL) {
fprintf(stderr, "ctime failed\n");
return 1;
}
// ctimeが返す文字列には既に改行(\n)が含まれます
// printf("%s\n", s); とすると空行が1つ余分に出るので注意
printf("%s", s);
return 0;
}
Wed Apr 16 14:23:05 2025
表示のコツ
ctimeの文字列には末尾の改行が含まれるため、printf("%s", s);
のように余計な\n
を付けないのがコツです。
文中に埋め込みたい場合や、後ろに他の文字を続けたい場合は、改行を取り除いてから使うと体裁が整います(方法は後述します)。
ctimeの注意点と落とし穴
ローカル時刻になる
ctimeは常にローカル時刻に基づく文字列を返します。
UTCが必要な場合はgmtime
や別手段を検討します(本記事では詳細説明は割愛します)。
末尾に改行\nが付く
末尾の改行が最もよくある落とし穴です。
二重の改行や、文字列連結時の体裁崩れを招きます。
改行を消す方法
簡潔に消すにはstrcspn
やstrchr
が便利です。
#include <stdio.h>
#include <string.h>
#include <time.h>
int main(void) {
time_t now = time(NULL);
if (now == (time_t)-1) return 1;
char *s = ctime(&now);
if (!s) return 1;
// 改行位置を探して終端に置き換える
s[strcspn(s, "\n")] = '#include <stdio.h>
#include <string.h>
#include <time.h>
int main(void) {
time_t now = time(NULL);
if (now == (time_t)-1) return 1;
char *s = ctime(&now);
if (!s) return 1;
// 改行位置を探して終端に置き換える
s[strcspn(s, "\n")] = '\0'; // 改行がなければ末尾の '\0' をそのまま上書きするだけ
printf("現在時刻: [%s]\n", s); // 角括弧内に収まる
return 0;
}
'; // 改行がなければ末尾の '#include <stdio.h>
#include <string.h>
#include <time.h>
int main(void) {
time_t now = time(NULL);
if (now == (time_t)-1) return 1;
char *s = ctime(&now);
if (!s) return 1;
// 改行位置を探して終端に置き換える
s[strcspn(s, "\n")] = '\0'; // 改行がなければ末尾の '\0' をそのまま上書きするだけ
printf("現在時刻: [%s]\n", s); // 角括弧内に収まる
return 0;
}
' をそのまま上書きするだけ
printf("現在時刻: [%s]\n", s); // 角括弧内に収まる
return 0;
}
現在時刻: [Wed Apr 16 14:23:05 2025]
返す文字列は静的領域で書き換え不可
ctimeの返す文字列は静的領域で、書き換えたりfree
したりしてはいけません。
必要に応じて自前のバッファへコピーしてから加工します。
正しいコピー例
#include <stdio.h>
#include <string.h>
#include <time.h>
int main(void) {
time_t now = time(NULL);
char *s = ctime(&now);
if (!s) return 1;
char buf[26]; // ctimeの出力は通常26バイト(終端込み)
// 安全にコピー(超過時は切り詰め)
snprintf(buf, sizeof(buf), "%s", s);
// bufは自分の配列なので書き換え可能
buf[strcspn(buf, "\n")] = '#include <stdio.h>
#include <string.h>
#include <time.h>
int main(void) {
time_t now = time(NULL);
char *s = ctime(&now);
if (!s) return 1;
char buf[26]; // ctimeの出力は通常26バイト(終端込み)
// 安全にコピー(超過時は切り詰め)
snprintf(buf, sizeof(buf), "%s", s);
// bufは自分の配列なので書き換え可能
buf[strcspn(buf, "\n")] = '\0'; // 改行を落とす
printf("コピー後: %s\n", buf);
return 0;
}
'; // 改行を落とす
printf("コピー後: %s\n", buf);
return 0;
}
複数回の呼び出しで結果が上書きされる
ctimeは毎回同じ静的バッファを使うため、二度目以降の呼び出しで前の結果が上書きされます。
次の例で問題を体感できます。
#include <stdio.h>
#include <time.h>
int main(void) {
time_t t1 = time(NULL);
time_t t2 = t1 + 86400; // 1日後と仮定
char *s1 = ctime(&t1);
char *s2 = ctime(&t2); // ここで静的バッファが上書き
// s1もs2も同じ内容(最後に呼んだほうの文字列)が表示される可能性
printf("s1: %s", s1);
printf("s2: %s", s2);
return 0;
}
対策は自前の配列にすぐコピーして保持することです。
#include <stdio.h>
#include <string.h>
#include <time.h>
int main(void) {
time_t t1 = time(NULL);
time_t t2 = t1 + 86400;
char b1[26], b2[26];
snprintf(b1, sizeof(b1), "%s", ctime(&t1)); // ただちにコピー
snprintf(b2, sizeof(b2), "%s", ctime(&t2)); // ただちにコピー
printf("b1: %s", b1);
printf("b2: %s", b2);
return 0;
}
スレッド非対応
ctimeはスレッドセーフではありません。
複数スレッドから同時に呼ぶと結果が干渉します。
標準Cだけではスレッド安全な代替は提供されていません。
実務では以下を検討します。
- POSIX環境:
ctime_r
(非標準)やlocaltime_r
+strftime
- Windows/MSVC:
ctime_s
など(実装依存) - どうしても
ctime
を使うなら、排他制御で呼び出しを直列化
失敗時はNULLをチェック
ctimeは失敗時にNULL
を返します。
またtime
も失敗時は(time_t)-1
です。
どちらもチェックしておくと堅牢です。
#include <stdio.h>
#include <time.h>
int main(void) {
time_t now = time(NULL);
if (now == (time_t)-1) {
perror("time failed");
return 1;
}
char *s = ctime(&now);
if (s == NULL) {
fprintf(stderr, "ctime failed\n");
return 1;
}
printf("%s", s);
return 0;
}
タイムゾーン(TZ)の影響
ctimeの結果はローカルタイムゾーンの設定に依存します。
多くの環境では環境変数TZ
の影響を受けますが、設定方法や反映の仕組みは実装依存です。
POSIXではsetenv("TZ", ...)
とtzset()
、Windowsでは_putenv
と_tzset
などが使われます。
移植性が重要なら、タイムゾーンに敏感な表示をctime
に任せない設計(例えばUTCで扱い、必要に応じてstrftime
で明示的に整形)が安全です。
よくある間違いと対処
&を付け忘れる
ctime(now);
正: ctime(&now);
ctime
はtime_t *
を受け取ります。
<time.h>のインクルード忘れ
#include <time.h>
を忘れると宣言が見つからず未定義動作を招く可能性があります。
必ずヘッダをインクルードしてください。
改行を消さずに文字列連結して体裁が崩れる
printf("Now: %s - end\n", ctime(&now));
途中の\n
で折り返してしまいます。
対処はstrcspn
やstrchr
で改行を削除してから連結します。
char *s = ctime(&now);
s[strcspn(s, "\n")] = 'char *s = ctime(&now);
s[strcspn(s, "\n")] = '\0';
printf("Now: %s - end\n", s);
';
printf("Now: %s - end\n", s);
返された文字列をfreeしてしまう誤り
返り値は静的領域なのでfree(s)
してはいけません。
必要なら自前の配列へコピーします。
返された文字列を書き換えてしまう誤り
直接s[0] = '\0';
のように書き換えるのは未定義動作です。
コピーしてから編集してください。
まとめ
ctimeはtime_t
からローカル時刻の読みやすい文字列を一瞬で得られる、手軽で強力な関数です。
一方で、末尾の改行が付く、静的領域で書き換え不可、複数回や並行呼び出しで上書きされる、スレッド非対応、タイムゾーンの影響を受けるといった注意点があります。
初心者の方は、次の3点をまず押さえると安全です。
- アドレスを渡す(
&now
) - 改行を必要に応じて落とす
- 必要なら自前バッファへコピーして保持する。
用途が広がってきたら、strftime
などでの書式指定も学ぶと実務の幅が一気に広がります。