C++のstd::time徹底解説:基本的な使い方から注意点まで

2025-06-01

std::timeは、主にC言語の標準ライブラリから引き継がれた時間操作のための機能群を指します。C++11以降で導入されたstd::chronoライブラリの方がより高精度で柔軟な時間操作を可能にしますが、std::timeも依然として基本的な時間取得やC言語スタイルの日付/時刻フォーマットに利用されます。

主なポイントは以下の通りです。

std::timeの主な機能

  1. std::time_t time(std::time_t* arg) 関数

    • これは<ctime>ヘッダで定義されている関数です。
    • 現在のカレンダー時間(システム時刻)をstd::time_t型の値として返します。
    • argnullptrでない場合、そのポインタが指すstd::time_tオブジェクトにも同じ値を格納します。
    • 返されるstd::time_tの値は、通常、UNIXエポック(1970年1月1日00:00:00 UTC)からの秒数を表す整数型です。ただし、このエンコーディングは標準では未指定とされており、システムによって異なる可能性があります(ほとんどのシステムではUNIXタイムに従います)。
    • エラーが発生した場合は(std::time_t)(-1)を返します。
    #include <iostream>
    #include <ctime> // time関数を使うために必要
    
    int main() {
        std::time_t current_time;
        current_time = std::time(nullptr); // 現在時刻を取得
    
        std::cout << "UNIXエポックからの秒数: " << current_time << std::endl;
    
        // 人間が読める形式に変換
        char* dt = std::ctime(&current_time);
        std::cout << "現在時刻: " << dt << std::endl; // 改行が含まれる
    
        return 0;
    }
    
  2. std::time_t

    • 時間値を格納するための算術型(整数型)のエイリアスです。
    • 前述の通り、多くのシステムではUNIXエポックからの秒数を格納します。
    • 一部の古いシステムでは32ビット符号付き整数として実装されており、2038年問題(Year 2038 problem)に直面する可能性があります。
  3. 関連する他の関数 (<ctime> ヘッダ) std::time単独では日付や時刻を人間が読める形式に変換することはできません。そのため、通常は以下の関数と組み合わせて使用されます。

    • struct tm* localtime(const std::time_t* timer): std::time_tの値をローカルタイム(実行環境のタイムゾーン)の日付と時刻の構造体struct tmに変換します。
    • struct tm* gmtime(const std::time_t* timer): std::time_tの値をUTC(協定世界時)の日付と時刻の構造体struct tmに変換します。
    • char* asctime(const struct tm* timeptr): struct tm構造体の内容を、固定された形式の文字列(例: "Wed Sep 21 10:27:52 2011\n")に変換します。
    • char* ctime(const std::time_t* timer): std::time_tの値を直接、asctimeと同じ形式の文字列に変換します(内部でlocaltimeを呼び出します)。
    • std::size_t strftime(char* str, std::size_t count, const char* format, const struct tm* timeptr): struct tm構造体の内容を、指定されたフォーマット文字列に従って整形し、文字列strに書き込みます。日付や時刻を柔軟な形式で出力したい場合に非常に便利です。

    struct tmのメンバー (例)

    • tm_sec: 秒 (0-60, 60は閏秒)
    • tm_min: 分 (0-59)
    • tm_hour: 時 (0-23)
    • tm_mday: 月の日 (1-31)
    • tm_mon: 1月からの月数 (0-11)
    • tm_year: 1900年からの年数
    • tm_wday: 日曜日からの日数 (0-6)
    • tm_yday: 1月1日からの日数 (0-365)
    • tm_isdst: 夏時間フラグ (-1, 0, 1)

