1

Using C++20's <chrono>, how can I find the days difference of two year_month_day objects?

int main()
{
    using namespace std::chrono;
    const auto now = system_clock::now();
    const year_month_day today = floor<days>(now);
    const year_month_day xmas = today.year() / month(12) / day(25);

    const days days_till_xmas = xmas - today;
    // I want something like this ---^
}
jyl
  • 401
  • 3
  • 12

2 Answers2

3

You can use std::chrono::sys_days to convert to std::chrono::time_point. For example,

auto diff = std::chrono::sys_days(xmas) - std::chrono::sys_days(today);
std::cout << "diff days: " << std::chrono::duration_cast<std::chrono::days>(diff).count() << "days\n";
GAVD
  • 1,977
  • 3
  • 22
  • 40
0

Building on the answers already given, I believe this code can be shorted down to this (full types given, I do know of auto but from a pedagogical perspective I believe it better to show all details):

#include <iostream>
#include <chrono>

int main(void)
{
    std::chrono::time_point<std::chrono::system_clock> today = floor<std::chrono::days>(std::chrono::system_clock::now());
    std::chrono::time_point<std::chrono::system_clock> xmas = std::chrono::sys_days(today.year() / std::chrono::month(12) / std::chrono::day(25));

    std::cout << "There are "
              << std::chrono::duration_cast<std::chrono::days>(xmas - today).count()
              << " days until Santa Claus is coming to town!"
              << std::endl;

    return 0;
}

This solves half my related problem, I just wish chrono could let me do something like this:

std::chrono::time_point<std::chrono::system_clock> xmas = std::chrono::datetime("2023-12-25");

E.g. let me get a fixed timepoint based on a date as specified by ISO 8601. If the date is wrong somehow, either raise exception or otherwise set timepoint to epoch with whatever error handling is deemed appropriate. Non-ISO 8601 dates will not be supported, although it should be noted other standards could also be implemented.

Optionally, you can use XXXX for current year e.g. XXXX-01-01 becomes Jan. first of this year and XXXX-12-25 becomes dec. 25th but now I am getting really out there on the wish list :)

Also for anyone stumbling across this answer, this only works in C++20 and later. Check your compiler settings.

  • `month(25)`?! Perhaps you should store your `today.year() / std::chrono::month(25) / std::chrono::day(10)` expression first and check `ok()`: `auto date = today.year() / std::chrono::month(25) / std::chrono::day(10); if (!date.ok()) ...`. You could also use this less verbose syntax: `auto date = today.year() / 25 / 10;`. The result will be the same. – Howard Hinnant Jul 06 '23 at 13:33
  • Whoops, brainfart, fixed it now. Tested with a different date, didn't see a point in testing the new date since the code obviously works... :P And yes, while the shorthand kinda helps, I want to read the date as a string from a text file in my particular case. While I could easily do a string parse, the ISO 8601 standard has existed for ages and is widely accepted more or less everywhere, so I'd rather not rely on home built tools if I can help it... Especially with as fickle things as dates. Thanks for the comment though! :) – Per Ekström Jul 08 '23 at 22:32
  • 1
    See https://stackoverflow.com/a/76645704/576911 for an answer to your question in your answer. – Howard Hinnant Jul 09 '23 at 02:51
  • Yes, this does it for my particular use case, however, I do not think it is too much to ask for an ISO 6801 full implementation here (in some easy-to-access form, does not have to be std::chrono specifically could just be based on it). I do appreciate the help, it does exactly what I need it to do for my usecase which is wonderful - but this is code I still necessarily must maintain, and it is such a common task having at least some built in support for the spec in the C++ standard proper would be awesome. I recommend https://en.wikipedia.org/wiki/ISO_8601 for further reading. – Per Ekström Jul 09 '23 at 11:58
  • You can parse straight into a `system_clock::time_point` if desired. There are several ISO68001 formats you can use, e.g. "%FT%T" (but not XXXX-mm-dd). `is >> parse("%FT%T", tp);`. It's one line. – Howard Hinnant Jul 09 '23 at 13:07
  • Ah, that is true. So, slightly more complex than I would wish, but possibly good enough for my use case. While I wish this problem was as simple as Python's `delta = datetime.strptime("2023-12-25", "%Y-%m-%d") - datetime.now()`, I think I will need to create a function that validates the string from any ISO format and then another function that allows any field with XX to mean "use current year/month/date/hour/whatever". I do believe I hve sufficient knowledge to create a really small library file based on Chrono now though. Again, thank you for your patience and assistance! – Per Ekström Jul 09 '23 at 20:33