Using (draft) C++20 chrono, how to compute various facts about a date

情到浓时终转凉″ 提交于 2020-01-22 06:59:05

问题


https://www.timeanddate.com/date/weekday.html computes various facts about a day of the year, for example:

Given an arbitrary date, how can these numbers be computed with the draft C++20 chrono specification?


回答1:


This is remarkably easy with the draft C++20 chrono specification. Below I show a function which inputs an arbitrary date, and prints this information to cout. Though at the time of this writing, the draft C++20 chrono specification isn't yet shipping, it is approximated by a free, open-source library. So you can experiment with it today, and even include it in shipping applications as long as you adopt C++11 or later.

This answer will take the form of a function:

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

sys_days is a day-precision time_point in the system_clock family. That means it is simply a count of days since 1970-01-01 00:00:00 UTC. The type alias sys_days is new with C++20, but the underlying type has been available since C++11 (time_point<system_clock, duration<int, ratio<86400>>>). If you use the open-source C++20 preview library, sys_days is in namespace date.

The code below assumes function-local:

using namespace std;
using namespace std::chrono;

to reduce verbosity. If you are experimenting with the open-source C++20 preview library, also assume:

using namespace date;

Heading

To output the first two lines is simple:

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

Just take the date sd and use format with the familiar strftime/put_time flags to print out the date and text. The open-source C++20 preview library hasn't yet integrated the fmt library, and so uses the slightly altered format string "%d %B %Y is a %A\n".

This will output (for example):

26 December 2019 is a Thursday

Additional facts

Common intermediate results computed once

This section of the function is written last, because one doesn't yet know what computations will be needed multiple times. But once you know, here is how to compute them:

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;

We will need the year and month fields of sd, and the weekday (day of the week). It is efficient to compute them once and for all in this manner. We will also need (multiple times) the first and last days of the current year. It is hard to tell at this point, but it is efficient to store these values as type sys_days as their subsequent use is only with day-oriented arithmetic which sys_days is very efficient at (sub-nanosecond speeds).

Fact 1: day number of year, and number of days left in year

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";

This prints out the day number of the year, with January 1 being day 1, and then also prints out the number of days remaining in the year, not including sd. The computation to do this is trivial. Dividing each result by days{1} is a way to extract the number of days in dn and dl into an integral type for formatting purposes.

Fact 2: Number of this weekday and total number of weekdays in year

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 is the day of the week (Monday thru Sunday) computed at the top of this article. To perform this computation we first need the dates of the first and last wd's in the year y. y/1/wd[1] is the first wd in January, and y/12/wd[last] is the last wd in December.

The total number of wds in the year is just the number of weeks between these two dates (plus 1). The sub-expression last_wd - first_wd is the number of days between the two dates. Dividing this result by 1 week results in an integral type holding the number of weeks between the two dates.

The week number is done the same way as the total number of weeks except one starts with the current day instead of the last wd of the year: sd - first_wd.

Fact 3: Number of this weekday and total number of weekdays in month

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);

This works just like Fact 2, except we start with the first and last wds of the year-month pair y/m instead of the entire year.

Fact 4: Number of days in year

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

The code pretty much speaks for itself.

Fact 5 Number of days in month

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";

The expression y/m/last is the last day of the year-month pair y/m, and of course y/m/1 is the first day of the month. Both are converted to sys_days so that they can be subtracted to get the number of days between them. Add 1 for the 1-based count.

Use

info can be used like this:

info(December/26/2019);

or like this:

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

Here is example output:

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.

Edit

For those who are not fond of the "conventional syntax", there is a complete "constructor syntax" that can be used instead.

For example:

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

can be replaced by:

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}};


来源:https://stackoverflow.com/questions/59418514/using-draft-c20-chrono-how-to-compute-various-facts-about-a-date

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!