Chronoライブラリ最後の日

遊びで調べただけのネタ記事です。

C++20段階の標準時間ライブラリChronoが、西暦何年まで扱えるのかを調べました。

#include <iostream>
#include <chrono>

using namespace std::chrono;

int main() {
    auto tp = system_clock::time_point::max();
    // ほんとはこれでできるはず (できない悲しい)
    // std::cout << tp << std::endl;

    auto dp = floor<days>(tp);
    year_month_day date{dp}; // 日付だけでなく時間もとりたかったけど、hh_mm_ssの実装がない

    // 日付を見てみる
    // ほんとはこう書けるけど、実装がない
    // std::cout << date << std::endl;
    std::cout
        << static_cast<int>(date.year()) << '/' // yearクラスが16ビット符号付き整数で年を管理しているので溢れている
        << static_cast<unsigned int>(date.month()) << '/'
        << static_cast<unsigned int>(date.day())
        << std::endl;

    // 年だけ考えたらどうなるか
    std::cout << floor<years>(system_clock::duration::max()).count() + 1970 << std::endl;

    // time_tでローカル時間を見てみる
    std::time_t t = system_clock::to_time_t(tp);
    std::cout << std::ctime(&t) << std::endl;
}

出力:

32103/1/10
294247
Sun Jan 10 13:00:54 294247

yearクラスの問題がなければ西暦でだいたい約30万年くらいまで扱えるようです。

もしこれが64ビットの符号なし整数で、西暦1970年1月1日から秒単位で数えていたら、西暦6000億年くらいまで扱えます。いまのChronoライブラリのlibc++の実装では、マイクロ秒単位で数えています。また仕様として、符号付き整数型として時間間隔を扱っているのでこうなっています。

符号付きで半分になるので3000億年、マイクロ秒を扱うので1/100万。これでだいたい30万年になります。

結論

C++20段階のlibc++実装のChronoライブラリを使う場合は、だいたい西暦30万年くらいまでにほかの実装に移行してください。

考えられるライブラリ設計の改善点

西暦30万年を超えて生きたい場合は、ライブラリの設計を改善する必要があります。そのために考えられる設計の改善点は、以下のようなことです:

  • system_clock::now()に時間単位を指定できるようにし、秒単位でカウントする。いまは実装定義の時間単位で返されるのでlibc++はマイクロ秒を返している
  • time_pointは時間間隔のdurationと違って1970年より前の日時を扱える必要はないので、符号なしのdurationを扱えるようにする

これで西暦6000億年くらいまで、このライブラリを使えるようになります。