C++11以降では、よりモダンで強力な時間ライブラリとしてstd::chronoが導入されました。

  • std::chronoの利点

    • 高精度
      ナノ秒レベルまでの時間を扱える。
    • 型安全性
      時間の単位(秒、ミリ秒、ナノ秒など)が型として表現され、誤った単位での計算を防ぐ。
    • 柔軟な時間表現
      duration(期間)、time_point(特定時点)、clock(時計)という概念で時間を表現。
    • 時間計算
      期間の加算・減算、異なる期間単位間の変換などが容易。
    • C++20ではタイムゾーン機能も追加され、より包括的な時間操作が可能になった。
  • std::timeの欠点

    • 精度が秒単位まで(ミリ秒、マイクロ秒などの高精度は扱えない)。
    • タイムゾーンの扱いが限定的(localtimegmtimeのみ)。
    • 時間計算がしにくい。
    • std::time_tの具体的な表現がシステム依存。
    • スレッドセーフでない関数がある(例: localtimegmtimeは内部で静的バッファを使用するため、マルチスレッド環境では注意が必要)。
  • std::timeの利点

    • シンプルで使いやすい。
    • C言語との互換性が高い。
    • 基本的な秒単位の時刻取得には十分。


2038年問題 (Year 2038 Problem)

  • トラブルシューティング/解決策

    • 64ビットシステムでのコンパイル
      現代のほとんどの64ビットシステムでは、std::time_tはデフォルトで64ビット整数として実装されており、2038年問題は解消されています。したがって、可能な限り64ビットシステムでコンパイルし、実行することを推奨します。
    • std::chronoの使用
      C++11以降で導入されたstd::chronoライブラリは、この問題を根本的に解決します。std::chrono::system_clock::time_pointは、通常、64ビット以上の時間表現を使用し、より高い精度と柔軟性を提供します。新しいコードでは、std::timeではなくstd::chronoを使用することを強く推奨します。
    • 明示的な64ビット整数の使用
      もし古いシステムで32ビットtime_tしか利用できない場合でも、独自に64ビット整数型 (long longstd::int64_t) で秒数を管理し、表示時にstruct tmへの変換が必要な場合のみmktimegmtimeを使用するなどの工夫が必要です。ただし、これらはstd::time_tを引数に取るため、変換時にやはりオーバーフローのリスクが残ります。
  • 原因
    std::time_tのサイズが32ビットに制限されているため。

スレッドセーフティの問題

  • トラブルシューティング/解決策

    • localtime_r() / gmtime_r() の使用 (POSIX固有)
      POSIXシステム(Linux, macOSなど)では、スレッドセーフなバージョンとしてlocaltime_r()gmtime_r()が提供されています。これらの関数は、struct tm構造体を呼び出し元が用意し、そのポインタを渡すことで、静的バッファの使用を回避します。
    • ミューテックスなどによる排他制御
      スレッドセーフなバージョンが利用できないプラットフォームでは、std::mutexなどを用いて、これらの関数呼び出しを排他制御する必要があります。
    • std::chronoとC++20のタイムゾーン機能
      C++20では、std::chronoにタイムゾーン関連の機能が追加され、より安全かつモダンな方法で日付/時刻とタイムゾーンを扱うことができます。例えば、std::chrono::current_zone()std::chrono::zoned_timeなどが利用できます。
  • 原因
    共有リソース(静的バッファ)への同時アクセス。

