Використовуючи C ++ 20 хроно, як обчислити різні факти про побачення


19

https://www.timeanddate.com/date/weekday.html обчислює різні факти про день року, наприклад:

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

З огляду на довільну дату, як можна обчислити ці числа за специфікацією хронолу C ++ 20 ?


2
"... і всі ми знаємо, коли ISO тиждень 1, правда? ..." - "Ні, але у мене є бібліотека" ... :-) - Браво Говард!
Тед Лінгмо

Зображення, взято з stackoverflow.com/q/59391132/560648 (тепер видалено). Соромно, що це було видалено, оскільки це повинно було відповісти на це питання.
Гонки легкості на орбіті

Правильно. Я проголосував за повторне відкриття цього.
Говард Хінант

Відповіді:


22

Це надзвичайно просто завдяки специфікації C ++ 20 chrono . Нижче я показую функцію, яка вводить довільну дату і друкує цю інформацію cout. Хоча на момент написання цього документа специфікація хронології C ++ 20 ще не доставлена, вона наближається до безкоштовної бібліотеки з відкритим кодом . Таким чином, ви можете сьогодні експериментувати з ним і навіть включати його в додатки для доставки, якщо ви прийняли C ++ 11 або новішу версію.

Ця відповідь буде мати форму функції:

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

sys_days- це точність дня time_pointв system_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;

Заголовок

Вивести перші два рядки просто:

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, а також друкується кількість днів, що залишилися в році, не враховуючи sd. Розрахунок для цього тривіальний. Розділивши кожен результат, days{1}являєшся способом вилучення кількості днів в dnі dlв інтегральний тип для цілей форматування.

Факт 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. y/1/wd[1]є першим wdу січні, і y/12/wd[last]останнім wdу грудні.

Загальна кількість wds в році - це лише кількість тижнів між цими двома датами (плюс 1). Підвираз last_wd - first_wd- це кількість днів між двома датами. Поділ цього результату на 1 тиждень призводить до цілісного типу, що містить кількість тижнів між двома датами.

Номер тижні робиться таким же чином , як загальне число тижнів , за винятком одного починається з поточним днем замість останнього 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замість цілого року.

Факт 4: Кількість днів у році

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
Це нове зловживання оператором поділу ще гірше, ніж старе зловживання операторами бітшіфті. Мене це сумно :(
Дейв

2
Більш серйозну увагу, чи можу я запропонувати вам перемістити деякі свої попередньо обчислені змінні вниз до розділів, які їх використовують? Трохи незручно слідувати, коли потрібно прокручувати вгору та вниз, щоб побачити, звідки беруться значення і як вони були створені. І ви можете трохи знеструмити свої щоденні речі, зробивши поділ спочатку, як це робили тижні.
Дейв

1
Не погоджуюся повністю. Це добре виглядає, його легко зрозуміти і, особливо, його легше читати, ніж більш багатослівну версію.
Кассіо Ренан

@ CássioRenan може бути, але пам’ятайте, що зловживання синтаксисом досить часто відбувається з несподіваною поведінкою. З урахуванням вищезгаданих змін бітів, наприклад, зверніть увагу на поведінку std::cout << "a*b = " << a*b << "; a^b = " << a^b << '\n';(яка, на щастя, майже завжди потрапляє під час компіляції, але все ще викликає роздратування). Тож я б з обережністю користувався цим новим оператором підрозділу.
Руслан

@Ruslan Застереження завжди гарантується будь-якою новою бібліотекою. Ось чому цей вільно та загальнодоступний тест з 2015 року. Відгуки клієнтів були включені ще в дизайн. Він не був запропонований для стандартизації, поки він не мав міцної основи багаторічного позитивного досвіду. Зокрема, використання операторів було розроблено з урахуванням переваги операторів, широко перевірених на місцях та має еквівалентний "конструктор API". Див. Star-history.t9t.io/#HowardHinnant/date&google/cctz та youtube.com/watch?v=tzyGjOm8AKo .
Говард Хіннант
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.