how to use localtime() correctly? - c++

In one of my utility programs, localtime() is used to covert unix timestamps to human readable date time.
The following code used to work in VS2010 while it fails to work in VS2019:
std::string sec = "1234123456";
int nsec = atoi(sec.c_str());
tm* t = localtime((time_t*)&nsec); // return null pointer
If I change the code in the following way, it will work also in VS2019:
std::string sec = "1234123456";
int nsec = atoi(sec.c_str());
time_t tt = nsec;
tm* t = localtime(&tt); // works
I have no idea why the additional int to time_t conversion is needed, any suggestion would be appreciated.

On most (if not all) modern compilers time_t is now a 64-bit integer. (time_t*)&nsec is therefore undefined behaviour as you are casting from one pointer type to a different one.
You fixed version is well defined but you will run into the reason that time_t is now 64-bit as 32-bit numbers will only work for times up to 2038 (assuming time_t is using the Unix epoch).
Unfortunately c++ doesn't provide a simple method for converting a string to time_t, to do it properly you'd need something like this:
#include <iostream>
#include <charconv>
time_t str_to_time_t(const std::string& str)
{
auto begin = str.c_str();
auto end = begin + str.size();
time_t time;
auto result = std::from_chars(begin, end, time);
if (result.ec != std::errc())
{
throw std::system_error(std::make_error_code(result.ec));
}
if (result.ptr != end)
{
throw std::invalid_argument("invalid time_t string");
}
return time;
}
int main()
{
std::string sec = "1234123456";
auto nsec = str_to_time_t(sec);
tm* t = localtime((time_t*)&nsec);
if (t)
{
std::cout << "parsed OK\n";
}
}

Related

C++ Convert a string timestamp to std::chrono::system_clock::time_point

I am trying to convert a string timestamp expressed in the following format: "28.08.2017 03:59:55.0007" to a std::chrono::system_clock::time_point by preserving the microseconds precision.
Is there any way to achieve this by using the standard library or boost?
Thanks.
I'd make use of Howard Hinnant's date library https://howardhinnant.github.io/date/date.html
e.g.:
std::stringstream str( "28.08.2017 03:59:55.0007" );
str.imbue( std::locale() );
std::chrono::time_point< std::chrono::system_clock, std::chrono::microseconds > result;
date::from_stream( str, "%d.%m.%Y %H:%M:%S", result );
std::cout << result.time_since_epoch().count();
Thought I'd add an answer since there isn't one available that exclusively uses the standard.
Given the input: istringstream timestamp("28.08.2017 03:59:55.0007"), this could be converted to a tm via get_time, but for the fractional seconds. The fractional seconds would need to be converted manually (this could be done by constructing chrono::microseconds from the rounded remainder divided by the micro ratio.) All this could be combined into something like this:
tm tmb;
double r;
timestamp >> get_time(&tmb, "%d.%m.%Y %T") >> r;
const auto output = chrono::time_point_cast<chrono::microseconds>(chrono::system_clock::from_time_t(mktime(&tmb))) + chrono::microseconds(lround(r * micro::den));
Live Example
One implementation can be:
#include <ctime>
#include <cmath>
#include <chrono>
#include <string>
#include <cstdint>
#include <stdexcept>
std::chrono::system_clock::time_point parse_my_timestamp(std::string const& timestamp) {
auto error = [&timestamp]() { throw std::invalid_argument("Invalid timestamp: " + timestamp); };
std::tm tm;
auto fraction = ::strptime(timestamp.c_str(), "%d.%m.%Y %H:%M:%S", &tm);
if(!fraction)
error();
std::chrono::nanoseconds ns(0);
if('.' == *fraction) {
++fraction;
char* fraction_end = 0;
std::chrono::nanoseconds fraction_value(std::strtoul(fraction, &fraction_end, 10));
if(fraction_end != timestamp.data() + timestamp.size())
error();
auto fraction_len = fraction_end - fraction;
if(fraction_len > 9)
error();
ns = fraction_value * static_cast<std::int32_t>(std::pow(10, 9 - fraction_len));
}
else if(fraction != timestamp.data() + timestamp.size())
error();
auto seconds_since_epoch = std::mktime(&tm); // Assumes timestamp is in localtime. For UTC use timegm.
auto timepoint_ns = std::chrono::system_clock::from_time_t(seconds_since_epoch) + ns;
return std::chrono::time_point_cast<std::chrono::system_clock::duration>(timepoint_ns);
}

How do I get this function to return a date-time-string