日付/時刻のフォーマットの不整合

  • トラブルシューティング/解決策

    • strftimeのドキュメント確認
      使用しているシステムやC++のバージョンに対応するstrftimeのフォーマット指定子を正確に確認します。
    • struct tmの正しい初期化
      mktimeなどでstruct tmを初期化する場合、その関数のセマンティクスを理解し、正しく値を設定します。手動で設定する場合は、各メンバーの基準(例: tm_yearは1900年からのオフセット)を厳密に守ります。
    • バッファサイズの確認
      strftimeの戻り値を確認し、必要なバイト数がバッファに書き込まれたか検証します。また、十分なサイズのバッファを確保するようにします。
    #include <iostream>
    #include <ctime>
    #include <vector> // より安全なバッファのために
    
    int main() {
        std::time_t rawtime;
        std::time(&rawtime);
        struct tm *timeinfo;
    
        timeinfo = std::localtime(&rawtime); // ローカルタイムを取得
    
        if (timeinfo == nullptr) {
            std::cerr << "localtime() failed." << std::endl;
            return 1;
        }
    
        // tm_yearは1900年からの年数
        // tm_monは0-11 (0=January)
        std::cout << "tm_year: " << timeinfo->tm_year << std::endl; // 例えば 2024年なら 124
        std::cout << "tm_mon: " << timeinfo->tm_mon << std::endl;   // 例えば 5月なら 4
        std::cout << "tm_mday: " << timeinfo->tm_mday << std::endl;
    
        // strftimeのバッファサイズ不足を防ぐ
        // 十分なサイズを見積もるか、std::vector<char>で動的に確保
        std::vector<char> buffer(256); // 十分なサイズを確保
        size_t bytes_written = std::strftime(buffer.data(), buffer.size(), "%Y-%m-%d %H:%M:%S", timeinfo);
    
        if (bytes_written == 0) {
            std::cerr << "strftime() failed or buffer too small." << std::endl;
            return 1;
        }
    
        std::cout << "Formatted time: " << buffer.data() << std::endl;
    
        return 0;
    }
    
  • 原因

    • フォーマット文字列の誤り
      %Y(4桁年)、%m(月)、%d(日)などの指定ミス。
    • struct tmメンバーの不適切な設定
      tm_yearは1900年からの年数、tm_monは0から始まる月(0が1月、11が12月)であるなど、各メンバーが期待する値の範囲や基準を誤解している場合。
    • バッファオーバーフロー
      strftimeの出力バッファが小さすぎて、結果の文字列が入りきらない場合。

time_tのプラットフォーム依存性

  • トラブルシューティング/解決策

    • std::chronoの使用
      再度強調しますが、std::chrono::system_clock::time_pointは、基になる表現が標準によってより明確に定義されており、移植性が高いです。
    • 明示的な固定幅整数型 (std::int64_tなど) の使用
      時間の絶対値をやり取りする際には、std::time_tを直接シリアライズ/デシリアライズするのではなく、std::int64_tのような固定幅の整数型に変換してから扱うことで、プラットフォーム間の互換性を確保できます。
  • 原因
    time_tが実装定義の型であるため。

  • トラブルシューティング/解決策

    • std::time_t time(std::time_t* arg)は、エラー時に(std::time_t)(-1)を返す場合があります(ただし、これは一般的なエラーケースではありません)。nullptrを渡した場合も、返された値が有効かどうか確認することが重要です。
    #include <iostream>
    #include <ctime>
    
    int main() {
        std::time_t current_time = std::time(nullptr);
        if (current_time == (std::time_t)(-1)) {
            std::cerr << "Failed to get current time." << std::endl;
            return 1;
        }
        std::cout << "Current time: " << current_time << std::endl;
        return 0;
    }
    
  • 原因
    エラーハンドリングの不足。

std::timeは基本的な時間操作には使えますが、その設計がC言語由来であるため、現代のC++で要求される高精度、スレッドセーフティ、そして2038年問題への対応といった点では限界があります。



例1: 現在のUNIXタイムスタンプを取得する

std::time関数の最も基本的な使い方です。UNIXエポック(1970年1月1日00:00:00 UTC)からの秒数を取得します。

#include <iostream>
#include <ctime> // std::time, std::time_t を使うために必要

int main() {
    std::time_t current_time;

    // 現在のUNIXタイムスタンプを取得
    // nullptr を渡すと、戻り値として時刻が得られる
    current_time = std::time(nullptr);

    // エラーチェック (通常、time関数は失敗しないが念のため)
    if (current_time == (std::time_t)(-1)) {
        std::cerr << "エラー: 現在時刻の取得に失敗しました。" << std::endl;
        return 1;
    }

    std::cout << "現在のUNIXタイムスタンプ: " << current_time << " 秒" << std::endl;

    // 現在時刻を人間が読める形式に変換 (ローカルタイムゾーン)
    // std::ctime は内部で localtime を呼び出し、固定形式の文字列を返す
    char* dt = std::ctime(&current_time);
    if (dt == nullptr) {
        std::cerr << "エラー: 時刻の文字列変換に失敗しました。" << std::endl;
        return 1;
    }
    std::cout << "現在のローカル時刻 (ctime): " << dt; // ctimeの戻り値には改行が含まれる

    return 0;
}

