Хороший вопрос (проголосовал).
Думаю, сначала нам нужно определиться с правильным ответом. Вот ваш ответ, и в настоящее время единственным другим ответом является Matteo's . В демонстрационных целях я изменил оба ответа, чтобы заменить в «подделке сейчас», чтобы мы могли сравнить яблоки с яблоками:
using namespace std::chrono_literals;
auto now = date::sys_days{date::March/27/2019} + 0h + 32min + 22s + 123456us;
(примерно сейчас, когда я пишу это)
Код Чила дает:
Fractional day of the year: 85.0225
Код Маттео дает:
Fractional day of the year: 85.139978280740735
Они близко, но не настолько близко, чтобы оба считались правильными.
Код Маттео работает со "средними годами":
auto this_year = date::floor<date::years>(now);
Длина date::years
составляет 365,2425 дней, что совершенно верно, если вы усредните все гражданские годы за 400-летний период. И работа со средней продолжительностью года может быть очень полезной, особенно когда речь идет о системах, которые не заботятся о созданных человеком календарях (например, физика или биология).
Я собираюсь догадаться, что из-за способа написания кода Чила он предпочел бы результат, который более точно относится к этому конкретному году. Поэтому приведенный ниже код является алгоритмом Чиля, в результате чего получается точно такой же результат, только чуть более эффективный и лаконичный.
// Get actual time.
auto now = std::chrono::system_clock::now();
// Get the number of days since start of the year.
auto sd = date::floor<date::days>(now);
auto ymd = date::year_month_day( sd );
auto ymd_ref = ymd.year()/1/1;
std::chrono::duration<double, date::days::period> days = sd - date::sys_days{ymd_ref};
// Get the fractional number of seconds of the day.
days += now - sd;
// Get fractional day number.
std::cout << "Fractional day of the year: " << days.count() << std::endl;
Первое, что я заметил, было то, что date::floor<date::days>(now)
вычислялось в 3 местах, поэтому я вычисляю его один раз и сохраняю в sd
.
Далее, поскольку окончательный ответ представляет собой двойное представление days
, я позволю <chrono>
выполнить эту работу за меня, сохранив ответ в duration<double, days>
. Каждый раз, когда вы решаете конвертировать единицы, лучше позволить <chrono>
сделать это за вас. Это, вероятно, не будет быстрее. Но это определенно не будет медленнее или неправильнее.
Теперь просто добавить дробный день к результату:
days += now - sd;
с любой точностью now
(микросекунды или что-то еще). И результат теперь просто days.count()
.
Обновление
И еще немного времени, чтобы подумать ...
Я заметил, что с упрощенным кодом, приведенным выше, легче увидеть весь алгоритм в виде одного выражения. То есть (убрав квалификацию пространства имен, чтобы получить все в одной строке):
duration<double, days::period> days = sd - sys_days{ymd_ref} + now - sd;
И это явно алгебраически упрощается до:
duration<double, days::period> days = now - sys_days{ymd_ref};
В итоге:
using namespace std::chrono;
using namespace date;
// Get actual time.
auto now = system_clock::now();
// Get the start of the year and subract it from now.
using ddays = duration<double, days::period>;
ddays fd = now - sys_days{year_month_day{floor<days>(now)}.year()/1/1};
// Get fractional day number.
std::cout << "Fractional day of the year: " << fd.count() << '\n';
В этом случае, разрешив <chrono>
выполнить преобразования для нас, можно было достаточно упростить код, так что сам алгоритм мог быть алгебраически упрощен, что привело к получению более чистого и более эффективного кода, который доказуемо эквивалентен исходному алгоритму в вопрос ОП.