How to Convert Std::Chrono::Time_Point to Calendar Datetime String with Fractional Seconds

How to convert std::chrono::time_point to calendar datetime string with fractional seconds?

If system_clock, this class have time_t conversion.

#include <iostream>
#include <chrono>
#include <ctime>

using namespace std::chrono;

int main()
{
system_clock::time_point p = system_clock::now();

std::time_t t = system_clock::to_time_t(p);
std::cout << std::ctime(&t) << std::endl; // for example : Tue Sep 27 14:21:13 2011
}

example result:

Thu Oct 11 19:10:24 2012

EDIT:
But, time_t does not contain fractional seconds.
Alternative way is to use time_point::time_since_epoch() function. This function returns duration from epoch.
Follow example is milli second resolution's fractional.

#include <iostream>
#include <chrono>
#include <ctime>

using namespace std::chrono;

int main()
{
high_resolution_clock::time_point p = high_resolution_clock::now();

milliseconds ms = duration_cast<milliseconds>(p.time_since_epoch());

seconds s = duration_cast<seconds>(ms);
std::time_t t = s.count();
std::size_t fractional_seconds = ms.count() % 1000;

std::cout << std::ctime(&t) << std::endl;
std::cout << fractional_seconds << std::endl;
}

example result:

Thu Oct 11 19:10:24 2012

925

How to convert std::chrono::time_point to string

Howard Hinnant's free, open source, header-only, portable date/time library is a modern way to do this that doesn't traffic through the old C API, and doesn't require that you discard all of your sub-second information. This library is also being proposed for standardization.

There is a lot of flexibility in formatting. The easiest way is to just stream out:

#include "date.h"
#include <iostream>

int
main()
{
using namespace date;
std::cout << std::chrono::system_clock::now() << '\n';
}

This just output for me:

2017-09-15 13:11:34.356648

