C++の基礎 - 時刻

提供: MochiuWiki : SUSE, EC, PCB

2024年9月5日 (木) 03:46時点におけるWiki (トーク | 投稿記録)による版

概要



本日の経過時刻の取得

std::chrono

本日の0時0分0秒000からの経過時刻 (mS) を求めるには、std::chrono (C++11以降) を使用する。
なお、C++17以降およびLinux環境では別の手段も存在する。

現在時刻に関する情報は、std::chrononowメソッドで取得できる。
なお、この時刻はUTC (協定世界時) であり日本時間とは異なることに注意する。

 #include <chrono>
 
 std::chrono::system_clock::time_point tp = std::chrono::system_clock::now();


まず、time_since_epocメソッドを使用して、エポックタイムからの経過時刻を取得した後、countメソッドでミリ秒を取得する。
取得した値から現在時刻のミリ秒の部分を得る。

エポックタイムは、未規定ではあるが概ね1970年1月1日から始まる。
ただし、経過時刻 (mS) は1兆を超える値になるため、32ビットのint型では格納できないことに注意する。

 // エポックタイムからの経過時刻
 std::chrono::milliseconds tp_msec = std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch());
 
 // 経過時刻をミリ秒 (整数値) に変換する
 long long all_msec = tp_msec.count();
 
 // 1秒未満の値を取り出す
 int msec = all_msec % 1000;


時間、分、秒を取得するため、time_t型に変換する。
さらに、現地時間 (日本時間) に変換する。

 time_t time = std::chrono::system_clock::to_time_t(tp);
 struct tm* t = localtime(&time);


この時、本日の経過時刻 (mS) は計算できるが、std::chrono::system_clock::time_point型は減算ができるため、本日の0時0分0秒000のタイムスタンプを求める。

 // 本日0時0分0秒000のtime_pointを求める
 std::chrono::system_clock::time_point day_tp = tp;
 
 // 0時にする
 day_tp -= std::chrono::hours(t->tm_hour);
 
 // 0分にする
 day_tp -= std::chrono::minutes(t->tm_min);
 
 // 0秒にする
 day_tp -= std::chrono::seconds(t->tm_sec);
 
 // コンマ0秒にする
 day_tp -= std::chrono::milliseconds(msec);


time_point型同士で減算した後、本日の経過時刻 (mS) を求める。

 std::chrono::milliseconds tp_msec_today = std::chrono::duration_cast<std::chrono::milliseconds>(tp - day_tp);
 int msec_today = tp_msec_today.count();


以下の例では、UTC時間、日本時間、フランス時間の3つのタイムゾーンで現在時刻と本日の経過時間を表示している。

以下の例は夏時間を考慮していないため、実務では、タイムゾーンライブラリ (例: Howard Hinnant's dateライブラリ等) の使用を検討すること。
(フランスの時差は、一般的に、UTC+1 であるが、夏時間中は UTC+2 となる)

 #include <iostream>
 #include <chrono>
 #include <ctime>
 #include <iomanip>
 
 // タイムゾーンごとの時差 (秒)
 const int JAPAN_OFFSET  = 9 * 3600;  // 日本時間
 const int FRANCE_OFFSET = 1 * 3600;  // フランス時間 (ただし、夏時間を考慮していないことに注意する)
 
 // 指定されたタイムポイントを文字列形式に変換する
 // オフセットを適用してタイムゾーンを調整する
 std::string format_time(const std::chrono::system_clock::time_point& tp, int offset = 0)
 {
    auto time = std::chrono::system_clock::to_time_t(tp) + offset;
    auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch()).count() % 1000;
    std::tm* t = std::gmtime(&time);
 
    std::ostringstream oss;
    oss << std::put_time(t, "%Y-%m-%d %H:%M:%S") << '.' << std::setfill('0') << std::setw(3) << msec;
    return oss.str();
 }
 
 // 指定されたタイムポイントの日の開始時刻 (00:00:00.000) を計算する
 // タイムゾーンのオフセットも考慮する
 std::chrono::system_clock::time_point get_day_start(const std::chrono::system_clock::time_point& tp, int offset = 0)
 {
    auto time = std::chrono::system_clock::to_time_t(tp) + offset;
    std::tm* t = std::gmtime(&time);
    return tp - std::chrono::hours(t->tm_hour) - std::chrono::minutes(t->tm_min) - std::chrono::seconds(t->tm_sec) 
              - std::chrono::milliseconds(std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch()).count() % 1000)
              - std::chrono::seconds(offset);
 }
 
 // 指定されたタイムゾーンの現在時刻と経過時間を表示する
 void print_time_info(const std::string& zone_name, const std::chrono::system_clock::time_point& tp, int offset = 0)
 {
    auto day_start = get_day_start(tp, offset);
    auto msec_today = std::chrono::duration_cast<std::chrono::milliseconds>(tp - day_start).count();
 
    std::cout << zone_name << ":" << std::endl;
    std::cout << "  現在時刻: " << format_time(tp, offset) << std::endl;
    std::cout << "  経過時間: " << msec_today << " ms" << std::endl;
    std::cout << std::endl;
 }
 
 int main()
 {
    auto now = std::chrono::system_clock::now();
 
    print_time_info("UTC", now);
    print_time_info("日本時間", now, JAPAN_OFFSET);
    print_time_info("フランス時間", now, FRANCE_OFFSET);  // フランス時間で
 
    return 0;
 }


# 出力例

UTC:
  現在時刻: 2024-09-05 05:30:45.123
  経過時間: 19845123 ms

日本時間:
  現在時刻: 2024-09-05 14:30:45.123
  経過時間: 52245123 ms

フランス時間:
  現在時刻: 2024-09-05 06:30:45.123
  経過時間: 23445123 ms


※注意
より正確な時間を取得する場合は、タイムゾーンライブラリ (例: Howard Hinnant's dateライブラリ等) の使用を推奨する。

C++20では、std::chrono::monthstd::chrono::yearが追加されるため、月初めからや年頭からの経過時刻 (mS) を取得することができる。
また、C++20では、現地時刻用のtime_point型、今日の時間情報を取得するtime_of_dayクラスも追加されるため、time_t型に変換せずに簡単に取得できる。