閉じる

【C言語】asctimeの使い方と注意点 基礎からわかるstruct tmの文字列化

日時を人が読める形に整える最短経路の1つがasctimeです。

用意したstruct tmを一行の文字列に変換でき、学習段階ではとても便利です。

ただし末尾の改行静的バッファなど見落としやすい点があります。

この記事では基礎から丁寧に解説し、正しく使うための実例と注意点を示します。

C言語のasctimeとは

何をする関数か

asctimeは、struct tm型の日時情報を人間が読みやすい固定形式の文字列に変換する関数です。

返り値は内部の静的バッファ(後述)を指すchar *で、標準出力にそのまま表示できます。

主な用途は次の通りです。

プログラムのログ出力やデバッグ時に、struct tmの中身を手早く確認するのに向いています。

一方で、書式の自由度は低く、国際化や厳密な体裁が必要な用途ではstrftimeの使用が推奨されます。

出力される文字列の形式

asctimeが返す文字列は概ね次の形です。

Www Mmm dd hh:mm:ss yyyy\n\0

例として「Thu Oct 16 14:32:00 2025」などの形になります。

最後に改行\nが必ず付き、さらに終端のヌル文字\0が続きます。

歴史的には合計26文字(終端含む)という前提が広く知られていますが、文字数や表記の細部に依存したコードは移植性を損ねます

以下は出力構成の目安です。

要素備考
曜日省略名Thu3文字。多くの環境で英語の略称。
月省略名Oct3文字。多くの環境で英語の略称。
161桁日は先頭に空白になる実装が多いです。
時刻14:32:0024時間表記。
2025西暦4桁が一般的。
末尾改行+終端最後に\n\0

ロケールにより略称が変化する実装もあります(後述)。

必要なヘッダとプロトタイプ

asctime<time.h>に宣言されています。

C言語
#include <time.h>

/* 標準Cのプロトタイプ */
char *asctime(const struct tm *timeptr);

この関数は静的バッファへのポインタを返します。

解放は不要で、解放してはいけません

asctimeの使い方

最小サンプルコード

もっとも簡単な使い方は、現在時刻からstruct tmを得てasctimeに渡す方法です。

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

int main(void) {
    time_t now = time(NULL);                 // 現在のUNIX時刻(秒)
    struct tm *lt = localtime(&now);         // ローカル時刻のstruct tmを取得
    // 注: localtimeやasctimeは静的領域を使うためスレッドセーフではありません(後述)

    char *s = asctime(lt);                   // 読みやすい文字列に変換
    // asctimeの文字列末尾には改行が入っています。printfで'\n'を足さないのがコツです。
    printf("現在時刻: %s", s);

    return 0;
}
実行結果
現在時刻: Thu Oct 16 14:32:05 2025

末尾の改行はasctimeが付けるため、printf("%s\n", s)のように重ねて改行すると、空行が1つ増えてしまいます。

struct tmの準備ポイント

asctimeに渡すstruct tmは、各フィールドを妥当な範囲に整える必要があります。

自前で値を詰めるときはmktimeで正規化してtm_wdaytm_ydayを設定しましょう。

メンバ意味範囲と注意
tm_sec0〜60(うるう秒により60が現れる実装あり)
tm_min0〜59
tm_hour0〜23
tm_mday1〜31
tm_mon0〜11(0が1月)
tm_year1900年からの年数(例: 2025年→125)
tm_wday曜日0〜6(0が日曜)。mktime等で算出させるのが安全
tm_yday年内通算日0〜365
tm_isdst夏時間正なら適用、0で非適用、負で不明(自動判定)

以下は日付を手動で作り、正規化してからasctimeで表示する例です。

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

int main(void) {
    struct tm tmv = {0};  // すべて0で初期化するのが安全
    tmv.tm_year = 2025 - 1900;  // 2025年
    tmv.tm_mon  = 9;            // 10月(0が1月)
    tmv.tm_mday = 16;           // 16日
    tmv.tm_hour = 14;           // 14時
    tmv.tm_min  = 32;           // 32分
    tmv.tm_sec  = 0;            // 0秒
    tmv.tm_isdst = -1;          // 夏時間は不明(システムに判定させる)

    // mktimeで正規化し、tm_wdayやtm_ydayを埋めてもらう
    if (mktime(&tmv) == (time_t)-1) {
        // 表現できない日時だった場合の簡易エラーハンドリング
        fprintf(stderr, "mktimeに失敗しました\n");
        return 1;
    }

    char *s = asctime(&tmv);
    printf("%s", s);            // 改行はasctimeが付加済み
    return 0;
}
実行結果
Thu Oct 16 14:32:00 2025

