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.
Related
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";
}
}
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.
Here's the thing:
A datetime access is created with C# using DateTime.Now. This datetime is passed through JSON to a C++ method. I'm using JsonCpp to handle the Json data, but I'm not sure how to handle when the data is a datetime.
I want to compare this datetime that I received with the actual datetime and check the minutes difference between this two (if the difference is on a interval that was defined).
If I convert the Json datetime to a string using JsonCpp I have this format:
2015-06-08T11:17:23.746389-03:00
So what I'm trying to do is something like this:
var d1 = oldAccess["Date"].ToString(); //Json datetime converted to a string
var d2 = actualAccess["Date"].ToString()
if((d2 - d1) < 20) { //Difference between the two dates needs to be less than 20 minutes
return true;
} else return false;
I'm new in C++, even looking for I don't discovered how to do this.
Well, I got it. Not the best way neither the pretty one, but it works since I know that the two dates were set on the same server and always comes in the same format \"2015-01-01T23:40:00.000000-03:00\"
Here's what I did:
int convertToInt(std::string number_str){
int number;
std::istringstream ss(number_str);
ss.imbue(std::locale::classic());
ss >> number;
return number;
}
time_t convertDatetime(std::string date_str) {
time_t rawtime;
struct tm date;
int year, month, day, hour, min, sec;
date_str.erase(std::remove_if(date_str.begin(), date_str.end(), isspace), date_str.end());
year = convertToInt(date_str.substr(1, 4));
month = convertToInt(date_str.substr(6, 2));
day = convertToInt(date_str.substr(9, 2));
hour = convertToInt(date_str.substr(12, 2));
min = convertToInt(date_str.substr(15, 2));
sec = convertToInt(date_str.substr(18, 2));
time(&rawtime);
localtime_s(&date, &rawtime);
date.tm_year = year - 1900;
date.tm_mon = month - 1;
date.tm_mday = day;
date.tm_hour = hour;
date.tm_min = min;
date.tm_sec = sec;
return mktime(&date);
}
bool isValidIntervalDatetime(std::string actualDatetime_str, std::string oldDatetime_str, int maxMinutesInterval) {
double maxSecondsInterval = 60 * maxMinutesInterval;
time_t actualDatetime = convertDatetime(actualDatetime_str);
time_t oldDatetime = convertDatetime(oldDatetime_str);
double secondsDiff = difftime(actualDatetime, oldDatetime);
return secondsDiff <= maxSecondsInterval;
}
int main(int argc, char* argv[])
{
auto maxMinutesInterval = 20;
auto actuaDatetime = JsonConverter::toString(actualAccess["Date"]); // \"2015-01-02T00:00:00.000000-03:00\"
auto oldDatetime = JsonConverter::toString(oldAccess["Date"]); // \"2015-01-01T23:40:00.000000-03:00\"
if (isValidIntervalDatetime(actuaDatetime, oldDatetime, maxMinutesInterval){
//do something
}
}
I am writing a application which needs the possibility to compare two dates. This is what I have so far:
struct entry {
string text;
string date; // format: dd.mm.yyyy
bool finished;
};
string addNulls(int number, int cols) {
string num = to_string(number);
if (num.size() < cols) {
int count = cols - num.size();
for (int i = 0; i < count; i++) {
num = "0" + num;
}
}
return num;
}
// [...]
entry e = {"here is some text", "21.03.2019", false};
int day2 = atoi(e.date.substr(0, 2).c_str());
int month2 = atoi(e.date.substr(3, 2).c_str());
int year2 = atoi(e.date.substr(6, 4).c_str());
time_t t = time(0);
struct tm * now = localtime(&t);
string date1 = e.date.substr(6, 4) + "-" + e.date.substr(3, 2) + "-" + e.date.substr(0, 2) + " 00:00:00";
string date2 = addNulls(now->tm_year, 4) + "-" + addNulls(now->tm_mon, 2) + "-" + addNulls(now->tm_mday, 2) + " 00:00:00";
if(date2 > date1) {
// do something
}
the code gets an "entry" struct which contains a date. Than the code compares the date with the actual time. The problem is, it does not work! I run some tests with some example content, but the result (date2 > date1) returns false.
Why?
I read this: C++ compare to string dates
I'm not actually answering your question. However I am offering you a solution. Have you considered a date/time library? Boost datetime is very popular.
If you are compiling in C++11 or later, I recommend this date time library, as it is header-only (eliminating the need to link to a library such as boost), and in my opinion, it has cleaner syntax (that is a very subjective and biased viewpoint).
This latter library builds on the C++11 <chrono> library. Here is your example code using this library:
#include "date.h"
#include <iostream>
#include <string>
struct entry {
std::string text;
date::year_month_day date;
bool finished;
};
int
main()
{
entry e = {"here is some text", date::day(21)/3/2019, false};
auto day2 = e.date.day();
auto month2 = e.date.month();
auto year2 = e.date.year();
auto t = std::chrono::system_clock::now();
auto date1 = date::sys_days{e.date};
auto date2 = t;
if (date2 > date1)
std::cout << "It is past " << e.date << '\n';
else
std::cout << "It is not past " << e.date << '\n';
}
Which currently outputs:
It is not past 2019-03-21
In C++14, the chrono literals make specifying literal times very compact:
using namespace std::literals;
auto date1 = date::sys_days{e.date} + 0h + 0min + 0s;
Also on the subject of literals, you can make the construction of entry slightly more compact if you drop in a using namespace date;:
entry e = {"here is some text", 21_d/3/2019, false};
Reusing a date or datetime class, or even creating your own, is easier than trying to use a string to hold a date. Additionally you get the type-safety of not accidentally adding a string to a date, when you meant to add a time duration to a time point.
Why don't you use strptime to parse your date strings, convert them to epoch times and then compare?
#include <time.h>
char *
strptime(const char *restrict buf, const char *restrict format,
struct tm *restrict tm);
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
How do I convert a date string, formatted as "MM-DD-YY HH:MM:SS", to a time_t value in either C or C++?
Use strptime() to parse the time into a struct tm, then use mktime() to convert to a time_t.
In the absence of strptime you could use sscanf to parse the data into a struct tm and then call mktime. Not the most elegant solution but it would work.
Boost's date time library should help; in particular you might want to look at http://www.boost.org/doc/libs/1_37_0/doc/html/date_time/date_time_io.html
Note that strptime mentioned in accepted answer is not portable. Here's handy C++11 code I use to convert string to std::time_t :
static std::time_t to_time_t(const std::string& str, bool is_dst = false, const std::string& format = "%Y-%b-%d %H:%M:%S")
{
std::tm t = {0};
t.tm_isdst = is_dst ? 1 : 0;
std::istringstream ss(str);
ss >> std::get_time(&t, format.c_str());
return mktime(&t);
}
You can call it like this:
std::time_t t = to_time_t("2018-February-12 23:12:34");
You can find string format parameters here.
I'm afraid there isn't any in Standard C / C++ . There is the POSIX function strptime which can convert to struct tm, which can then be converted to time_t using mktime.
If you are aiming for cross platform compatibility, better use boost::date_time, which has sophisticated functions for this.
best way to convert a date string, formatted as "MM-DD-YY HH:MM:SS", to a time_t
Restricting code to standard C library functions is looking for the inverse of strftime(). To expand #Rob general idea, uses sscanf().
Use "%n" to detect completed scan
time_t date_string_to_time(const char *date) {
struct tm tm = { 0 }; // Important, initialize all members
int n = 0;
sscanf(date, "%d-%d-%d %d:%d:%d %n", &tm.tm_mon, &tm.tm_mday, &tm.tm_year,
&tm.tm_hour, &tm.tm_min, &tm.tm_sec, &n);
// If scan did not completely succeed or extra junk
if (n == 0 || date[n]) {
return (time_t) -1;
}
tm.tm_isdst = -1; // Assume local daylight setting per date/time
tm.tm_mon--; // Months since January
// Assume 2 digit year if in the range 2000-2099, else assume year as given
if (tm.tm_year >= 0 && tm.tm_year < 100) {
tm.tm_year += 2000;
}
tm.tm_year -= 1900; // Years since 1900
time_t t = mktime(&tm);
return t;
}
Additional code could be used to insure only 2 digit timestamp parts, positive values, spacing, etc.
Note: this assume the "MM-DD-YY HH:MM:SS" is a local time.
static time_t MKTimestamp(int year, int month, int day, int hour, int min, int sec)
{
time_t rawtime;
struct tm * timeinfo;
time ( &rawtime );
timeinfo = gmtime ( &rawtime );
timeinfo->tm_year = year-1900 ;
timeinfo->tm_mon = month-1;
timeinfo->tm_mday = day;
timeinfo->tm_hour = hour;
timeinfo->tm_min = min;
timeinfo->tm_sec = sec;
timeinfo->tm_isdst = 0; // disable daylight saving time
time_t ret = mktime ( timeinfo );
return ret;
}
static time_t GetDateTime(const std::string pstr)
{
try
{
// yyyy-mm-dd
int m, d, y, h, min;
std::istringstream istr (pstr);
istr >> y;
istr.ignore();
istr >> m;
istr.ignore();
istr >> d;
istr.ignore();
istr >> h;
istr.ignore();
istr >> min;
time_t t;
t=MKTimestamp(y,m,d,h-1,min,0);
return t;
}
catch(...)
{
}
}