I have the following function that for the life of me I cannot get to return a string:
void GetDateTimeString()
{
auto t1 = std::time(nullptr);
auto tm1 = *std::localtime(&t1);
stringstream dattim1;
cout << put_time(&tm1, "%Y-%m-%d_%H-%M-%S");
}
I have tried this in vain, which crashes the program:
std::string GetDateTimeString()
{
time_t t1 = std::time(nullptr);
tm tm1 = *std::localtime(&t1);
stringstream dattim1;
dattim1 << put_time(&tm1, "%Y-%m-%d_%H-%M-%S");
std::string returnValue = dattim1.str();
return returnValue;
}
In the end I want to call it like this:
string dateString = GetDateTimeString();
What am I doing wrong?
The first version of your function is of type void so it does not return anything. cout will just print the time e.g. to the console.
In the second function you try to use put_time again, but that is the wrong function for your demand. instead use strftime to copy the time to a char-array and then to a string:
std::string GetDateTimeString()
{
time_t t1 = std::time(nullptr);
tm tm1 = *std::localtime(&t1);
char buffer[80];
strftime(buffer, sizeof(buffer), "%Y-%m-%d_%H-%M-%S", &tm1);
std::string returnValue(buffer);
return returnValue;
}
Since the code works in a simple test, but crashes in your application, I would suggest you look at using localtime_s or localtime_r (system dependent) instead of localtime, which returns a pointer to a static buffer, and is not threadsafe.

C++ Converting A String to a time_t variable

I'm working on a C++ function that is supposed to figure out if a specified event happened between two time points. The event name, start datetime, and end datetime are all passed in from Lua as strings. Because of that, I need to parse my datetime strings into time_t variables. Based on what I've seen on StackOverflow and other forums, this code should work:
time_t tStart;
int yy, month, dd, hh, mm, ss;
struct tm whenStart = {0};
const char *zStart = startTime.c_str();
sscanf(zStart, "%d/%d/%d %d:%d:%d", &yy, &month, &dd, &hh, &mm, &ss);
whenStart.tm_year = yy - 1900;
whenStart.tm_mon = month - 1;
whenStart.tm_mday = dd;
whenStart.tm_hour = hh;
whenStart.tm_min = mm;
whenStart.tm_sec = ss;
whenStart.tm_isdst = -1;
tStart = mktime(&whenStart);
However, tStart appears to be assigned the value of -1 here. If I use strftime to reconstruct a string from whenStart, that tm structure appears to have been made completely correctly. Somehow mktime() is not liking the structure, I think. What is wrong with this code?
Also, before you answer, know that I already tried using a strptime() call. For reasons that are unclear to me, this function gets rejected with an "undefined reference to 'strptime'" error. The various descriptions I've found for how to fix this problem only serve to destroy the rest of the code base I'm working with, so I would rather avoid messing with _XOPEN_SOURCE or similar redefinitions.
Thanks for your help!
The code you posted is correct.
This leads me to believe that your input string (startTime) is not in the format you are expecting, and therefore sscanf cannot parse out the values.
Example:
#include <iostream>
int main()
{
std::string startTime = "2016/05/18 13:10:00";
time_t tStart;
int yy, month, dd, hh, mm, ss;
struct tm whenStart;
const char *zStart = startTime.c_str();
sscanf(zStart, "%d/%d/%d %d:%d:%d", &yy, &month, &dd, &hh, &mm, &ss);
whenStart.tm_year = yy - 1900;
whenStart.tm_mon = month - 1;
whenStart.tm_mday = dd;
whenStart.tm_hour = hh;
whenStart.tm_min = mm;
whenStart.tm_sec = ss;
whenStart.tm_isdst = -1;
tStart = mktime(&whenStart);
std::cout << tStart << std::endl;
}
Output:
1463595000
Have you sanity checked your inputs?
Please note that you can check the return value of sscanf to verify if it worked as you expected.
Return value
Number of receiving arguments successfully assigned, or EOF if read failure occurs before the first receiving argument was assigned.
If the return value is not 6, then the input string is incorrect.
int num_args = sscanf(zStart, "%d/%d/%d %d:%d:%d", &yy, &month, &dd, &hh, &mm, &ss);
if (num_args != 6)
{
std::cout << "error in format string " << startTime << '\n';
return 1;
}
As a rule of thumb you shouldn't ever assume that your inputs will be correct. As such, defensive programming is a good habit to get into.

Milliseconds since epoch to dateformat C++