tm_wdayなどを自前で計算せず、mktimeに任せると、曜日名の出力ミスを避けられます。

戻り値の扱い方

asctime静的バッファへのポインタを返します

そのため次の点に注意します。

  • freeしてはいけません。所有権は呼び出し側にありません。
  • 次のasctime呼び出しで上書きされます。複数の結果を保持したい場合は、自分のバッファにstrncpysnprintfでコピーします。
  • 書き換えが必要なら必ずコピー側で行います。返ってきた静的領域の直接変更は推奨されません。
  • 実装によっては、表現不可な日時でNULLを返すことがあります。実用上は稀ですが、返り値のNULLチェックは入れておくと堅牢です。

コピーの例です。

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

int main(void) {
    time_t now = time(NULL);
    struct tm *lt = localtime(&now);

    char *p = asctime(lt);
    if (!p) {
        fprintf(stderr, "asctimeがNULLを返しました\n");
        return 1;
    }

    char buf[32];                           // asctimeの出力は通常26文字程度
    strncpy(buf, p, sizeof(buf) - 1);       // 安全にコピー
    buf[sizeof(buf) - 1] = '
#include <stdio.h>
#include <string.h>
#include <time.h>
int main(void) {
time_t now = time(NULL);
struct tm *lt = localtime(&now);
char *p = asctime(lt);
if (!p) {
fprintf(stderr, "asctimeがNULLを返しました\n");
return 1;
}
char buf[32];                           // asctimeの出力は通常26文字程度
strncpy(buf, p, sizeof(buf) - 1);       // 安全にコピー
buf[sizeof(buf) - 1] = '\0';            // 念のため終端
// 末尾の改行を取り除く(必要な場合)
size_t len = strlen(buf);
if (len > 0 && buf[len - 1] == '\n') {
buf[len - 1] = '\0';
}
printf("コピー後: %s\n", buf);
return 0;
}
'; // 念のため終端 // 末尾の改行を取り除く(必要な場合) size_t len = strlen(buf); if (len > 0 && buf[len - 1] == '\n') { buf[len - 1] = '
#include <stdio.h>
#include <string.h>
#include <time.h>
int main(void) {
time_t now = time(NULL);
struct tm *lt = localtime(&now);
char *p = asctime(lt);
if (!p) {
fprintf(stderr, "asctimeがNULLを返しました\n");
return 1;
}
char buf[32];                           // asctimeの出力は通常26文字程度
strncpy(buf, p, sizeof(buf) - 1);       // 安全にコピー
buf[sizeof(buf) - 1] = '\0';            // 念のため終端
// 末尾の改行を取り除く(必要な場合)
size_t len = strlen(buf);
if (len > 0 && buf[len - 1] == '\n') {
buf[len - 1] = '\0';
}
printf("コピー後: %s\n", buf);
return 0;
}
'; } printf("コピー後: %s\n", buf); return 0; }
実行結果
コピー後: Thu Oct 16 14:32:05 2025

asctimeの注意点

末尾に改行が入る

asctimeの文字列は末尾に\nを含みます

そのためprintf("%s\n", asctime(...))とすると改行が2つ続きます。

避けるには次のいずれかを使います。

  • printf("%s", asctime(...))とし、改行を追加しない。
  • いったんコピーし、末尾の\nを取り除く。
  • strftimeで改行なしの文字列を最初から作る(推奨)。

strftimeを使う例を後述します。

戻り値は静的バッファ

戻り値は静的バッファなので、複数回呼び出すと内容が上書きされます。

下の例では最初の結果を自分のバッファにコピーしないと、二度目の呼び出しで上書きされてしまいます。

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

int main(void) {
    time_t now = time(NULL);
    struct tm *lt = localtime(&now);

    char s1[32], s2[32];

    // 1回目の結果を直ちにコピーして保存
    snprintf(s1, sizeof(s1), "%s", asctime(lt));

    // 2回目: UTCに変換して別の文字列を取得
    struct tm *gt = gmtime(&now);
    snprintf(s2, sizeof(s2), "%s", asctime(gt));

    printf("Local : %s", s1);  // 末尾に改行を含む
    printf("UTC   : %s", s2);
    return 0;
}
実行結果
Local : Thu Oct 16 14:32:05 2025
UTC   : Thu Oct 16 05:32:05 2025

コピーしてから別のasctimeを呼ぶ、という順番が安全です。

スレッドセーフではない

asctimeはスレッドセーフではありません

内部の静的バッファを共有するため、複数スレッドから同時に呼び出すとデータ競合になります。

対策としては次のいずれかを選びます。

  • strftimeで呼び出し側バッファに書式化する(標準Cで最もポータブル)。
  • POSIX環境ならasctime_rを使って呼び出し側バッファへ出力する。
  • 一部処理系の拡張asctime_s(Annex K)を使う。ただし移植性は高くありません。

strftimeasctimeに近い書式を再現する例です。

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

int main(void) {
    time_t now = time(NULL);
    struct tm tmv = *localtime(&now);

    char buf[64];
    // asctime相当の形式(末尾改行なし)を狙う書式
    // %a: 曜日略名, %b: 月略名, %e: 日(先頭空白埋め), %H:%M:%S: 時刻, %Y: 西暦
    if (strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %Y", &tmv) == 0) {
        fprintf(stderr, "strftimeに失敗しました\n");
        return 1;
    }

    printf("%s\n", buf);  // 改行はここで追加
    return 0;
}
実行結果
Thu Oct 16 14:32:05 2025

この方法はスレッドセーフで、末尾の改行有無も自在です。

ロケール依存と表記

曜日や月の略称はロケールの影響を受ける実装があります

例えばLC_TIMEを日本語ロケールに設定すると、%a%bの結果が変わる場合があります。

asctime自体は書式固定で制御しづらく、国際化対応や一貫した表記が必要ならstrftimeを使い、必要に応じてsetlocaleLC_TIMEを設定してください。

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

int main(void) {
    time_t now = time(NULL);
    struct tm tmv = *localtime(&now);

    // ロケールをC(英語)に固定
    setlocale(LC_TIME, "C");

    char buf_c[64];
    strftime(buf_c, sizeof(buf_c), "%a %b %e %H:%M:%S %Y", &tmv);
    printf("Cロケール: %s\n", buf_c);

    // ロケールを(環境に存在すれば)日本語に変更
    // 環境により"ja_JP.UTF-8"が存在しない場合があります
    if (setlocale(LC_TIME, "ja_JP.UTF-8")) {
        char buf_ja[64];
        strftime(buf_ja, sizeof(buf_ja), "%a %b %e %H:%M:%S %Y", &tmv);
        printf("日本語ロケール: %s\n", buf_ja);
    } else {
        printf("日本語ロケールはこの環境で利用できません\n");
    }

    return 0;
}
実行結果
Cロケール: Thu Oct 16 14:32:05 2025
日本語ロケール: 木 10 16 14:32:05 2025

asctimeはロケールを細かく制御できず、表記の一貫性を保ちにくいため、国際化が必要な場面ではstrftimeが適しています。

書式を変えたいときはstrftime

asctimeは書式を変えられません

たとえば「YYYY-MM-DD HH:MM:SS」のような形式や、末尾の改行を付けたくない場合、strftimeを用います。

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

int main(void) {
    time_t now = time(NULL);
    struct tm tmv = *localtime(&now);

    char iso[32];
    if (strftime(iso, sizeof(iso), "%Y-%m-%d %H:%M:%S", &tmv) == 0) {
        fprintf(stderr, "strftimeに失敗しました\n");
        return 1;
    }

    printf("ISO風: %s\n", iso);
    return 0;
}
実行結果
ISO風: 2025-10-16 14:32:05

自在な書式、末尾改行の有無、スレッドセーフ性を同時に満たすのがstrftimeの利点です。

まとめ

asctimestruct tmを手早く文字列化するのに便利ですが、末尾の改行静的バッファスレッドセーフでないといった落とし穴があります。

実務では結果を自分のバッファにコピーする、またはstrftimeで直接フォーマットするのが安全です。

さらにstruct tmを自前で組み立てる際はmktimeで正規化し、曜日などの整合性を保つのがポイントです。

まずはasctimeで挙動を掴み、必要に応じてstrftimeへ移行する、という段階的な学習をおすすめします。

C言語 標準ライブラリの活用

この記事を書いた人
エーテリア編集部
エーテリア編集部

プログラミングの基礎をしっかり学びたい方向けに、C言語の基本文法から解説しています。ポインタやメモリ管理も少しずつ理解できるよう工夫しています。

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

URLをコピーしました!