出力例

現在のUNIXタイムスタンプ: 1717161029 秒
現在のローカル時刻 (ctime): Fri May 31 08:50:29 2024

(実行するたびにタイムスタンプと時刻は変化します)

例2: struct tm を使って日付と時刻を詳細に取得・表示する

std::time_tstruct tmに変換し、年、月、日などの各要素にアクセスします。

#include <iostream>
#include <ctime> // std::time, std::time_t, struct tm, localtime, gmtime, mktime を使うために必要

int main() {
    std::time_t rawtime;
    std::time(&rawtime); // 現在のUNIXタイムスタンプを取得

    // ----------- ローカルタイムゾーンでの時刻情報 -----------
    struct tm *local_time_info;
    // localtime はスレッドセーフではない可能性があるので注意
    local_time_info = std::localtime(&rawtime);

    if (local_time_info == nullptr) {
        std::cerr << "エラー: ローカル時刻の変換に失敗しました。" << std::endl;
        return 1;
    }

    std::cout << "--- ローカルタイムゾーンでの時刻情報 ---" << std::endl;
    // struct tm の各メンバーは特定の基準値を持つ
    std::cout << "  年 (1900年からのオフセット): " << local_time_info->tm_year << " -> " << (local_time_info->tm_year + 1900) << std::endl;
    std::cout << "  月 (0=1月, 11=12月):      " << local_time_info->tm_mon << " -> " << (local_time_info->tm_mon + 1) << "月" << std::endl;
    std::cout << "  日:                   " << local_time_info->tm_mday << "日" << std::endl;
    std::cout << "  時 (0-23):             " << local_time_info->tm_hour << "時" << std::endl;
    std::cout << "  分 (0-59):             " << local_time_info->tm_min << "分" << std::endl;
    std::cout << "  秒 (0-60):             " << local_time_info->tm_sec << "秒" << std::endl;
    std::cout << "  曜日 (0=日, 6=土):      " << local_time_info->tm_wday << std::endl;
    std::cout << "  年内通算日 (0=1月1日):   " << local_time_info->tm_yday << std::endl;
    std::cout << "  夏時間フラグ (-1=不明, 0=なし, 1=あり): " << local_time_info->tm_isdst << std::endl;
    std::cout << std::endl;

    // ----------- UTC (協定世界時) での時刻情報 -----------
    struct tm *gm_time_info;
    // gmtime もスレッドセーフではない可能性があるので注意
    gm_time_info = std::gmtime(&rawtime);

    if (gm_time_info == nullptr) {
        std::cerr << "エラー: UTC時刻の変換に失敗しました。" << std::endl;
        return 1;
    }

    std::cout << "--- UTC (協定世界時) での時刻情報 ---" << std::endl;
    std::cout << "  年: " << (gm_time_info->tm_year + 1900) << std::endl;
    std::cout << "  月: " << (gm_time_info->tm_mon + 1) << std::endl;
    std::cout << "  日: " << gm_time_info->tm_mday << std::endl;
    std::cout << "  時: " << gm_time_info->tm_hour << std::endl;
    std::cout << "  分: " << gm_time_info->tm_min << std::endl;
    std::cout << "  秒: " << gm_time_info->tm_sec << std::endl;
    std::cout << std::endl;

    return 0;
}

出力例 (日本時間で実行した場合)

--- ローカルタイムゾーンでの時刻情報 ---
  年 (1900年からのオフセット): 124 -> 2024
  月 (0=1月, 11=12月):      4 -> 5月
  日:                   31日
  時 (0-23):             8時
  分 (0-59):             50分
  秒 (0-60):             29秒
  曜日 (0=日, 6=土):      5
  年内通算日 (0=1月1日):   151
  夏時間フラグ (-1=不明, 0=なし, 1=あり): 0

