C ++ 20クロノを使用して、日付に関するさまざまな事実を計算する方法


19

https://www.timeanddate.com/date/weekday.htmlは、その年の1日に関するさまざまな事実を計算します。次に例を示します。

https://i.stack.imgur.com/WPWuO.png

任意の日付が与えられた場合、これらの数値はC ++ 20クロノ仕様でどのように計算できますか?


2
「...そして、ISOの第1週がいつになるのか、私たちは皆知っていますか?」 -「いいえ、しかし、ライブラリは持っています」... :-)-ブラボーハワード!
Ted Lyngmo

stackoverflow.com/q/59391132/560648から取得した画像(現在は削除)。残念ながら、これはその質問への答えであるはずだったので削除されました。
にオービットの軽量レース

正しい。私はそれを再開することに投票しました。
Howard Hinnant

回答:


22

これは、C ++ 20クロノ仕様で非常に簡単です。以下に、任意の日付を入力し、この情報をに出力する関数を示しcoutます。この記事の執筆時点では、C ++ 20クロノ仕様はまだ出荷されていませんが、無料のオープンソースライブラリによって概算されています。したがって、C ++ 11以降を採用している限り、今日それを試し、出荷アプリケーションに含めることもできます。

この答えは関数の形をとります:

void info(std::chrono::sys_days sd);

sys_days当日精度でtime_pointsystem_clock家族。つまり、1970-01-01 00:00:00 UTCからの日数です。型エイリアスsys_daysはC ++ 20で新しく追加されましたが、基礎となる型はC ++ 11(time_point<system_clock, duration<int, ratio<86400>>>)以降で使用できます。あなたが使用している場合は、オープンソースのC ++ 20のプレビューライブラリをsys_daysですnamespace date

以下のコードは関数ローカルを想定しています:

using namespace std;
using namespace std::chrono;

冗長性を減らすため。オープンソースのC ++ 20プレビューライブラリを試す場合は、次のことも想定してください。

using namespace date;

見出し

最初の2行を出力するのは簡単です:

cout << format("{:%d %B %Y is a %A}\n", sd)
     << "\nAdditional facts\n";

日付sdを取得formatし、使い慣れたstrftime/ put_timeフラグを使用して日付とテキストを出力します。オープンソースのC ++ 20プレビューライブラリはまだ統合されていないFMTライブラリを、そして多少変更された書式文字列を使用します"%d %B %Y is a %A\n"

これは(例えば)出力します:

26 December 2019 is a Thursday

Additional facts

一度計算された一般的な中間結果

関数のこのセクションは最後に書かれています。何回計算が必要になるかはまだわからないからです。しかし、あなたが知ったら、それらを計算する方法は次のとおりです:

year_month_day ymd = sd;
auto y = ymd.year();
auto m = ymd.month();
weekday wd{sd};
sys_days NewYears = y/1/1;
sys_days LastDayOfYear = y/12/31;

年と月のフィールドsd、およびweekday(曜日)が必要です。この方法で一度にすべてを計算するのが効率的です。今年の最初と最後の日も(複数回)必要です。この時点で伝えるのは難しいですが、タイプとして、これらの値を格納するのに効率的であるsys_days彼らのその後の使用が唯一の日指向算術であるようsys_daysである非常に(サブナノ秒の速度)で効率的。

ファクト1:年の日数、および年の残り日数

auto dn = sd - NewYears + days{1};
auto dl = LastDayOfYear - sd;
cout << "* It is day number " << dn/days{1} << " of the year, "
     << dl/days{1} << " days left.\n";

これは、1月1日を1日として、その年の日数を出力し、次に、を含まない、その年の残りの日数も出力しsdます。これを行うための計算は簡単です。各結果を分割するdays{1}の日数を抽出する方法であるdndl目的をフォーマットするための一体型に。

ファクト2:この平日の数と年間の平日の総数

sys_days first_wd = y/1/wd[1];
sys_days last_wd = y/12/wd[last];
auto total_wd = (last_wd - first_wd)/weeks{1} + 1;
auto n_wd = (sd - first_wd)/weeks{1} + 1;
cout << format("* It is {:%A} number ", wd) << n_wd << " out of "
     << total_wd << format(" in {:%Y}.\n}", y);

