Search code examples
c++datetimeparsingc++20c++-chrono

C++ parse datetime string with "last"-day notation


std::chrono::year_month_day_last provides a very convenient way to express last days of year/month pairs.

Is there a possibility using std::chrono::parse, Howard Hinnant's free, open-source, C++11, header-only datetime library or boost to parse a date-string where the "last" part is modeled as the value zero or the absence of a value, f.e. parse "2023-01-00" or "2023-01" as year_month_day_last{2023y / January / last}?


Solution

  • How about the last part being modeled with the string "last"?

    #include "date/date.h"
    #include <chrono>
    #include <iostream>
    #include <sstream>
    
    date::year_month_day
    my_parse(std::istream& is)
    {
        using namespace date;
        using namespace std;
        using namespace chrono;
    
        year_month_day result{};
        year_month ym;
        is >> parse(" %Y-%m-", ym);
        if (!is.fail())
        {
            if (isdigit(is.peek()))
            {
                day d;
                is >> parse("%d", d);
                if (!is.fail())
                    result = ym/d;
            }
            else
            {
                string word;
                is >> word;
                if (word != "last")
                    is.setstate(ios::failbit);
                else
                    result = ym/last;
            }
        }
        return result;
    }
    

    For C++20, drop #include "date/date.h" and using namespace date;.

    Just read in a year, month and the trailing -, and then test to see if you have a number or not following. If number it is a day number, else it better be the string "last".

    The beginning space in the format string matches zero or more white space characters.

    This could be exercised with:

    int
    main()
    {
        using namespace std;
    
        istringstream in{"2023-01-30 2023-01-last"};
        in.exceptions(ios::failbit);
        auto date1 = my_parse(in);
        cout << date1 << '\n';
        auto date2 = my_parse(in);
        cout << date2 << '\n';
    }
    

    which outputs:

    2023-01-30
    2023-01-31