My last post was about trying to find a solution to a fairly simple need regarding dates that I thought would have been solved in the C++ language or the STL by this point in time. In the end we decided to write our own logic to format the date string because our need was specific and simple and there wasn’t a common solution already in place. At this point there isn’t a need to do any more with dates and, as such, that decision still stands.
BUT!! The date library by Howard Hinnant was presented as an option. Because it is the basis for the types that will be included in C++20, is compatible with C++11, and is pretty simple, I did a proof of concept to see if it is something that I could use on the project.
Sample Code from Howard Hinnant
When I was presented this library as a possible solution in the cpplang slack, Howard started a thread and gave me a short sample that produced the date in exactly the form I needed it. That sample follows and is what I’m using in my proof of concept.
#include "date/date.h"
#include <iostream>
#include <sstream>
#include <string>
int main(void)
{
std::string s = "20190324";
date::year_month_day ymd{};
std::istringstream in{ s };
in >> date::parse("%Y%m%d", ymd);
if (!ymd.ok())
{
std::cout << "error";
return 1;
}
std::cout << ymd << 'n';
}
How did the date library perform?
On Windows, we’re using Visual Studio 2017 and, with it set to compile as C++14, the sample code compiled and ran with no issue. On Linux we are using g++ 4.8.5 and, because it’s an old version of g++, there were problems (of course). I ran into a compile error and found quickly that the issue had already been seen and a workaround given by Howard.
Compile Error with G++ 4.8.5
The error is as follows:
/home/taylor/development/date/include/date/date.h:
In instantiation of ‘std::basic_istream<_CharT, _Traits>& date::from_stream(std::basic_istream<_CharT, _Traits>&, const CharT*, date::year_month_day&, std::basic_string<_CharT, _Traits, _Alloc>*, std::chrono::minutes*) [with CharT = char; Traits = std::char_traits<char>; Alloc = std::allocator<char>; std::chrono::minutes = std::chrono::duration<int, std::ratio<60l> >]’:
/home/taylor/development/date/include/date/date.h:7830:74: required from ‘std::basic_istream<_CharT, _Traits>& date::operator>>(std::basic_istream<_CharT, _Traits>&, const date::parse_manip<Parsable, CharT, Traits, Alloc>&) [with Parsable = date::year_month_day; CharT = char; Traits = std::char_traits<char>; Alloc = std::allocator<char>]’ main.cpp:11:33: required from here /home/taylor/development/date/include/date/date.h:4557:5: error: converting to ‘date::weekday’ from initializer list would use explicit constructor ‘constexpr date::weekday::weekday(unsigned int)’
fields() = default;
^
/home/taylor/development/date/include/date/date.h:7735:20: note: synthesized method ‘date::fields<Duration>::fields() [with Duration = std::chrono::duration<long int>]’ first required here fields<CT> fds{};
Workaround
The workaround is to open date.h and change the following line:
fields() = default;
To:
fields() : ymd{year{0}/0/0}, wd{7u} {}
Additionally, the ONLY_C_LOCALE macro must be defined when compiling. After making this simple adjustments the code compiled and worked as expect.
Concerns with the workaround
My only major concern about the workaround is that it requires a change to the date header specifically on Linux. This means that the code for the library that I’d be using on Linux and Windows isn’t exactly the same. It also means that I need to keep two copies of the library source around so that they can be compiled appropriately on both Linux and Windows.
In reality, though, the workaround doesn’t affect the API and the way my code will look. This means that when we eventually update our compiler our code won’t have to change to work with the library.
Conclusion
I like the library enough that, even with my concern about having to implement the workaround for Linux, that I will still make use of this library when I’ve got the time to make such a change.