導入
C++で日付や時刻を扱う際、標準ライブラリの std::tm は避けて通れない存在です。しかし、その設計の古さからくる「癖」を理解していないと、バグや予期せぬ挙動の原因となります。本記事では、std::tm の基礎知識から、実務で安全に扱うためのポイントを解説します。
基礎知識
std::tm は、日時を年月日時分秒といった個別の整数型メンバとして保持する構造体です。ヘッダーファイル
・tm_year: 1900年からの経過年数(例: 2024年の場合、124が入る)
・tm_mon: 0から始まる月(0 = 1月、11 = 12月)
この「1900年基準」と「0始まりの月」は、変換ミスが非常に発生しやすいため注意が必要です。
実装/解決策
std::tm を利用する際は、必ず std::localtime(またはスレッドセーフな std::localtime_s / localtime_r)を使用します。注意点として、std::localtime は静的領域を指すポインタを返すため、マルチスレッド環境では競合が発生します。環境に応じて、より安全な関数を選択することが重要です。
サンプルプログラム
以下のコードは、現在時刻を取得し、安全にフォーマットして出力する実用的な例です。
include
include
include
int main() {
// 現在の時刻(time_t型)を取得
std::time_t now = std::time(nullptr);
// std::tm構造体に変換(環境によってlocaltime_sなどを使用してください)
std::tm time_info;
localtime_s(&time_info, &now); // Windows環境の例
// tm_yearは1900年が基準なので+1900、tm_monは0始まりなので+1する
int year = time_info.tm_year + 1900;
int month = time_info.tm_mon + 1;
int day = time_info.tm_mday;
// フォーマット出力
std::cout << "現在の日付: "
<< year << "年"
<< std::setw(2) << std::setfill('0') << month << "月"
<< std::setw(2) << std::setfill('0') << day << "日"
<< std::endl;
return 0;
}
応用・注意点
実務で陥りやすい罠として、std::tm の値を直接計算して加算する手法があります。例えば、1日進めるために tm_mday に 1 を足すと、月末の桁上がりが正しく処理されません。日付の演算を行う場合は、std::mktime 関数に一度変換して正規化(normalize)させるか、可能であれば C++20 で導入された

コメント