はじめに
C++ での時刻計算では、struct tm
を使った方法がよく紹介されています。ただし、struct tmにはクセがあり、1900年始まりだったり、0月始まりだったりとプログラマが注意を払わなければならない部分があります。
そこで、struct tmをラッピングし、時刻計算を簡単に扱えるライブラリを作成しました。
名前をEasy Datetime
とし、下記のシンプルな機能を提供します。
- 機能一覧
- 日付計算
- 時刻差の計算
- 文字列 ↔︎ 時刻への相互パース
- 現在時刻の取得
Eazy-Datetime-Cpp ← ライブラリへのリンクはこちらです!
目次
インストール
-
こちらのGitを開く
-
include
フォルダをあなたのC++プロジェクトに追加します。 - 以下のサンプルのように
#include datetime.h
を宣言すればOKです。 - ヘッダオンリーで使用できます。
- ※ 下記のように
try-catch
句で囲んでおくと、エラーメッセージの確認ができて便利です。
- ※ 下記のように
-
sample.cpp
/* 必須 */
#include "datetime.h"
/* 関連するヘッダ */
#include <iostream>
#include <string>
#include <vector>
/* 使用する名前空間 */
// using namespace EZ;
int main()
{
try {
// ここにサンプルを記述
}
catch (EZ::DatetimeException e) {
// 例外時のエラーメッセージを表示
std::cout << e.what() << std::endl;
}
return 0;
}
EZ::Datetime(日付クラス)
現在時刻の取得
- static関数の
now()
を呼び出すと取得できます。返り値はEZ::Datetime
型です。- now(true)とすると、UTC基準の時刻が取得できます。
sample.cpp
// 現在時刻の取得
auto now = EZ::Datetime::now();
std::cout << "Now is " << now << std::endl;
/* 出力 */
// >> Now is 2021/04/25 22:02:00 JST
時刻の設定
- 時刻の設定は、コンストラクタで行います。渡し方は次の通りです。
- 1. タイムスタンプのみを引数に渡す(※ ただし、フォーマットは"%Y/%m/%d %H:%M:%S"として解釈されます。)
- 2. タイムスタンプとフォーマットを引数に渡す
- 3. 年, 月, 日, 時, 分, 秒 の順に整数値を渡す
sample.cpp
// 時刻の設定
auto date1 = EZ::Datetime("2021/1/1 0:00:00" /*, false*/);
auto date2 = EZ::Datetime("2021/1/1 0:00:00", "%Y/%m/%d %H:%M:%S" /*, false*/);
auto date3 = EZ::Datetime(2022,1,1,0,0,0 /*, false*/);
// 時刻の表示
std::cout << "date1 is " << date1 << std::endl;
std::cout << "date2 is " << date2 << std::endl;
std::cout << "date3 is " << date3.str("西暦%Y年%m月%d日 %H時%M分%S秒 TZ=%Z") << std::endl;
/* 出力 */
// >> date1 is 2021/01/01 00:00:00 JST
// >> date2 is 2021/01/01 00:00:00 JST
// >> date3 is 西暦2022年01月01日 00時00分00秒 TZ=JST
- コンストラクタの末尾の引数を true にすると、タイムゾーンがUTCとなります。
- falseにすると、タイムゾーンは現地時間になります。
- デフォルトは false です。
フォーマット指定子
- Datetimeクラスでは下記の入出力指定子をサポートしています。
- 入力フォーマットは
コンストラクタ
で指定可能 - 出力フォーマットは関数
str()
で指定可能
- 入力フォーマットは
指定子 | 機能 | 入力フォーマット | 出力フォーマット |
---|---|---|---|
%Y | 西暦を4桁で指定 | ○ 対応 | ○ 対応 |
%y | 西暦を下2桁で指定 | × 非対応 | ○ 対応 |
%m | 月を指定 | ○ 対応(入力桁数:1〜2桁) | ○ 対応(2桁で出力) |
%d | 日を指定 | ○ 対応(入力桁数:1〜2桁) | ○ 対応(2桁で出力) |
%H | 時を指定 | ○ 対応(入力桁数:1〜2桁) | ○ 対応(2桁で出力) |
%M | 分を指定 | ○ 対応(入力桁数:1〜2桁) | ○ 対応(2桁で出力) |
%S | 秒を指定 | ○ 対応(入力桁数:1〜2桁) | ○ 対応(2桁で出力) |
%Z | タイムゾーンを指定 | × 非対応 (引数isUTCで設定) | ○ 対応 |
Datetimeクラスの値の取得
sample.cpp
int sec() const // 日時のうち"秒"のみを返却
int minute() const // 日時のうち"分"のみを返却
int hour() const // 日時のうち"時"のみを返却
int day() const // 日時のうち"日"のみを返却
int month() const // 日時のうち"月"のみを返却
long year() const // 日時のうち"西暦年"のみを返却
int daysOfWeek() const // 日曜始まりの曜日番号を返却
// 時刻を {年, 月, 日, 時, 分, 秒}の6つの要素を持つvectorで返却する
std::vector<long long> toVector() const
時刻どうしの引き算
-
EZ::Datetime
オブジェクトどうしの時刻差を計算することができます。- 返り値は、
EZ::TimeDelta
型として返却されます。
- 返り値は、
sample.cpp
// 時刻差の計算
auto delta = date2 - date1;
std::cout << date2 << " - " << date1 << "\n\t = " << delta << std::endl;
/* 出力 */
// >> 2022/01/01 00:00:00 JST - 2021/01/01 00:00:00 JST
// >> = TimeDelta(days=365, hours=0, minutes=0, seconds=0)
EZ::TimeDelta(時刻差クラス)
TimeDelta(時刻差クラス)の設定
- 時刻差の設定はコンストラクタで行います。引数の渡し方は下記の通りです。
- 1. 時刻差を秒数で渡す
- 2. 時刻差を (日, 時, 分, 秒) として渡す
sample.cpp
auto delta0 = EZ::TimeDelta(-365 * 24 * 3600);
auto delta1 = EZ::TimeDelta(365, 0, 0, 0);
std::cout << "delta0 = " << delta0 << std::endl;
std::cout << "delta1 = " << delta1 << std::endl;
/* 出力 */
// >> delta0 = TimeDelta(days=-365, hours=0, minutes=0, seconds=0)
// >> delta1 = TimeDelta(days=365, hours=0, minutes=0, seconds=0)
TimeDelta(時刻差クラス)の値の取得
- totalSeconds()のようにトータル時間で取得する方法と、toVector()のように、(日, 時, 分, 秒)の組み合わせで取得する方法があります。
sample.cpp
long long totalSeconds() const // 時刻差をトータル秒数で返す
long long totalMinutes() const // 時刻差をトータル分数で返す(小数切り捨て)
long long totalHours() const // 時刻差をトータル時間数で返す(小数切り捨て)
long long totalDays() const // 時刻差をトータル日数で返す(小数切り捨て)
long long totalWeeks() const // 時刻差をトータル週数で返す(小数切り捨て)
// 時刻差を {日, 時, 分, 秒}の4つの要素を持つvectorで返却する
std::vector<long long> toVector() const
TimeDelta(時刻差クラス)の四則演算
- 和,差 : 2つのTimeDeltaオブジェクトどうしの演算ができます。
-
積,商 : 左にTimeDeltaオブジェクト, 右にdoubleのオペランドでの演算ができます。
- なお、TimeDeltaオブジェクトどうしの除算も可能です。その際の返り値はdoubleです。
sample.cpp
// 和・差
std::cout << "delta0 + delta1 = "<< delta1 + delta1 << std::endl;
std::cout << "delta0 - delta1 = "<< delta1 - delta1 << std::endl;
// 積・商
std::cout << "delta1 * 2.0 = "<< delta1 * 2 << std::endl;
std::cout << "delta1 / 2.0 = "<< delta1 / 2 << std::endl;
/* 出力 */
// >> delta0 + delta1 = TimeDelta(days=730, hours=0, minutes=0, seconds=0)
// >> delta0 - delta1 = TimeDelta(days=0, hours=0, minutes=0, seconds=0)
// >> delta1 * 2.0 = TimeDelta(days=730, hours=0, minutes=0, seconds=0)
// >> delta1 / 2.0 = TimeDelta(days=182, hours=12, minutes=0, seconds=0)
例外送出
- Datetimeオブジェクト、TimeDeltaオブジェクトともに、
EZ::DatetimeException
を送出します。- 送出された例外は、
ex.what()
でメッセージを確認できます。
- 送出された例外は、
sample.cpp
// 例外処理
try{
// Year is Negative.
EZ::Datetime err = EZ::Datetime(-2000, 1, 1, 0, 0, 0);
}
catch(EZ::DatetimeException ex){
std::cout << "Error message: " << ex.what() << std::endl;
}
/* 出力 */
// >> Error message: Input time is out of range. minimum datetime is 1970/01/01 09:00:00 JST
Q&A
Q1. タイムゾーンは2種類しか設定できないの?
- はい。現地時刻 か UTC の2種類しか選べません。
- 現地時刻を選択した場合、どのタイムゾーンが適用されるかはOSの時刻設定に依存し、自動で決まります。
Q2. タイムゾーンはどうやって設定するの?
- コンストラクタの末尾の引数
const bool& isUTC
の真偽値で決定します。- isUTC = false ならばローカル時刻
- isUTC = true ならばUTC
- デフォルトでは isUTC = false です。
- こちらの章もご覧ください。
Q3. タイムゾーンの確認方法は?
- Datetimeインスタンスに対し、メンバ関数
timezone()
を呼び出します。- インスタンスに適用されたタイムゾーン情報が std::string 型で返却されます。
Q4. 小数秒の計算には対応しているの?
- 小数秒には対応しておりません。小数秒は切り捨てられ、整数秒として扱われます。
Q5. 閏年、閏秒への対応は?
- 閏年には対応しておりますが、閏秒には対応していません。
- 閏年、閏秒の計算方法は、
struct tm
の計算方法に依存しています。
Q6. サマータイムの設定は?
-
struct tm
と同様に、タイムゾーンに応じてサマータイムは自動設定されます。 - サマータイムの適用状況は、メンバ関数
isDst()
を呼び出すことで確認できます。- isDst() => 0 :夏時間が無効
- isDst() => 1 :夏時間が有効
- isDst() => 負の値 :処理系に依存
- 日本で使用する場合は、常に0になります。
Q7. 処理可能な時間幅は?
- 1970/1/1 0:00:00 UTC 〜 3000/1/2 0:00:00 UTC です。
Q8. このライブラリの実体は何?
-
struct tm
とtime_t
のラッピングライブラリです。
Q9. テストは行ったの?
- GoogleTestでテストコードを記述し、テストを行いました。
- バグがございましたらお気軽にコメントをお願いいたします。