メモの日々


2023年08月23日(水) [長年日記]

[c++] std::chronoメモ

std::chronoについてメモ。C++20で色々機能が追加されており、それを前提とする。

durationは時間

durationは時間を表すクラステンプレート。テンプレートパラメータとして値の型と、値の1が何秒を表すかの比率を持つ。

テンプレートをインスタンス化したクラスとして seconds、milliseconds、years などの色々な型が用意されている。なお、yearsは31556952秒、monthsは2629746秒という「平均的な長さ」が単位になっていることに注意。

sなどのリテラル演算子も用意されており、これらを使ってオブジェクトを作ることもできる。

durationクラス間の変換は、精度が落ちなければコンストラクタや代入で行え、精度が落ちる場合はduration_cast()floor()などを使い分けて行う。

#include <chrono>
#include <iostream>

int main()
{
    using namespace std::literals::chrono_literals;
    namespace ch = std::chrono;

    const ch::duration<int> d1{ -100 };
    const ch::milliseconds d2 = d1;
    const auto d3 = ch::duration_cast<ch::minutes>(d1);
    const auto d4 = ch::floor<ch::minutes>(d1);
    const auto d5 = d4 + 1s;
    const ch::months d6{ 1 };
    const ch::years d7{ 1 };

    std::cout << "d1 = " << d1 << std::endl;
    std::cout << "d2 = " << d2 << std::endl;
    std::cout << "d3 = " << d3 << std::endl;
    std::cout << "d4 = " << d4 << std::endl;
    std::cout << "d5 = " << d5 << std::endl;
    std::cout << "d6 = " << d6 << std::endl;
    std::cout << "d7 = " << d7 << std::endl;
}
d1 = -100s
d2 = -100000ms
d3 = -1min
d4 = -2min
d5 = -119s
d6 = 1[2629746]s
d7 = 1[31556952]s

time_pointは時刻

time_pointは時刻を表すクラステンプレート。テンプレートパラメータとして「時計」型と、時刻の刻み幅とするdurationを持つ。

時計型としてsystem_clocklocal_tなどが用意されており、それらを使ってtime_pointテンプレートをインスタンス化したクラスとしてsys_secondslocal_daysなどが用意されている。

時刻は起点となる時刻(エポック)からの経過時間として表現されている。エポックは時計型から決まる。

特定の時刻を生成するには、/ 演算子を使ってyear_month_dayオブジェクトを作り、それをsys_daysに変換するという方法が使える。

time_pointクラス間の変換もdurationと同様で、精度が落ちなければコンストラクタや代入で行え、精度が落ちる場合はtime_point_cast()floor()などを使い分けて行う。

#include <iostream>

int main()
{
    using namespace std::literals::chrono_literals;
    namespace ch = std::chrono;

    const ch::time_point<ch::system_clock> t1;   // system_clockのエポック
    const ch::sys_seconds t2{ ch::months{ 1 } }; // エポックから約一か月後
    const auto t3 = ch::sys_days{ 2023y/8/15 } + 12h + 34min + 56s;
    const ch::time_point<ch::system_clock, ch::milliseconds> t4 = t3;
    const auto t5 = ch::floor<ch::days>(t3);

    std::cout << "t1 = " << t1 << std::endl;
    std::cout << "t2 = " << t2 << std::endl;
    std::cout << "t3 = " << t3 << std::endl;
    std::cout << "t4 = " << t4 << std::endl;
    std::cout << "t5 = " << t5 << std::endl;
}
t1 = 1970-01-01 00:00:00.0000000
t2 = 1970-01-31 10:29:06
t3 = 2023-08-15 12:34:56
t4 = 2023-08-15 12:34:56.000
t5 = 2023-08-15

時計とタイムゾーン

時計は現在時刻を取得するときに使うクラス。いくつか用意されているが、通常はsystem_clockを使い、経過時間を計る際にsteady_clockを使うくらいか。

system_clock::now()が返す時刻はUTC相当のものであることに注意。システムのタイムゾーンでの時刻を取得するには、current_zone()time_zoneオブジェクトを取得し、そのメンバ関数to_local()を呼び出す方法が使える。

また、時刻をタイムゾーン付きで管理したい場合はzoned_timeクラステンプレートを使う。zoned_timeは時刻を表すがtime_point型ではなく、time_pointとtime_zoneのペアである。

#include <chrono>
#include <iostream>

int main()
{
	namespace ch = std::chrono;

	const auto now1 = ch::system_clock::now();
	const auto now2 = ch::current_zone()->to_local(now1);
	const ch::zoned_time now3{ ch::current_zone(), now1 };

	std::cout << "now1 = " << now1 << std::endl;
	std::cout << "now2 = " << now2 << std::endl;
	std::cout << "now3 = " << now3 << std::endl;
}
now1 = 2023-08-23 10:16:29.2074391
now2 = 2023-08-23 19:16:29.2074391
now3 = 2023-08-23 19:16:29.2074391 JST