I am on a custom board that is running qnx. I was using the ctime function to convert a std::time_point to a human readable time.
This is the function:
auto TimePointAsString(const std::chrono::system_clock::time_point& tp) -> std::string
{
std::time_t t{ std::chrono::system_clock::to_time_t(tp) }; // Convert time_point to time_t.
LOG("tp = {}", t);
std::string ts{ std::ctime(&t) }; // Convert time_t to string.
ts.resize(ts.size() - 1); // Remove CR at end of string.
return ts;
}
It would output the expected time and then every so often, it would spit out something like Tue Jun 1 00:00:28 2021
even when the time_point was the exact same value as it was the last time it spit out the expected time.
This is worrying as the only thing I could think of that could cause that is something repeatedly but at random time hitting memory related to the std::ctime
function call.
Is there any other explanation that would cause this?
The logging I'm getting:
2021/06/01 00:00:17.191150: tp = 1683495362
2021/06/01 00:00:17.191150: log point 2: Sun May 7 21:36:02 2023
2021/06/01 00:00:17.191150: tp = 1683495363
2021/06/01 00:00:17.192150: log point 1: Sun May 1 00:00:17 2021
2021/06/01 00:00:18.091661: tp = 1683495363
2021/06/01 00:00:18.091661: log point 2: Sun May 7 21:36:03 2023
2021/06/01 00:00:18.092661: tp = 1683495364
2021/06/01 00:00:18.092661: log point 1: Tue Jun 1 00:00:18 2021
2021/06/01 00:00:18.391935: tp = 1683495363
2021/06/01 00:00:18.391935: log point 2: Sun May 7 21:36:03 2023
2021/06/01 00:00:18.391935: tp = 1683495364
2021/06/01 00:00:18.392935: log point 1: Tue Jun 1 00:00:18 2021
2021/06/01 00:00:21.992204: tp = 1683495365
2021/06/01 00:00:21.993205: log point 2: Sun May 7 21:36:05 2023
2021/06/01 00:00:21.993205: tp = 1683495368
2021/06/01 00:00:21.993205: log point 1: Tue Jun 1 00:00:21 2021
2021/06/01 00:00:22.592505: tp = 1683495365
2021/06/01 00:00:22.592505: log point 2: Sun May 7 21:36:05 2023
2021/06/01 00:00:22.592505: tp = 1683495368
2021/06/01 00:00:22.593505: log point 1: Tue Jun 1 00:00:22 2021
2021/06/01 00:00:23.492990: tp = 1683495366
2021/06/01 00:00:23.492990: log point 2: Sun May 7 21:36:06 2023
2021/06/01 00:00:23.493990: tp = 1683495369
2021/06/01 00:00:23.493990: log point 1: Tue Jun 1 00:00:23 2021
2021/06/01 00:00:23.593116: tp = 1683495366
2021/06/01 00:00:23.594116: log point 2: Sun May 7 21:36:06 2023
2021/06/01 00:00:23.594116: tp = 1683495369
2021/06/01 00:00:23.594116: log point 1: Tue Jun 1 00:00:23 2021
The expected time has a year of 2023. In the log, that is what is prevalent. I only showed the logged time just previous to each 2021 time and is why you're seeing an alternating 2023/2021.
Interestingly, the logging system is showing a 2021 year. I'm not familiar with the logging system used, but this could be a hint. Although the 2021 times coincide with the log times, the dates are not always in agreement. One shows Sun May 1
and another shows Tue Jun 1
.
Anyone see anything like this before? Any ideas as to what could be causing this?
FYI: It is not related to this: C++ <ctime> printing wrong date
Here is a free, open-source, header-only alternative that works with C++14:
#include "date/date.h"
auto TimePointAsString(const std::chrono::system_clock::time_point& tp) -> std::string
{
return date::format("%a %b %e %T %Y", date::floor<std::chrono::seconds>(tp));
}
This is thread-safe and efficient, and it returns the same format as in your question. And it returns the expected string for all of your values.
You may get better results by defining this configuration macro when compiling:
-DONLY_C_LOCALE
This tells date.h
to not use your C++ lib's std::locale
when forming the day and month names, and instead uses its own code, but is restricted to the "C" locale. On an embedded system I'm not sure how much C++ locale
support is available to you.
The above does use std::ostringstream
under the covers which can have overhead. Additionally parsing the formatting string also takes time. If performance is the paramount concern, you can drop down to a lower level and do the formatting manually like this:
// Convert system_clock::time_point to string of the form:
// Fri Dec 1 21:36:02 2023
// in UTC
//
// Returns the string
// Throws on allocation error for string memory (24 bytes)
// Hardwires "C" locale for weekday and month abbreviations
// Requires year is in the range [0000, 9999]
// Assumes proleptic Gregorian calendar
auto
TimePointAsString(std::chrono::system_clock::time_point const& tp)
-> std::string
{
// Names for formatting
char constexpr wd[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
char constexpr mn[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
// Convert i to string form. s points to the last (least significant) digit
auto stampd = [](char* s, unsigned i)
{
do
{
*s-- = char(i % 10 + '0');
i /= 10;
} while (i > 0);
};
// memcpy 3 char starting at s and c from c to s
auto stamp3 = [](char* s, char const* c)
{
for (auto i = 0; i < 3; ++i)
*s++ = *c++;
};
// Truncate date_time to seconds precision
auto const tps = date::floor<std::chrono::seconds>(tp);
// Extract date from date_time
auto const tpd = date::floor<date::days>(tps);
// Extract time of day from date_time and date,
// and convert time to {h, m, s} form
date::hh_mm_ss<std::chrono::seconds> const tod{tps - tpd};
// Convert date to {y, m, d} form
date::year_month_day const ymd = tpd;
// format {y, m, d}, {h, m, s} as desired
// 012345678901234567890123
std::string r = "www mmm d 00:00:00 000y";
auto const s = &r[0];
stamp3(s+0, wd[date::weekday{tpd}.c_encoding()]);
stamp3(s+4, mn[unsigned{ymd.month()}-1]);
stampd(s+9, unsigned{ymd.day()});
stampd(s+12, tod.hours().count());
stampd(s+15, tod.minutes().count());
stampd(s+18, tod.seconds().count());
stampd(s+23, int{ymd.year()});
return r;
}
And nothing will get faster than that. The main expense is allocating the memory for the string
. The computation of turning the count of seconds into numeric date and time fields is practically free in comparison to the allocation.