--- UTC (協定世界時) での時刻情報 ---
  年: 2024
  月: 5
  日: 30
  時: 23
  分: 50
  秒: 29

(ローカルタイムゾーンがUTC+9の場合、UTCの時刻は9時間前になります。日付も変わる可能性があります。)

例3: strftime を使って日付と時刻を任意の形式でフォーマットする

strftimeは、struct tmの情報を基に、指定されたフォーマット文字列に従って日付/時刻文字列を生成する強力な関数です。

#include <iostream>
#include <ctime>   // std::time, localtime, strftime, struct tm を使うために必要
#include <vector>  // バッファを動的に確保するために使用

int main() {
    std::time_t rawtime;
    std::time(&rawtime);
    struct tm *timeinfo;

    timeinfo = std::localtime(&rawtime); // ローカルタイムゾーンでstruct tmを取得

    if (timeinfo == nullptr) {
        std::cerr << "エラー: ローカル時刻の変換に失敗しました。" << std::endl;
        return 1;
    }

    // 十分なサイズのバッファを確保
    // 例: "YYYY-MM-DD HH:MM:SS" は約20文字 + null終端
    // strftimeの出力は最大でバッファサイズ-1まで書き込まれる
    std::vector<char> buffer(80); // 十分なサイズとして80文字を確保

    // フォーマット文字列を指定
    // %Y: 4桁の年 (例: 2024)
    // %m: 2桁の月 (01-12)
    // %d: 2桁の日 (01-31)
    // %H: 24時間表記の時 (00-23)
    // %M: 2桁の分 (00-59)
    // %S: 2桁の秒 (00-59)
    const char* format1 = "%Y-%m-%d %H:%M:%S";
    size_t bytes_written1 = std::strftime(buffer.data(), buffer.size(), format1, timeinfo);

    if (bytes_written1 == 0) {
        std::cerr << "エラー: strftimeによるフォーマット1に失敗しました (バッファが小さい可能性)。" << std::endl;
    } else {
        std::cout << "フォーマット1: " << buffer.data() << std::endl;
    }

    // 別のフォーマットの例: 曜日、月名、日、年
    // %a: 曜日の略称 (例: Fri)
    // %b: 月の略称 (例: May)
    // %e: 日 (先頭のスペースあり、例: " 1" "10")
    const char* format2 = "%a, %b %e, %Y %I:%M:%S %p"; // %I: 12時間表記の時, %p: AM/PM
    size_t bytes_written2 = std::strftime(buffer.data(), buffer.size(), format2, timeinfo);

    if (bytes_written2 == 0) {
        std::cerr << "エラー: strftimeによるフォーマット2に失敗しました (バッファが小さい可能性)。" << std::endl;
    } else {
        std::cout << "フォーマット2: " << buffer.data() << std::endl;
    }
    
    // ロケールに合わせた日付と時刻の表現
    const char* format3 = "%c"; // ロケールに合わせた日付と時刻の表現
    size_t bytes_written3 = std::strftime(buffer.data(), buffer.size(), format3, timeinfo);

    if (bytes_written3 == 0) {
        std::cerr << "エラー: strftimeによるフォーマット3に失敗しました (バッファが小さい可能性)。" << std::endl;
    } else {
        std::cout << "フォーマット3 (%c): " << buffer.data() << std::endl;
    }


    return 0;
}

出力例 (日本時間で実行した場合)

フォーマット1: 2024-05-31 08:50:29
フォーマット2: Fri, May 31, 2024 08:50:29 AM
フォーマット3 (%c): 2024年 5月31日 金曜日 08時50分29秒

例4: 特定の日付を std::time_t に変換する (mktime)

struct tmの値を設定し、それをstd::time_tに変換することで、特定の日付のタイムスタンプを得ることができます。

#include <iostream>
#include <ctime>