The using namespace date; is required in order to find the streaming operator for the system_clock::time_point (it isn't legal for my lib to insert it into namespace std::chrono). No information is lost in this format: the full precision of your system_clock::time_point will be output (microseconds where I ran this on macOS).

The full suite of strftime-like formatting flags is available for other formats, with minor extensions to handle things like fractional seconds. Here is another example that outputs with millisecond precision:

#include "date.h"
#include <iostream>

int
main()
{
using namespace date;
using namespace std::chrono;
std::cout << format("%D %T %Z\n", floor<milliseconds>(system_clock::now()));
}

which just output for me:

09/15/17 13:17:40.466 UTC

Convert steady_clock::time_point (C++) to System::DateTime (C++/CLI)

The approach is sound, but the code can be made shorter and easier to read with a couple small changes:

template<typename Rep, typename Period>
System::TimeSpan DurationToTimeSpan(std::chrono::duration<Rep, Period> const& input)
{
auto milliSecs =
std::chrono::duration_cast<std::chrono::milliseconds>(input).count();
return System::TimeSpan::FromMilliseconds(milliSecs);
}

System::DateTime SteadyClockTimePointToDateTime(
std::chrono::steady_clock::time_point const & tCPP)
{
auto const nowCPP = std::chrono::steady_clock::now();
auto nowCLI = System::DateTime::Now;
auto tCLI = nowCLI + DurationToTimeSpan(tCPP - nowCPP);
}

The specific changes made are:

  1. Hide use of the duration cast and TimeSpan factory function invocation in another helper function.1
  2. Reverse the order of subtraction of nowCPP and tCPP, to avoid having to reverse the sign later.
  3. Add const to local variables having native type and which will not be changed. .NET types sadly are not const-correct, because const-ness is only respected by the C++/CLI compiler and not the languages in which the .NET library is written.
  4. Avoid explicit type conversion between the return value of count() and the parameter of AddMilliseconds. If for some platform there is a different duration representation and implicit conversion doesn't work, it's better to have the compiler tell the maintenance programmer.

Note that the result of this function does NOT provide the "steady clock" guarantee. In order to do so, one should generate a single time_point/DateTime pair and save it for later reuse.


1 Another choice would be to use the AddMilliseconds member function, which replaces a call to TimeSpan factory function and overloaded operator-:

System::DateTime SteadyClockTimePointToDateTime(
std::chrono::steady_clock::time_point const & tCPP)
{
auto const nowCPP = std::chrono::steady_clock::now();
auto nowCLI = System::DateTime::Now;
auto const milliSecsUntilT =
std::chrono::duration_cast<std::chrono::milliseconds>(tCPP - nowCPP).count();
auto tCLI = nowCLI.AddMilliseconds(milliSecsUntilT);
return tCLI;
}

Creating a `std::chrono::time_point` from a calendar date known at compile time

Yes, you can do the entire computation at compile time, creating a constexpr system_clock::time_point using Howard Hinnant's date/time library.

#include "date/date.h"
#include <chrono>

int
main()
{
using namespace date;
using namespace std::chrono;
constexpr system_clock::time_point tp = sys_days{January/9/2014} + 12h + 35min + 34s;
static_assert(tp == system_clock::time_point{1389270934s}, "");
}

This is assuming that the date/time is UTC. If it isn't, you will have to manually add/subtract the UTC offset to make it so. As time zone rules are changed at the whim of politicians all the time, there is little hope in making them constexpr. Even historical time zone rules are updated when misunderstandings come to light.

Also this program will port to C++20 by dropping #include "date/date.h" and using namespace date;. Also using Howard Hinnant's date/time library requires C++14 constexpr muscle. C++11 constexpr is not sufficient (but you can do it at run-time, dropping the constexpr and static_assert).

Convert a string with micro second precision to a chrono time_point

With just a time, you've got a couple of options. But you should definitely not put "just a time" into a system_clock::time_point. The reason for this is that system_clock::time_point has a specific meaning: This measures the time since (or before) 1970-01-01 00:00:00 UTC, excluding leap seconds.

To be fair, this meaning is absent in C++11/14/17, and is only specified in C++20. However all implementations followed this convention in C++11/14/17.

You could store "time of day" in a duration such as microseconds. Then in your code you simply interpret this duration as time elapsed since midnight, where you get to define "midnight" (local midnight? UTC? etc.).

std::chrono::microseconds
makeTime(std::string s)
{
using namespace std;
using namespace std::chrono;
istringstream in{move(s)};
microseconds tp;
in >> date::parse("%T", tp);
return tp;
}

The above solution is simple, and that is good. But it is possible that this "time of day" could be accidentally mixed up with some other duration that doesn't mean "time of day" in a way that would cause logic errors in your program.

To make the above more type-safe, you could create a "time of day" clock:

struct time_of_day
{
using duration = std::chrono::microseconds;
using rep = duration::rep;
using period = duration::period;
using time_point = std::chrono::time_point<time_of_day>;
static constexpr bool is_steady = false;

static
time_point
now()
{
using namespace date;
using namespace std::chrono;
auto t_sys = floor<microseconds>(system_clock::now());
return time_point{t_sys - floor<days>(t_sys)};
};
};

time_of_day::time_point
makeTime(std::string s)
{
using namespace std;
using namespace std::chrono;
istringstream in{move(s)};
microseconds d;
in >> date::parse("%T", d);
return time_of_day::time_point{d};
}

Above I've defined midnight as UTC just to simplify the demonstration.

The time_of_day solution isn't necessarily better. The programmer should decide if the extra complexity cost justifies the type safety benefit.

Extract year/month/day etc. from std::chrono::time_point in C++

You can only extract this information from a system_clock::time_point. This is the only system-supplied clock that has a relationship with the civil calendar. Here is how to get the current time_point using this clock:

 system_clock::time_point now = system_clock::now();

You can then convert this to a time_t with:

time_t tt = system_clock::to_time_t(now);

Using the C library you can then convert a time_t to a tm, but you must choose whether you want that conversion to happen in the UTC timezone, or you local timezone:

tm utc_tm = *gmtime(&tt);
tm local_tm = *localtime(&tt);

Then you can print out the components of the tm, for example:

std::cout << local_tm.tm_year + 1900 << '\n';
std::cout << local_tm.tm_mon + 1 << '\n';
std::cout << local_tm.tm_mday << '\n';

Additionally

If you want, you can take advantage of this non-guaranteed information:

Every implementation of system_clock I'm aware of is based on unix time. I.e. the number of seconds since New Years 1970 UTC, neglecting leap seconds. And the precision of this count is usually finer than seconds. Here is a complete program which extracts all of this information:

#include <chrono>
#include <ctime>
#include <iostream>

int
main()
{
using namespace std;
using namespace std::chrono;
typedef duration<int, ratio_multiply<hours::period, ratio<24> >::type> days;
system_clock::time_point now = system_clock::now();
system_clock::duration tp = now.time_since_epoch();
days d = duration_cast<days>(tp);
tp -= d;
hours h = duration_cast<hours>(tp);
tp -= h;
minutes m = duration_cast<minutes>(tp);
tp -= m;
seconds s = duration_cast<seconds>(tp);
tp -= s;
std::cout << d.count() << "d " << h.count() << ':'
<< m.count() << ':' << s.count();
std::cout << " " << tp.count() << "["
<< system_clock::duration::period::num << '/'
<< system_clock::duration::period::den << "]\n";

time_t tt = system_clock::to_time_t(now);
tm utc_tm = *gmtime(&tt);
tm local_tm = *localtime(&tt);
std::cout << utc_tm.tm_year + 1900 << '-';
std::cout << utc_tm.tm_mon + 1 << '-';
std::cout << utc_tm.tm_mday << ' ';
std::cout << utc_tm.tm_hour << ':';
std::cout << utc_tm.tm_min << ':';
std::cout << utc_tm.tm_sec << '\n';
}

It is handy to create a custom duration to model days:

typedef duration<int, ratio_multiply<hours::period, ratio<24> >::type> days;

Now you can get the time since the epoch, to as fine a precision as it can manage, with:

system_clock::duration tp = now.time_since_epoch();

Then truncate it to days, and subtract that off.

Then truncate it to hours, and subtract that off.

Continue until you've subtracted off the seconds.

What you're left with is the fraction of a second with the units of system_clock::duration. So print out that run time value and the compile time units of that value as shown.

For me this program prints out:

15806d 20:31:14 598155[1/1000000]
2013-4-11 20:31:14

My output indicates the system_clock::duration precision is microseconds. If desired, that can be truncated to milliseconds with:

milliseconds ms = duration_cast<milliseconds>(tp);

Update

This header-only C++11/14 library encapsulates the work above, reducing client work down to:

#include "date.h"
#include <iostream>

int
main()
{
// Reduce verbosity but let you know what is in what namespace
namespace C = std::chrono;
namespace D = date;
namespace S = std;

auto tp = C::system_clock::now(); // tp is a C::system_clock::time_point
{
// Need to reach into namespace date for this streaming operator
using namespace date;
S::cout << tp << '\n';
}
auto dp = D::floor<D::days>(tp); // dp is a sys_days, which is a
// type alias for a C::time_point
auto ymd = D::year_month_day{dp};
auto time = D::make_time(C::duration_cast<C::milliseconds>(tp-dp));
S::cout << "year = " << ymd.year() << '\n';
S::cout << "month = " << ymd.month() << '\n';
S::cout << "day = " << ymd.day() << '\n';
S::cout << "hour = " << time.hours().count() << "h\n";
S::cout << "minute = " << time.minutes().count() << "min\n";
S::cout << "second = " << time.seconds().count() << "s\n";
S::cout << "millisecond = " << time.subseconds().count() << "ms\n";
}

Which just output for me:

2015-07-10 20:10:36.023017
year = 2015
month = Jul
day = 10
hour = 20h
minute = 10min
second = 36s
millisecond = 23ms

Another Update

This library grew into a C++ standards proposal and is now in the C++20 working draft. The syntax for extracting these fields from a system_clock::time_point in C++20 will be:

#include <chrono>

int
main()
{
using namespace std::chrono;
auto tp = system_clock::now();
auto dp = floor<days>(tp);
year_month_day ymd{dp};
hh_mm_ss time{floor<milliseconds>(tp-dp)};
auto y = ymd.year();
auto m = ymd.month();
auto d = ymd.day();
auto h = time.hours();
auto M = time.minutes();
auto s = time.seconds();
auto ms = time.subseconds();
}

The above assumes you want these fields in UTC. If you prefer them in some other time zone, that will also be possible. For example, here is how to do it in your computer's current local time zone:

#include <chrono>

int
main()
{
using namespace std::chrono;
auto tp = zoned_time{current_zone(), system_clock::now()}.get_local_time();
auto dp = floor<days>(tp);
year_month_day ymd{dp};
hh_mm_ss time{floor<milliseconds>(tp-dp)};
auto y = ymd.year();
auto m = ymd.month();
auto d = ymd.day();
auto h = time.hours();
auto M = time.minutes();
auto s = time.seconds();
auto ms = time.subseconds();
}

The only difference above is the construction of tp which now has type local_time as opposed to sys_time in the UTC example. Alternatively one could have picked an arbitrary time zone with this small change:

auto tp = zoned_time{"Europe/London", system_clock::now()}.get_local_time();

Error in comparing two std::chrono::time_point instances


t_exp is -4243023785

This value of time_t corresponds to 1835-07-18 22:16:55 (assuming the Unix epoch and a precision of seconds, neither of which are specified by the standard, but are common).

Apparently the implementation of ctime on your platform can't handle dates this far in the past, which is a little surprising as 1835 is not very far in the past.

The value of exp is -4243023785 times a million or a billion (depending on the precision of system_clock on your platform) and is stored with a signed 64 bit integer (there is no overflow). Thus time > exp == 1 is correct (time is 1590775919s converted to the precision of system_clock).

Sat Apr 26 01:39:43 4758 corresponds to a time_t of 87990716383.

I see nothing wrong with your use of the chrono library in the above code.

Update

The value 87990716383 is being converted to a time_point using from_time_t()

Ah, this combined with the knowledge that on your platform the precision of system_clock is nanoseconds tells me that you are experiencing overflow on the construction of exp.

This is not the code you have:

std::time_t t_exp = std::chrono::system_clock::to_time_t(exp);
std::time_t t_time = std::chrono::system_clock::to_time_t(time);
std::cout << std::ctime(&t_exp) << std::ctime(&t_time) << (time > exp) << std::endl;

The code you have looks something like:

// ...
std::time_t t_exp = 87990716383;
auto exp = std::chrono::system_clock::from_time_t(t_exp);
std::cout << std::ctime(&t_exp) << std::ctime(&t_time) << (time > exp) << std::endl;

On your platform, system_clock stores nanoseconds since 1970-01-01 00:00:00 UTC in a signed 64 bit integer. The maximum storable date (system_clock::time_point::max()) on your platform is:

2262-04-11 23:47:16.854775807

Beyond this, the underlying storage of nanoseconds overflows.

When 87990716383 (seconds) is converted in from_time_t, it is multiplied by a billion which overflows. The overflowed value is -4243003985547758080 which corresponds to the date 1835-07-19 03:46:54.452241920.

You can get a larger range by using a coarser precision, for example:

std::time_t t_exp = 87990716383;
time_point<system_clock, microseconds> exp{seconds{t_exp}};
// exp == 4758-04-26 01:39:43.000000

How to convert chrono::seconds to string in HH:MM:SS format in C++?

Using Howard Hinnant's header-only date.h library it looks like ths:

#include "date/date.h"
#include <string>

std::string
ElapsedTime(std::chrono::seconds secs)
{
return date::format("%T", secs);
}

If you want to write it yourself, then it looks more like:

#include <chrono>
#include <string>

std::string
ElapsedTime(std::chrono::seconds secs)
{
using namespace std;
using namespace std::chrono;
bool neg = secs < 0s;
if (neg)
secs = -secs;
auto h = duration_cast<hours>(secs);
secs -= h;
auto m = duration_cast<minutes>(secs);
secs -= m;
std::string result;
if (neg)
result.push_back('-');
if (h < 10h)
result.push_back('0');
result += to_string(h/1h);
result += ':';
if (m < 10min)
result.push_back('0');
result += to_string(m/1min);
result += ':';
if (secs < 10s)
result.push_back('0');
result += to_string(secs/1s);
return result;
}

In C++20, you'll be able to say:

std::string
ElapsedTime(std::chrono::seconds secs)
{
return std::format("{:%T}", secs);
}


Related Topics



Leave a reply



Submit