wdこの記事の上部で計算された曜日(月曜日から日曜日)です。この計算を実行するには、最初wdに年の最初と最後の日付が必要yです。 1月y/1/wd[1]の最初wdで、12月y/12/wd[last]の最後wdです。

その年のの合計数はwd、これら2つの日付の間の週数(プラス1)です。部分式last_wd - first_wdは、2つの日付の間の日数です。この結果を1週間で割ると、2つの日付の間の週数を保持する整数型になります。

週番号は、週の総数と同じ方法で行われます。ただしwd、年の最後ではなく現在の日から始まる場合を除きますsd - first_wd

事実3:この平日の数と月の平日の総数

first_wd = y/m/wd[1];
last_wd = y/m/wd[last];
total_wd = (last_wd - first_wd)/weeks{1} + 1;
n_wd = (sd - first_wd)/weeks{1} + 1;
cout << format("* It is {:%A} number }", wd) << n_wd << " out of "
     << total_wd << format(" in {:%B %Y}.\n", y/m);

これはファクト2と同じように機能しますwdが、年y/m全体ではなく、年と月のペアの最初と最後のs から始めます。

事実4:1年の日数

auto total_days = LastDayOfYear - NewYears + days{1};
cout << format("* Year {:%Y} has ", y) << total_days/days{1} << " days.\n";

このコードは、それ自体が意味をなしています。

ファクト5月の日数

total_days = sys_days{y/m/last} - sys_days{y/m/1} + days{1};
cout << format("* {:%B %Y} has ", y/m) << total_days/days{1} << " days.\n";

y/m/lastは、年と月のペアの最後の日でy/mあり、もちろんy/m/1月の最初の日です。両方がに変換されsys_daysて、それらの間の日数を取得するために減算できます。1ベースのカウントに1を追加します。

使用する

info このように使用できます:

info(December/26/2019);

またはこのように:

info(floor<days>(system_clock::now()));

次に出力例を示します。

26 December 2019 is a Thursday

Additional facts
* It is day number 360 of the year, 5 days left.
* It is Thursday number 52 out of 52 in 2019.
* It is Thursday number 4 out of 4 in December 2019.
* Year 2019 has 365 days.
* December 2019 has 31 days.

編集する

「従来の構文」が苦手な方のために、代わりに使用できる完全な「コンストラクター構文」があります。

例えば:

sys_days NewYears = y/1/1;
sys_days first_wd = y/1/wd[1];
sys_days last_wd = y/12/wd[last];

で置き換えることができます:

sys_days NewYears = year_month_day{y, month{1}, day{1}};
sys_days first_wd = year_month_weekday{y, month{1}, weekday_indexed{wd, 1}};
sys_days last_wd = year_month_weekday_last{y, month{12}, weekday_last{wd}};

5
この除算演算子の新しい乱用は、ビットシフト演算子の古い乱用よりもさらに悪いものです。それは私を悲しくします:(
Dave

2
より深刻な注意として、事前に計算された変数のいくつかを、それらを使用するセクションに移動することを提案できますか?値をどこから取得し、どのように生成されたかを確認するために上下にスクロールする必要がある場合は、少し面倒です。そして、週のように最初に除算を行うことで、年の日を少し整理することができます。
Dave

1
完全に同意しません。見栄えがよく、理解しやすく、特に、より詳細なバージョンより読みやすくなっています。
カシオレナン

@CássioRenanはそうかもしれませんが、構文の乱用には予期しない動作が伴うことがよくあります。前述のビットシフトを使用して、たとえば、std::cout << "a*b = " << a*b << "; a^b = " << a^b << '\n';(幸いなことに、ほとんどの場合、コンパイル時に捕捉されますが、それでも厄介です)の動作に注意してください。したがって、この新しい除算演算子の乱用を使用するときは注意が必要です。
ルスラン

@Ruslan Cautionは、新しいライブラリでは常に保証されます。これが2015年以降、自由に公的にテストされている理由です。クライアントからのフィードバックは、設計に組み込まれています。長年にわたる実地経験の確固たる基盤が確立されるまで、標準化は提案されませんでした。特に、演算子の使用は、演算子の優先順位を考慮して設計されており、フィールドで広くテストされており、同等の「コンストラクターAPI」が付属しています。star-history.t9t.io/#HowardHinnant/date&google/cctzおよびyoutube.com/watch?v=tzyGjOm8AKoをご覧ください。
ハワードヒンナン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.