3
\$\begingroup\$

Motivation:

It is known that std::time_point<...> cannot be streamed directly using operator<<(), which is pretty disappointing. Although Howard Hinnant created a good library, I'd like to have sole ownership of all of the code in my library, so I could do some benchmarks without including extra license and setup repositories faster.

Description:

Mainly it is only a streaming operator, which actually uses some C date-time library under the hood. The approach creates a huge chance for data race if std::chrono::time_point<...>s are streamed from multiple threads, even into multiple streams, because std::localtime() can return a static variable.

The formatting string is shared as well, which increases burden on users.

The streaming operator is templated only on the duration type, since casting between clocks which produced time point is not possible via std::chrono::time_point_cast<>().

Code:

#include <iosfwd>
#include <chrono>
#include <iomanip>
#include <ctime>
#include <string>

namespace shino
{
    namespace detail
    {
        std::string time_format{"%Y %b %d %H:%M:%S"};
    }

    std::string time_format()
    {
        return detail::time_format;
    }

    void set_time_format(const std::string& format)
    {
        detail::time_format = format;
    }
}

template <typename Duration>
std::ostream& operator<<(std::ostream& os,
                         const std::chrono::time_point<std::chrono::system_clock, Duration>& timep)
{
    using system_clock_duration = std::chrono::system_clock::duration;
    auto converted_timep = std::chrono::time_point_cast<system_clock_duration>(timep);
    auto seconds_since_epoch = std::chrono::system_clock::to_time_t(timep);
    os << std::put_time(std::localtime(&seconds_since_epoch), shino::time_format().c_str());
    return os;
}

Small demonstration:

#include <iostream>

int main()
{
    std::cout << "current date and time: "
              << std::chrono::system_clock::now() << '\n';

    shino::set_time_format(shino::time_format() + " %a"); //add day of the week
    std::cout << "current date and time with day of the week: "
              << std::chrono::system_clock::now() << '\n';

    return 0;
}

Result:

current date and time: 2017 Jun 17 16:01:27
current date and time with day of the week: 2017 Jun 17 16:01:27 Sat
\$\endgroup\$
6
\$\begingroup\$

Global singleton format looks a bit peculiar. Unfortunatelly, iostreams do not support user-defined stream properties like time format, but you can do something similar:

struct format
{
    std::string format;
    time_point time;
};

ostream & operator << (ostream & os, format const & f)
{
    // output f.time using f.format
    // ...
}

Which can be used like

std::cout << format{"%Y %b %d %H:%M:%S %a", now()} << std::endl;

Alongside with operator<<(ostream &, time_point const &) (which just uses some default predefined format) this can be used in a lot cleaner way.

(obviously, all this should be put to appropriate namespaces)

| improve this answer | |
\$\endgroup\$
  • 2
    \$\begingroup\$ Great, making the format string second and defaulted argument of non explicit constructor will make things much more clearer. Thanks. \$\endgroup\$ – Incomputable Jun 18 '17 at 21:10
1
\$\begingroup\$

Maybe add a enum which predefines the output formats. This prevents from screwing up the string formatting. So you would have something like this:

enum class TimeFormats {

    ddmmyy,
    mmddyy,

    //etc....

};
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ yes, though I think an object that one could stream will be much easier to use. Thanks for the suggestion. \$\endgroup\$ – Incomputable Jun 18 '17 at 14:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.