int main() {
    struct tm target_time = {0}; // 全メンバーを0で初期化

    // ターゲットの日付と時刻を設定
    // tm_year: 1900年からの年数 (例: 2025年 -> 125)
    // tm_mon: 0-11 (例: 1月 -> 0, 6月 -> 5)
    // tm_mday: 1-31
    // tm_hour: 0-23
    // tm_min: 0-59
    // tm_sec: 0-59
    target_time.tm_year = 2025 - 1900; // 2025年
    target_time.tm_mon = 6 - 1;       // 6月 (0-indexed)
    target_time.tm_mday = 15;         // 15日
    target_time.tm_hour = 10;         // 午前10時
    target_time.tm_min = 30;          // 30分
    target_time.tm_sec = 0;           // 0秒
    target_time.tm_isdst = -1;        // 夏時間を自動で判断 (-1)

    // mktime はローカルタイムゾーンで struct tm を time_t に変換
    // 成功すると time_t を返し、失敗すると (time_t)-1 を返す
    // また、mktime は tm_wday や tm_yday などを正規化する
    std::time_t timestamp = std::mktime(&target_time);

    if (timestamp == (std::time_t)(-1)) {
        std::cerr << "エラー: 指定された日付のタイムスタンプ変換に失敗しました。" << std::endl;
        return 1;
    }

    std::cout << "2025年6月15日10時30分0秒のUNIXタイムスタンプ: " << timestamp << std::endl;

    // 変換されたタイムスタンプを再度 ctime で確認
    char* dt = std::ctime(&timestamp);
    if (dt == nullptr) {
        std::cerr << "エラー: 時刻の文字列変換に失敗しました。" << std::endl;
        return 1;
    }
    std::cout << "対応するローカル時刻: " << dt;

    return 0;
}
2025年6月15日10時30分0秒のUNIXタイムスタンプ: 1718485800
対応するローカル時刻: Sun Jun 15 10:30:00 2025
  • 2038年問題
    std::time_tが32ビット整数として実装されている古いシステムでは、2038年以降の時刻を正しく扱えない可能性があります。現代の64ビットシステムでは通常この問題は解消されていますが、移植性を考慮する場合はstd::chronoの使用が推奨されます。
  • 精度
    std::timeが扱うのは秒単位の精度までです。ミリ秒、マイクロ秒、ナノ秒といった高精度な時間計測が必要な場合は、C++11以降で導入されたstd::chronoライブラリを使用してください。
  • スレッドセーフティ
    std::localtimestd::gmtimeは、多くのシステムでスレッドセーフではありません。マルチスレッド環境で使用する場合は、localtime_rgmtime_rといったスレッドセーフな代替関数(POSIX環境の場合)を使用するか、ミューテックスなどで排他制御を行う必要があります。


std::chrono ライブラリ (C++11以降)

std::chronoは、C++11で標準化された、よりモダンで高精度かつ型安全な時間操作のためのライブラリです。std::timeがC言語由来のシンプルな機能を提供するのに対し、std::chronoは時間の概念をより厳密にモデル化し、様々な時間関連の操作をサポートします。

std::chronoの主要な構成要素

  1. クロック (Clocks) 特定の時点(time_point)を生成する時計です。

    • std::chrono::system_clock: システム全体のリアルタイムクロック。UNIXエポックからの経過時間を表すことが多く、std::time_tとの相互変換が可能です。
    • std::chrono::steady_clock: 単調増加するクロック。システム時刻の変更(NTP同期など)に影響されず、主に経過時間の計測(ベンチマークなど)に使用されます。
    • std::chrono::high_resolution_clock: 利用可能な中で最も高精度なクロック。通常はsystem_clocksteady_clockのエイリアスです。
  2. 期間 (Durations) 時間の長さを表します。単位(秒、ミリ秒、マイクロ秒など)と数値(カウント)で構成されます。

    • 例: std::chrono::seconds, std::chrono::milliseconds, std::chrono::microseconds, std::chrono::nanoseconds
    • カスタムの期間も定義できます(例: std::chrono::duration<double, std::ratio<60>> で分を浮動小数点数で表現)。
  3. 時点 (Time Points) 特定のクロックの基準点からの経過時間を表します。

    • std::chrono::time_point<Clock, Duration>というテンプレートで表現されます。
    • 例: std::chrono::system_clock::time_pointsystem_clockの基準点からの経過時間を示します。