I have the milliseconds since epoch (windows/gregorian) for a specific time in long long int and would like to convert it to human time such as yy-mm-dd-hh-mm-ss-milli. (My platform: Windows 7, 64bit)
Unfortunately, all solutions I have found so far can't deal with the milli second (long long int) part.
C++11 API is incomplete, so I had to invent a bicycle:
static long getTs() {
struct timeval tp;
gettimeofday(&tp, NULL);
long int ms = tp.tv_sec * 1000 + tp.tv_usec / 1000;
return ms;
}
static string format(long ts,string fmt,int cutBack=0){
time_t tt = ts/1000;
int microsec = ts%1000;
struct std::tm * ptm = std::localtime(&tt);
string fmtms=std::regex_replace(fmt, std::regex("%ms"), to_string(microsec));
std::stringstream ss;
ss << std::put_time(ptm, fmtms.c_str());
string ret = ss.str();
return ret.substr(0,ret.size()-cutBack);
}
std::cout << CommonUtils::format(CommonUtils::getTs(), "%Y-%m-%dT%H:%M:%S.%ms%Z")<<endl;
gives me: 2020-01-24T11:55:14.375+07, cutBack parameter is optional, it specifies how many characters to remove from the output string. It is useful when timezone format like +0700 is to long, and you just need +07.
Basically, you should be able to take whatever it is that you have that writes the formatted time without milliseconds, and add the remainder of the division of the number of millisconds by 1000. This should work because leap time is always an integer number of seconds.
Assuming C++11, you can try this:
#include <chrono>
#include <iomanip>
using namespace std;
using namespace chrono;
long long int milliSecondsSinceEpoch = ... // this is your starting point
const auto durationSinceEpoch = std::chrono::milliseconds(milliSecondsSinceEpoch);
const time_point<system_clock> tp_after_duration(durationSinceEpoch);
time_t time_after_duration = system_clock::to_time_t(tp_after_duration);
std::tm* formattedTime = std::localtime(&time_after_duration);
long long int milliseconds_remainder = milliSecondsSinceEpoch % 1000;
cout <<put_time(std::localtime(&time_after_duration), "%y-%m-%d-%H-%M-%S-") << milliseconds_remainder << endl;

Convert string datetime in C++

I have a date represented as string in the format "2012-10-28" and I want to convert it in the string format of "28/10/2012". Is this possible in C++ MS Visual Studio using a predefined function ?
This will do it:
#include <cstdio>
#include <iostream>
#include <string>
using namespace std;
string format_date(string s)
{
char buf[11];
int a, b, c;
sscanf(s.c_str(), "%d-%d-%d", &a, &b, &c);
sprintf(buf, "%02d/%02d/%d", c, b, a);
return buf;
}
int main()
{
cout << format_date("2012-09-28") << endl;
}
I worked it out that way:
Use sscan_f to break date into year, month and day.
Create struct tm with the data above.
Use strftime to convert from tm to string with the desired format.
Please look at COleDateTime::ParseDateTime.
If do not want to use COleDateTime the implementation of the ParseDateTime is just a thin wrapper around VarDateFromStr.
strptime unfortunately does not exist in windows. Seek help here: strptime() equivalent on Windows?
You can then write the date using strftime.
in Qt (some embedded system does not support new timer class yet, so here)
I here just give the idea how to convert a string without much mumbo jumbo.
the timer class has the epoch function anyway.
QString fromSecsSinceEpoch(qint64 epoch)
{
QTextStream ts;
time_t result = epoch;//std::time(NULL);
//std::cout << std::asctime(std::localtime(&result))
// << result << " seconds since the Epoch\n";
ts << asctime(gmtime(&result));
return ts.readAll();
}
qint64 toSecsSinceEpoch(QString sDate)//Mon Nov 25 00:45:23 2013
{
QHash <QString,int> monthNames;
monthNames.insert("Jan",0);
monthNames.insert("Feb",1);
monthNames.insert("Mar",2);
monthNames.insert("Apr",3);
monthNames.insert("May",4);
monthNames.insert("Jun",5);
monthNames.insert("Jul",6);
monthNames.insert("Aug",7);
monthNames.insert("Sep",8);
monthNames.insert("Oct",9);
monthNames.insert("Nov",10);
monthNames.insert("Dec",11);
QStringList l_date = sDate.split(" ");
if (l_date.count() != 5)
{
return 0;//has to be 5 cuz Mon Nov 25 00:45:23 2013
}
QStringList l_time = l_date[3].split(":");
if (l_time.count() != 3)
{
return 0;//has to be 3 cuz 00:45:23
}
struct tm result;
result.tm_mday=l_date[2].toInt();
result.tm_mon=monthNames[l_date[1]];
result.tm_year=l_date[4].toInt()-1900;;
result.tm_hour=l_time[0].toInt();
result.tm_min=l_time[1].toInt();
result.tm_sec=l_time[2].toInt();
time_t timeEpoch=mktime(&result);
qDebug()<<"epochhhh :"<<timeEpoch;
return timeEpoch;
}