Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces support for std::chrono::time_point serialization and deserialization, including a new Parser_time_point.hpp header and comprehensive unit tests. The implementation handles ISO 8601 formatting with microsecond precision. Feedback focuses on restricting the parser to std::chrono::system_clock to ensure compatibility with calendar-time conversions, increasing precision to nanoseconds, and improving input validation to strictly enforce the 'Z' suffix. Additionally, a thread-safety improvement was suggested regarding locale handling during parsing.
| template <class R, class W, class Clock, class Duration, class ProcessorsType> | ||
| requires AreReaderAndWriter<R, W, std::chrono::time_point<Clock, Duration>> | ||
| struct Parser<R, W, std::chrono::time_point<Clock, Duration>, ProcessorsType> { |
There was a problem hiding this comment.
The current implementation assumes the clock is std::chrono::system_clock by using from_time_t and to_time_t. However, the template specialization is currently open to any Clock. Clocks like std::chrono::steady_clock do not represent calendar time and cannot be portably converted to/from ISO 8601 strings using these methods. The specialization should be restricted to std::chrono::system_clock.
| template <class R, class W, class Clock, class Duration, class ProcessorsType> | |
| requires AreReaderAndWriter<R, W, std::chrono::time_point<Clock, Duration>> | |
| struct Parser<R, W, std::chrono::time_point<Clock, Duration>, ProcessorsType> { | |
| template <class R, class W, class Duration, class ProcessorsType> | |
| requires AreReaderAndWriter<R, W, std::chrono::time_point<std::chrono::system_clock, Duration>> | |
| struct Parser<R, W, std::chrono::time_point<std::chrono::system_clock, Duration>, ProcessorsType> { |
|
|
||
| using ParentType = Parent<W>; | ||
|
|
||
| using TimePointType = std::chrono::time_point<Clock, Duration>; |
| const auto sys_time = | ||
| std::chrono::time_point_cast<std::chrono::microseconds>(_tp); |
There was a problem hiding this comment.
The implementation is currently limited to microsecond precision. Since std::chrono::system_clock::time_point often supports nanosecond precision (especially on Linux), and the PR description mentions this as a goal, consider using std::chrono::nanoseconds here and in the parsing logic to avoid loss of data during serialization.
| } | ||
|
|
||
| return std::chrono::time_point_cast<Duration>(tp); |
There was a problem hiding this comment.
The parser should validate that the 'Z' suffix is present and that the entire input string has been consumed. Currently, it may silently ignore trailing characters or incorrect suffixes (e.g., it would successfully parse 2024-01-15T10:30:00Invalid).
}
if (*rest != 'Z' || *(rest + 1) != '\0') {
return error("Could not parse time point from '" + _str + "'.");
}
return std::chrono::time_point_cast<Duration>(tp);| static const char* parse_datetime(const char* _str, std::tm* _tm) { | ||
| #if defined(_MSC_VER) || defined(__MINGW32__) | ||
| std::istringstream input(_str); | ||
| input.imbue(std::locale(setlocale(LC_ALL, nullptr))); |
There was a problem hiding this comment.
Using setlocale(LC_ALL, nullptr) is not thread-safe and depends on the global environment. For parsing a fixed machine-readable format like ISO 8601, std::locale::classic() is preferred as it ensures consistent behavior regardless of the user's system settings.
| input.imbue(std::locale(setlocale(LC_ALL, nullptr))); | |
| input.imbue(std::locale::classic()); |
Addresses #586.
There are a couple immediate problems that I am not sure how to proceed with.
This forces an ISO 8601 time format conversion, which also needs some logic to convert. This could be slow.
I believe Parser duration uses a simpler struct format, which makes sense for the type:
{"seconds": 1234567, "microseconds": 123456}, but for a time_point a date-time format would make more sense.
Also we could go a step further and use nanosecond precision instead of the microsecond precision shown here.
TODO