特定の年の週番号が与えられている場合、C/C++ を使用して簡単な方法で週の最初と最後の日付 (日と月) を決定するにはどうすればよいですか? 前もって感謝します!
リー
古い質問に対する新しい答え。
この回答には、標準化のために提案され、却下されたコードが含まれています。私が言及する理由は、コードがstd::chrono
提案の目的のために名前空間にあるからです。ただし、この提案は受け入れられなかったことを理解することが重要です。したがって、この提案を使用する場合は、提案された部分を独自の名前空間に移動する必要があります。それらは標準の一部ではなく、標準化の対象にもなりません。
これは、この計算をかなり簡単にする私の日付クラスのユーザードキュメントです(ISO標準の週番号を想定しています)。ドキュメントは、提案を実装する1つのヘッダー<date>
と1つのソースdate.cppを示しています。
これらのソースと、以下に示す2つの非常に簡単なヘルパー関数を使用して、週番号と年を取得し、その週の最初(月曜日)と最後(日曜日)の日を市民(グレゴリオ暦)カレンダーに出力するプログラムを次に示します。
#include "date"
#include <utility>
#include <iostream>
std::chrono::date
week_to_date(int weeknum, std::chrono::weekday wd, std::chrono::year y)
{
using namespace std::chrono;
return (mon <= jan/_4th/y) + days((weeknum - 1)*7 + (wd == 0 ? 6 : wd - 1));
}
std::pair<std::chrono::date, std::chrono::date>
get_dates(unsigned week_num, unsigned y)
{
using namespace std::chrono;
return std::make_pair(week_to_date(week_num, mon, year(y)),
week_to_date(week_num, sun, year(y)));
}
int main()
{
auto p = get_dates(9, 2013);
std::cout << p.first << '\n';
std::cout << p.second << '\n';
}
私の場合、これは次のように出力されます。
2013-02-25
2013-03-03
のreturnステートメントはweek_to_date
、少し説明を使用できます。
(mon <= jan/_4th/y)
上記の式は、y年の1月4日以前の月曜日である日付を作成します。weeknum
次に、その日付より前の各週に7日を追加します。
+ days((weeknum - 1)*7
最後に、曜日(wd
)が日曜日の場合は6日を追加します。それ以外wd-1
の場合は、日が月曜日== 1、火曜日== 2、土曜日== 6(日曜日== 0)まで番号が付けられる日を追加します。
+ (wd == 0 ? 6 : wd - 1))
この最後のステップは、ISO週ベースの暦週が月曜日に始まることを考慮に入れるものですが、日付クラスのこの実装は、日曜日に始まる週のC /C++規則に従います。
提案week_to_date
には、実際にはISO週ベースの年の定義が含まれていますが、これらは日付クラスの操作方法の例として提案に含まれており、提案の一部ではありません。
このコードは自由に使用できますが、最初に名前空間stdから削除することを強くお勧めします。ソースは十分に短いので、このような小さな変更は難しくありません。
アップデート
この回答ではリンクが古くなっていたので、更新しました。ただし、リンクを更新することに加えて、上記にリンクされているライブラリの再設計に注意を向けたいと思います。再設計では、すべての入力がコンパイル時にわかっている場合のコンパイル時の日付計算など、効率に新たな焦点が当てられています。
つまり、C ++ 14のサポートにより、「日付定数」が実際になりました。
この再設計されたライブラリは、ドキュメントからリンクされた単一のヘッダー実装とともにここにドキュメント化されています。構文は似ていますが、上に示したものと正確ではありません。
#include "date.h"
#include <iostream>
constexpr
date::year_month_day
week_to_date(int weeknum, date::weekday wd, date::year y)
{
using namespace date;
auto const dp = sys_days(jan/4/y);
auto const wdjan4 = weekday(dp);
return dp - (wdjan4 - mon)
+ days((weeknum - 1)*7 + (wd == sun ? 6 : static_cast<unsigned>(wd) - 1));
}
constexpr
std::pair<date::year_month_day, date::year_month_day>
get_dates(int week_num, date::year y)
{
using namespace date;
return std::make_pair(week_to_date(week_num, mon, y),
week_to_date(week_num, sun, y));
}
int
main()
{
using namespace date;
constexpr auto p = get_dates(9, 2013_y);
static_assert(p.first == feb/25/2013, "");
static_assert(p.second == 2013_y/mar/3, "");
std::cout << p.first << '\n';
std::cout << p.second << '\n';
}
2つの大きな違いは、(C ++ 14では)計算を実行できることconstexpr
です。また、operator<=
平日が日付以下の日付を検索することはできなくなりました。代わりに、この機能は「平日減算」によって処理されます。これにより、常に正の数になります(weekday1からweekday2までに何日かかりますか?)。
このプログラムは、前のバージョンと同じように出力します。
2013-02-25
2013-03-03
現在の大きな違いは、コンパイル時定数がに出力されていることstd::cout
です。ただし、同じコード(マイナスstatic_asserts
)は、実行時の入力でも問題なく機能します。
そしてビートは続く
上記で紹介したライブラリを新しい互換性のあるライブラリと組み合わせると"iso_week.h"
、上記の計算の構文が大幅に簡素化されます。
#include "date.h"
#include "iso_week.h"
#include <iostream>
int
main()
{
using namespace iso_week::literals;
auto y = 2013_y;
auto wn = 9_w;
std::cout << "The dates for " << y/wn << " are "
<< date::year_month_day{y/wn/mon} << " through "
<< date::year_month_day{y/wn/sun} << '\n';
}
出力:
The dates for 2013-W09 are 2013-02-25 through 2013-03-03
これは、週番号付けに関するローカル ルールの影響を大きく受けます。どの曜日が週の始まりであるかについては、少なくとも 2 つの一般的な規則があり、年の開始時にどの週が 1 番であるかについては、3 つの一般的な規則があります。
そのようなコードを延々といじったり、ライブラリを使用したりする必要はありません。ICU ライブラリは、Calendar クラスでこれをカバーしています。関連紹介ページはこちら。ページの下部にある WEEK_OF_YEAR フィールドのメモを確認してください。