std::chronoの利点

  • C++20での拡張
    タイムゾーン、カレンダー、日付フォーマットなどの機能が追加され、より包括的な日付/時刻ライブラリとなりました。
  • 2038年問題の解消
    time_pointの内部表現は、通常64ビット以上の整数を使用するため、この問題に直面しません。
  • 時間計算
    期間の加算・減算、時点間の期間の計算などが直感的です。
  • 柔軟な時間表現
    様々な時間単位を簡単に変換・操作できます。
  • 型安全性
    時間の単位が型として表現されるため、異なる単位を誤って計算することを防ぎます(例: 秒とミリ秒を直接加算するとコンパイルエラー)。
  • 高精度
    ナノ秒レベルまでの時間を標準で扱えます。

std::chrono を使ったプログラミング例

例1: 現在時刻の取得と高精度な経過時間計測

#include <iostream>
#include <chrono> // std::chrono を使うために必要
#include <thread> // std::this_thread::sleep_for のために必要

int main() {
    // 1. system_clock で現在時刻を取得 (UNIXエポックからの時間)
    auto now = std::chrono::system_clock::now();
    std::cout << "現在の時刻 (time_point): " << std::endl;

    // time_point を time_t に変換して ctime で表示 (従来のstd::timeとの連携)
    std::time_t now_c = std::chrono::system_clock::to_time_t(now);
    std::cout << "  std::time_t 形式: " << std::ctime(&now_c); // ctimeは改行を含む

    // 2. steady_clock で経過時間を計測
    auto start = std::chrono::steady_clock::now();

    // 何らかの処理 (例: 100ミリ秒待機)
    std::this_thread::sleep_for(std::chrono::milliseconds(100));

    auto end = std::chrono::steady_clock::now();

    // 経過時間を計算
    auto elapsed_duration = end - start;

    // 経過時間を異なる単位で表示
    std::cout << "\n経過時間:" << std::endl;
    std::cout << "  ミリ秒: " << std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_duration).count() << " ms" << std::endl;
    std::cout << "  マイクロ秒: " << std::chrono::duration_cast<std::chrono::microseconds>(elapsed_duration).count() << " µs" << std::endl;
    std::cout << "  ナノ秒: " << std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed_duration).count() << " ns" << std::endl;

    // 秒単位の浮動小数点数での表示
    std::chrono::duration<double> sec_duration = elapsed_duration;
    std::cout << "  秒 (double): " << sec_duration.count() << " s" << std::endl;

    return 0;
}

出力例

現在の時刻 (time_point): 
  std::time_t 形式: Fri May 31 08:51:45 2025

経過時間:
  ミリ秒: 100 ms
  マイクロ秒: 100000 µs
  ナノ秒: 100000000 ns
  秒 (double): 0.1 s

例2: 特定の期間の作成と操作

#include <iostream>
#include <chrono>

int main() {
    // 期間の作成
    std::chrono::seconds s(10);        // 10秒
    std::chrono::milliseconds ms(500); // 500ミリ秒

    // 期間の加算・減算
    auto total_duration = s + ms;
    std::cout << "10秒 + 500ミリ秒 = " << std::chrono::duration_cast<std::chrono::milliseconds>(total_duration).count() << " ms" << std::endl;

    // 異なる単位への変換
    std::chrono::minutes m = std::chrono::duration_cast<std::chrono::minutes>(total_duration);
    std::cout << "10秒 + 500ミリ秒 = " << m.count() << " 分" << std::endl; // 切り捨てられる

    // 浮動小数点数での期間表現
    std::chrono::duration<double, std::ratio<60>> one_and_half_minutes(1.5); // 1.5分
    std::cout << "1.5分は " << std::chrono::duration_cast<std::chrono::seconds>(one_and_half_minutes).count() << " 秒" << std::endl;

    return 0;
}

