C++ の時間計算がつらいので改善してみた

# はじめに

C++ で時間計算をするときって `<chrono>` を使うと思うのですが、正直使いにくくないでしょうか？ C 言語で `time()` や `tm` を使ってやってきたことと同等のことを `<chrono>` を使って実現するにはどうすればいいかを調べるのに結構時間がかかってしまい学習コストが高いなぁと思いました。

C++20 ではカレンダー計算やタイムゾーンも扱えるようになって時間計算で必要になる機能は一通り揃った形になるのですが、初心者が扱うには敷居が高いと思いました。

そこで C++ での時間計算をもっとシンプルに扱えるライブラリを紹介したいと思います。

# Pendulum

Python には [Pendulum] という、とても使いやすい時間計算ライブラリがあります。使い勝手の良さに魅力を感じ、同じ機能を C++ でも使いたい！と思ったので Pendulum の C++ クローンである [Pendulum C++] というライブラリを開発しました。

**Pendulume C++:** https://github.com/rinatz/pendulum-cpp

# 基本的な使い方

日本時間の2020年4月6日の時間を扱いたい場合は次のようにします。

```cpp
#include <iostream>

#include <pendulum/pendulum.h>

int main() {
    const auto& japan = pendulum::datetime(2020, 4, 6, "Asia/Tokyo");

    std::cout << japan << std::endl;  // "2020-04-06T00:00:00+09:00"

    japan.year();           // 2020（年）
    japan.month();          // 4（月）
    japan.day();            // 6（日）
    japan.hour();           // 0（時）
    japan.minute();         // 0（分）
    japan.second();         // 0（秒）
    japan.day_of_week();    // 0（週の何日目か）
    japan.day_of_year();    // 97（年の何日目か）
    japan.week_of_month();  // 1（月の何周目か）
    japan.timestamp();      // 1586098800（UNIX 時間）
    japan.timezone_name();  // "Asia/Tokyo"（タイムゾーン名）
    japan.offset();         // 32400（UTC との時差（秒））
    japan.offset_hours();   // 9.0（UTC との時差（時））

    return 0;
}
```

日付や時間・タイムゾーンの変換も簡単に行なえます。

```cpp
// 日本時間の2021年7月23日12時はアメリカで何時？
const auto& usa = japan.on(2021, 7, 23).at(12, 0, 0).in_timezone("US/Eastern");
std::cout << usa << std::endl;  // "2021-07-22T23:00:00-04:00"
```

特定の曜日に移動することも簡単です。

```cpp
japan.next(pendulum::kSaturday);  // 2020-04-11（次の土曜日）
japan.start_of("week");           // 2020-04-06（今週の始まり（月曜日））

// 週の始まりは日曜日だろ！
pendulum::week_starts_at(pendulum::kSunday);
japan.start_of("week");           // 2020-04-05（今週の始まり（日曜日））
```

# 文字列のパース

文字列の書式を推測してパースすることができます。

```cpp
// 2020-04-06T12:00:00+09:00
pendulum::parse("2020-04-06 12:00:00", "Asia/Tokyo");

// 2020-04-06T00:00:00+09:00
pendulum::parse("20200406", "Asia/Tokyo");

// 2020-04-01T00:00:00+09:00
pendulum::parse("2020-04", "Asia/Tokyo");

// 現在時刻
pendulum::parse("now", "Asia/Tokyo");
```

# 単体テストのサポート

時間を扱うプログラムを書いているときに厄介に感じるのは現在時刻を取得するプログラムです。例えば2月14日のときだけ何か特別な処理を行うプログラムを書いたとき、そのプログラムが正しく動くことを確認するには現在時刻が2月14日になっている必要があります。

```cpp
// 今日の日付を取得
const auto& today = pendulum::today();

if (today.month() == 2 && today.day() == 14) {
    std::cout << "今日はバレンタイン！" << std::endl;
}
```

こういうプログラムをテストできるように Pendulum は現在時刻のハックができるようになっています。

```cpp
// 現在時刻が日本時間の2020年2月14日に固定される
pendulum::set_test_now(pendulum::datetime(2020, 2, 14, "Asia/Tokyo"));
pendulum::today();  // 2020-02-14

// 元に戻す
pendulum::set_test_now();
pendulum::today();  // 2020-04-06
```

特定の関数内だけ現在時刻を書き換えたいなら次のようにも書けます。

```cpp
pendulum::test(pendulum::datetime(2020, 2, 14, "Asia/Tokyo"), [&]() {
    // test() 内だけ2月14日になる
    const auto& today = pendulum::today();

    if (today.month() == 2 && today.day() == 14) {
        std::cout << "今日はバレンタイン！" << std::endl;
    }
});
```

# さいごに

学習コストが少なくて済むように設計したのでぜひ使ってみてください。

# リンク

- [Pendulum]
- [Pendulum C++]

[Pendulum]:https://pendulum.eustace.io/
[Pendulum C++]:https://github.com/rinatz/pendulum-cpp