C++のstd::time徹底解説:基本的な使い方から注意点まで
std::time
は、主にC言語の標準ライブラリから引き継がれた時間操作のための機能群を指します。C++11以降で導入されたstd::chrono
ライブラリの方がより高精度で柔軟な時間操作を可能にしますが、std::time
も依然として基本的な時間取得やC言語スタイルの日付/時刻フォーマットに利用されます。
主なポイントは以下の通りです。
std::time
の主な機能
-
std::time_t time(std::time_t* arg)
関数- これは
<ctime>
ヘッダで定義されている関数です。 - 現在のカレンダー時間(システム時刻)を
std::time_t
型の値として返します。 arg
がnullptr
でない場合、そのポインタが指す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(¤t_time); std::cout << "現在時刻: " << dt << std::endl; // 改行が含まれる return 0; }
- これは
-
std::time_t
型- 時間値を格納するための算術型(整数型)のエイリアスです。
- 前述の通り、多くのシステムではUNIXエポックからの秒数を格納します。
- 一部の古いシステムでは32ビット符号付き整数として実装されており、2038年問題(Year 2038 problem)に直面する可能性があります。
-
関連する他の関数 (
<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の欠点
- 精度が秒単位まで(ミリ秒、マイクロ秒などの高精度は扱えない)。
- タイムゾーンの扱いが限定的(
localtime
、gmtime
のみ)。 - 時間計算がしにくい。
std::time_t
の具体的な表現がシステム依存。- スレッドセーフでない関数がある(例:
localtime
、gmtime
は内部で静的バッファを使用するため、マルチスレッド環境では注意が必要)。
-
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 long
やstd::int64_t
) で秒数を管理し、表示時にstruct tm
への変換が必要な場合のみmktime
やgmtime
を使用するなどの工夫が必要です。ただし、これらはstd::time_t
を引数に取るため、変換時にやはりオーバーフローのリスクが残ります。
- 64ビットシステムでのコンパイル
-
原因
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
などが利用できます。
- localtime_r() / gmtime_r() の使用 (POSIX固有)
-
原因
共有リソース(静的バッファ)への同時アクセス。
日付/時刻のフォーマットの不整合
-
トラブルシューティング/解決策
- 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; }
- strftimeのドキュメント確認
-
原因
- フォーマット文字列の誤り
%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
のような固定幅の整数型に変換してから扱うことで、プラットフォーム間の互換性を確保できます。
- std::chronoの使用
-
原因
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(¤t_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_t
をstruct 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(×tamp);
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::localtime
やstd::gmtime
は、多くのシステムでスレッドセーフではありません。マルチスレッド環境で使用する場合は、localtime_r
やgmtime_r
といったスレッドセーフな代替関数(POSIX環境の場合)を使用するか、ミューテックスなどで排他制御を行う必要があります。
std::chrono
ライブラリ (C++11以降)
std::chrono
は、C++11で標準化された、よりモダンで高精度かつ型安全な時間操作のためのライブラリです。std::time
がC言語由来のシンプルな機能を提供するのに対し、std::chrono
は時間の概念をより厳密にモデル化し、様々な時間関連の操作をサポートします。
std::chrono
の主要な構成要素
-
クロック (Clocks) 特定の時点(
time_point
)を生成する時計です。std::chrono::system_clock
: システム全体のリアルタイムクロック。UNIXエポックからの経過時間を表すことが多く、std::time_t
との相互変換が可能です。std::chrono::steady_clock
: 単調増加するクロック。システム時刻の変更(NTP同期など)に影響されず、主に経過時間の計測(ベンチマークなど)に使用されます。std::chrono::high_resolution_clock
: 利用可能な中で最も高精度なクロック。通常はsystem_clock
かsteady_clock
のエイリアスです。
-
期間 (Durations) 時間の長さを表します。単位(秒、ミリ秒、マイクロ秒など)と数値(カウント)で構成されます。
- 例:
std::chrono::seconds
,std::chrono::milliseconds
,std::chrono::microseconds
,std::chrono::nanoseconds
- カスタムの期間も定義できます(例:
std::chrono::duration<double, std::ratio<60>>
で分を浮動小数点数で表現)。
- 例:
-
時点 (Time Points) 特定のクロックの基準点からの経過時間を表します。
std::chrono::time_point<Clock, Duration>
というテンプレートで表現されます。- 例:
std::chrono::system_clock::time_point
はsystem_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::format
とstd::chrono
のタイムゾーン機能はC++20で導入された比較的新しい機能であり、コンパイラのサポート状況やリンクするライブラリに注意が必要です。
-
外部ライブラリ
- Boost.Date_Time
Boostライブラリに含まれる強力な日付/時刻ライブラリです。std::chrono
が登場する以前は、C++での高機能な時間操作のデファクトスタンダードでした。現在でも多くのプロジェクトで使用されていますが、新規プロジェクトではstd::chrono
が推奨されます。
- Boost.Date_Time
-
プラットフォーム固有のAPI
- Windows
GetSystemTime()
,GetLocalTime()
,FILETIME
構造体など、Windows APIには独自の時間関連関数が多数存在します。これらはWindows環境でのみ動作し、クロスプラットフォーム性はありません。 - POSIX (Linux, macOSなど)
gettimeofday()
,clock_gettime()
(CLOCK_MONOTONIC
,CLOCK_REALTIME
など) など。これらはstd::chrono
が利用できない古いシステムや、非常に低レベルな制御が必要な場合に利用されることがあります。gettimeofday()
はマイクロ秒精度を提供しますが、単調増加を保証しません。clock_gettime()
はより柔軟なクロック選択が可能です。
- Windows
std::time
はC++の標準ライブラリの一部として残されていますが、現代のC++プログラミングにおいては、その精度、型安全性、機能の面で限界があります。
ほとんどの場合、std::chrono
ライブラリがstd::time
の優れた代替となります。
- 複雑な日付操作、カレンダー、タイムゾーン
C++20のstd::chrono
の拡張機能が非常に強力です。 - 高精度な時間計測、経過時間計算
std::chrono::steady_clock
やhigh_resolution_clock
が最適です。 - 簡単な時刻取得や基本的なUNIXタイムスタンプのやり取り
std::time
でも可能ですが、std::chrono::system_clock::now()
とto_time_t()
の組み合わせの方が推奨されます。