出力例

10秒 + 500ミリ秒 = 10500 ms
10秒 + 500ミリ秒 = 0 分
1.5分は 90 秒

C++20では、std::chronoが大幅に拡張され、日付、カレンダー、タイムゾーンの概念が導入されました。これにより、より複雑な日付/時刻の操作が標準ライブラリで可能になりました。

  • std::format (または std::chrono::format): 日付/時刻のフォーマット。
  • std::chrono::zoned_time: タイムゾーン情報を持つ時刻。
  • std::chrono::weekday: 曜日。
  • std::chrono::year_month_day: 年、月、日のカレンダー日付。

例3: C++20 での現在日付と時刻の表示 (概念的なコード)

// C++20 以降で利用可能
#include <iostream>
#include <chrono>
#include <format> // C++20 の標準ライブラリでのフォーマット機能

int main() {
    // 現在のシステム時刻 (UTC)
    auto now_sys_utc = std::chrono::system_clock::now();

    // 現在のローカルタイムゾーンを取得
    // C++20: std::chrono::current_zone()
    auto local_zone = std::chrono::current_zone();

    // タイムゾーン付きの時刻を作成
    std::chrono::zoned_time zt = {local_zone, now_sys_utc};

    // C++20 の std::format を使って整形出力
    std::cout << "現在のローカル時刻 (C++20 format): " << std::format("{:%Y-%m-%d %H:%M:%S %Z}", zt) << std::endl;
    std::cout << "今日の曜日: " << std::format("{:%A}", zt) << std::endl; // %A はロケール依存のフル曜日名

    // 特定の年月日を作成
    std::chrono::year_month_day date_2024_jul_4(std::chrono::year(2024), std::chrono::month(7), std::chrono::day(4));
    std::cout << "特定の年月日: " << std::format("{:%Y年%m月%d日}", date_2024_jul_4) << std::endl;

    return 0;
}

注意
std::formatstd::chronoのタイムゾーン機能はC++20で導入された比較的新しい機能であり、コンパイラのサポート状況やリンクするライブラリに注意が必要です。

  • 外部ライブラリ

    • Boost.Date_Time
      Boostライブラリに含まれる強力な日付/時刻ライブラリです。std::chronoが登場する以前は、C++での高機能な時間操作のデファクトスタンダードでした。現在でも多くのプロジェクトで使用されていますが、新規プロジェクトではstd::chronoが推奨されます。
  • プラットフォーム固有のAPI

    • Windows
      GetSystemTime(), GetLocalTime(), FILETIME構造体など、Windows APIには独自の時間関連関数が多数存在します。これらはWindows環境でのみ動作し、クロスプラットフォーム性はありません。
    • POSIX (Linux, macOSなど)
      gettimeofday(), clock_gettime() (CLOCK_MONOTONIC, CLOCK_REALTIMEなど) など。これらはstd::chronoが利用できない古いシステムや、非常に低レベルな制御が必要な場合に利用されることがあります。gettimeofday()はマイクロ秒精度を提供しますが、単調増加を保証しません。clock_gettime()はより柔軟なクロック選択が可能です。

std::timeはC++の標準ライブラリの一部として残されていますが、現代のC++プログラミングにおいては、その精度、型安全性、機能の面で限界があります。

ほとんどの場合、std::chronoライブラリstd::timeの優れた代替となります。

  • 複雑な日付操作、カレンダー、タイムゾーン
    C++20のstd::chronoの拡張機能が非常に強力です。
  • 高精度な時間計測、経過時間計算
    std::chrono::steady_clockhigh_resolution_clockが最適です。
  • 簡単な時刻取得や基本的なUNIXタイムスタンプのやり取り
    std::timeでも可能ですが、std::chrono::system_clock::now()to_time_t()の組み合わせの方が推奨されます。