I'm currently facing a weird issue where the same function outputs a different result. The function is supposed to calculate the time difference between a provided date and the current time. Since this function is supposed to work with milliseconds, my function currently looks like this:
int calcDelay(std::string dropTime) {
struct tm tm;
std::istringstream iss(dropTime);
iss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S");
time_t time = mktime(&tm);
SYSTEMTIME t;
GetSystemTime(&t);
struct tm tm1;
memset(&tm1, 0, sizeof(tm1));
tm1.tm_year = t.wYear - 1900;
tm1.tm_mon = t.wMonth - 1;
tm1.tm_mday = t.wDay;
tm1.tm_hour = t.wHour - 1;
tm1.tm_min = t.wMinute;
tm1.tm_sec = t.wSecond;
time_t time2 = mktime(&tm1);
//std::cout << "Input:" << dropTime << " Output:" << (int)(difftime(time, time2) * 1000) - t.wMilliseconds << std::endl;
int retVal = (int)(difftime(time, time2) * 1000) - t.wMilliseconds;
return retVal;
}
The provided date (dropTime) is in UTC/GMT and the WinAPI function GetSystemTime should also return the time in UTC.
I have two different threads that call this function. When the first thread calls this function, it returns the correct time difference. However, when my other thread calls this function with the exactly same input it returns a value that is exactly 3600000 ms larger - this equals the time of exactly one hour.
What's the cause of this bug?
Edit: It seems that the bug is caused by the get_time function. Even though the same string (2021-05-25T21:03:04) is used to parse the time, it sometimes adds a hour and sometimes it doesn't...
Could it be that the get_time function simply cannot be used across multiple threads?
I appreciate all help.
In C++20 your calcDelay can be greatly simplified. And there exists a preview of this functionality in a free, open-source, header-only library1 which works with C++11/14/17.
#include "date/date.h"
#include <chrono>
#include <sstream>
int calcDelay(std::string dropTime) {
using std::chrono::milliseconds;
date::sys_time<milliseconds> time;
std::istringstream iss(dropTime);
iss >> date::parse("%Y-%m-%dT%H:%M:%S", time);
auto time2 = date::floor<milliseconds>(std::chrono::system_clock::now());
return (time - time2).count();
}
As you state in your question, the input is UTC, and the current time is UTC. Time zones are not involved. And unlike the "C version", this version optionally supports millisecond-precision input:
std::cout << calcDelay("2021-05-26T00:41:01.568") << '\n';
Output:
12456
To port the above calcDelay to C++20:
Drop #include "date/date.h"
Change date:: to std::chrono:: (3 places)
You can also (optionally) simplify the parse string from "%Y-%m-%dT%H:%M:%S" to "%FT%T".
Also optional, you could increase type safety in the client code by returning std::chrono::milliseconds instead of int.
1 Full disclosure: I am the lead author of this library. I am not pursuing any financial gain from this effort. But sometimes people get upset if I don't fully disclose this information.
t.wHour - 1 is incorrect. Both the tm and SYSTEMTIME structures use hours from 0...23.
According to std::get_time API, The I/O manipulator std::get_time uses the std::time_get facet of the I/O stream's locale to convert text input to a std::tm object. And maybe all of your threads which are in the same process have the same native locale which is default behavior. So GetSystemTime(&t); has no problem.
The follwing code is API’s example:
#include <iostream>
#include <sstream>
#include <locale>
#include <iomanip>
int main()
{
std::tm t = {};
std::istringstream ss("2011-Februar-18 23:12:34");
ss.imbue(std::locale("de_DE.utf-8"));
ss >> std::get_time(&t, "%Y-%b-%d %H:%M:%S");
if (ss.fail()) {
std::cout << "Parse failed\n";
} else {
std::cout << std::put_time(&t, "%c") << '\n';
}
}
Related
I managed to convert a time point into an integer and write it into a file using code that looks like the following code:
std::ofstream outputf("data");
std::chrono::time_point<std::chrono::system_clock> dateTime;
dateTime = std::chrono::system_clock::now();
auto dateTimeSeconds = std::chrono::time_point_cast<std::chrono::seconds>(toSerialize->dateTime);
unsigned long long int serializeDateTime = toSerialize->dateTime.time_since_epoch().count();
outputf << serializeDateTime << "\n";
Now I'm trying to read that integer from the file, convert it into a time_point, and print it. Right now, my code looks something like this:
std::ifstream inputf("data");
unsigned long long int epochDateTime;
inputf >> epochDateTime;
std::chrono::seconds durationDateTime(epochDateTime);
std::chrono::time_point<std::chrono::system_clock> dateTime2(durationDateTime);
std::time_t tt = std::chrono::system_clock::to_time_t(dateTime2);
char timeString[30];
ctime_s(timeString, sizeof(timeString), &tt);
std::cout << timeString;
However, it doesn't print anything. Does anyone know where I went wrong?
You have some strange conversions and assign to a variable that you don't use. If you want to store system_clock::time_points as std::time_ts and restore the time_points from those, don't involve other types and use the functions made for this: to_time_t and from_time_t. Also, check that opening the file and that extraction from the file works.
Example:
#include <chrono>
#include <ctime>
#include <fstream>
#include <iostream>
int main() {
{ // save a time_point as a time_t
std::ofstream outputf("data");
if(outputf) {
std::chrono::time_point<std::chrono::system_clock> dateTime;
dateTime = std::chrono::system_clock::now();
outputf << std::chrono::system_clock::to_time_t(dateTime) << '\n';
}
}
{ // restore the time_point from a time_t
std::ifstream inputf("data");
if(inputf) {
std::time_t epochDateTime;
if(inputf >> epochDateTime) {
// use epochDateTime with ctime-like functions if you want:
std::cout << std::ctime(&epochDateTime) << '\n';
// get the time_point back (usually rounded to whole seconds):
auto dateTime = std::chrono::system_clock::from_time_t(epochDateTime);
// ...
}
}
}
}
Putting aside the possibility of the wrong date value, the problem here is with sizeof(timeString). It appears that you think it is 30, but it in fact is the size of the char*, likely 8 (or maybe 4).
According to ctime_s:
the following errors are detected at runtime and call the currently installed constraint handler function:
buf or timer is a null pointer
bufsz is less than 26 or greater than RSIZE_MAX
I want to be able to put into a string the local time and date with millisecond resolution like so:
YYYY-MM-DD hh:mm:ss.sss
Seems like a simple thing to do, but I haven't found a simple answer for how to do this. I am writing in C++ and do have access to 11 compiler but am fine using a C solution if it's cleaner. I found a post here with a solution Get both date and time in milliseconds but surely it can't be that difficult given use of standard libraries. I'm probably going to move forward with that type of solution but was hoping to add to the knowledge base by asking the question here on SO.
I know this will work but again, seems unnecessarily difficult:
#include <sys/time.h>
#include <stdio.h>
int main(void)
{
string sTimestamp;
char acTimestamp[256];
struct timeval tv;
struct tm *tm;
gettimeofday(&tv, NULL);
tm = localtime(&tv.tv_sec);
sprintf(acTimestamp, "%04d-%02d-%02d %02d:%02d:%02d.%03d\n",
tm->tm_year + 1900,
tm->tm_mon + 1,
tm->tm_mday,
tm->tm_hour,
tm->tm_min,
tm->tm_sec,
(int) (tv.tv_usec / 1000)
);
sTimestamp = acTimestamp;
cout << sTimestamp << endl;
return 0;
}
Tried looking at put_time for C++ and strftime for the old C way. Both only allow me to get to second resolution best I can tell. You can see the two approaches I've gotten so far below. I would like to put it into a string
auto t = std::time(nullptr);
auto tm = *std::localtime(&t);
std::cout << std::put_time(&tm, "%Y-%m-%d %H:%M:%S") << std::endl;
time_t rawtime;
struct tm * timeinfo;
char buffer[80];
time (&rawtime);
timeinfo = localtime(&rawtime);
strftime(buffer,sizeof(buffer),"%Y-%m-%d %I:%M:%S",timeinfo);
std::string str(buffer);
std::cout << str;
Only thing I can figure out is to use gettimeofday and get rid of all the data except the last second and append it to the timestamp, still wish there was a cleaner approach.
Anyone find a solution that works better?
I would recommend looking at Howard Hinnant's date library. One of the examples given in the wiki shows how to get the current local time, up to the given precision of your std::chrono::system_clock implementation (nanoseconds on Linux, from memory?):
EDIT: As Howard points out in the comments, you can use date::floor() to obtain the desired precision. So to generate a string as requested in the question, you could do something like this:
#include "tz.h"
#include <iostream>
#include <string>
#include <sstream>
std::string current_time()
{
const auto now_ms = date::floor<std::chrono::milliseconds>(std::chrono::system_clock::now());
std::stringstream ss;
ss << date::make_zoned(date::current_zone(), now_ms);
return ss.str();
}
int main()
{
std::cout << current_time() << '\n';
}
I want to convert a string in the format of "20160907-05:00:54.123" into milliseconds.
I know that strptime is not available in Windows and I want to run my program in both windows and linux. I can't use third party libraries as well.
I can tokenize the string and convert it. But is there a more elegant way like using the strptime to do so?
What about std::sscanf?
#include <iostream>
#include <cstring>
int main() {
const char *str_time = "20160907-05:00:54.123";
unsigned int year, month, day, hour, minute, second, miliseconds;
if (std::sscanf(str_time, "%4u%2u%2u-%2u:%2u:%2u.%3u", &year, &month,
&day, &hour, &minute, &second,&miliseconds) != 7)
{
std::cout << "Parse failed" << std::endl;
}
else
{
std::cout << year << month << day << "-" << hour << ":"
<< minute << ":" << second << "." << miliseconds
<< std::endl;
}
}
Output (ideone):
201697-5:0:54.123.
However, you should make sure the input is valid (for example, day can be in the range of [0,99]).
Too bad about no 3rd party libraries, because here is one (MIT license) that is just a single header, runs on linux and Windows, and handles the milliseconds seamlessly:
#include "date.h"
#include <iostream>
#include <sstream>
int
main()
{
date::sys_time<std::chrono::milliseconds> tp;
std::istringstream in{"20160907-05:00:54.123"};
date::parse(in, "%Y%m%d-%T", tp);
std::cout << tp.time_since_epoch().count() << '\n';
}
This outputs:
1473224454123
Error checking is done for you. The stream will fail() if the date is invalid.
date::sys_time<std::chrono::milliseconds> is a type alias for std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds>. I.e. it is from the family of system_clock::time_point, just milliseconds precision.
Fully documented:
https://howardhinnant.github.io/date/date.html
Doesn't get much more elegant than this.
Given the format of your string, it is fairly easy to parse it as follows (although a regex or get_time might be more elegant):
tm t;
t.tm_year = stoi(s.substr(0, 4));
t.tm_mon = stoi(s.substr(4, 2));
t.tm_mday = stoi(s.substr(6, 2));
t.tm_hour = stoi(s.substr(9, 2));
t.tm_min = stoi(s.substr(12, 2));
t.tm_sec = 0;
double sec = stod(s.substr(15));
Finding the time since the epoch can be done with mktime:
mktime(&t) + sec * 1000
Note that the fractional seconds need to be handled differently - unfortunately, tm has only integer seconds.
(See the full code here.)
Edit
As Mine and Panagiotis Kanavos correctly note in the comments, Visual C++ apparently supports get_time for quite a while, and it's much shorter with it (note that the fractional seconds need to be handled the same way, though).
How to get the current DateTime in C++ like in .NET (DateTime.Now) in this format : 20/10/2014 10:53:27 ?
I have found this library, but this cannot perform my vows.
ny brilliant idea, please ?
I guess you need std::time function.
Edit:
Here's example how to output current time in asked format:
#include <ctime>
#include <iostream>
int main()
{
std::time_t t = std::time(NULL);
char mbstr[100];
if (std::strftime(mbstr, 100, "%d/%m/%Y %T", std::localtime(&t))) {
std::cout << mbstr << '\n';
}
}
I use this in my Project, hope it helps
time_t t;
t = time(NULL);
tm tlm;
localtime_s(&tlm, &t);
cout << tlm.tm_hour << tlm.tm_min ...
the struct tm is explained here: http://www.cplusplus.com/reference/ctime/tm/
Perhaps boost date-time is what you want.
C++11: chrono?
std::chrono::time_point<std::chrono::system_clock>
stamp(std::chrono::system_clock::now());
I would like to convert an int date like:
20111201
to string:
01DEC2011
Is there a fast date format conversion built into C++ (or maybe a bash system command I can execute instead) to do this or am I stuck making a switch for all of the months?
You could use the strptime to convert your string to a struct tm, then use strftime to reformat it:
#include <ctime>
#include <iostream>
#include <sstream>
int main()
{
std::ostringstream date1;
date1 << 20111201;
struct tm tm;
strptime(date1.str().c_str(), "%Y%m%d", &tm);
char date2[10];
strftime(date2, sizeof(date2), "%d%b%Y", &tm);
std::cout << date1.str() << " -> " << date2 << std::endl;
}
Output is:
20111201 -> 01Dec2011
Just need to convert the Dec to upper case if it's necessary.
Don't use bash here. The way to go is to use Boost in C++ for more reasons than I've time to list here, but ultimately it will be just as fast as most other solutions you'll encounter and unless your functionality is absolutely time critical, it won't make a great deal of difference anyway.
Also, It's going to be far more flexible and maintainable than all those crappy little hard coded date conversion routines that you always encounter.
The following code will do what you want.
#include <iostream>
#include <sstream>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/algorithm/string.hpp>
using namespace boost::gregorian;
using namespace std;
int main(int argc, char **argv)
{
int dateIn = 20111201;
// Read the date in from ISO format as an int.
ostringstream ss;
ss << dateIn;
date d(from_undelimited_string( ss.str() ));
// Set the output format
date_facet *fct = new date_facet("%d%b%Y"); // [1]
locale loc = locale(locale::classic(), fct);
// Render the date as a string;
ss.str("");
ss.imbue(loc);
ss << d;
string dateOut( ss.str() );
boost::to_upper( dateOut );
cout << dateOut << endl;
}
This gives the following output:
01DEC2011
Just changing the format string "%d%b%Y" at ref [1] will change to a different output format but remember I've converted it to uppercase as well.
There's nothing directly built-in, since this format for dates
is relatively rare. The simplest solution here would be to
break the date up into year month day using % and /
operators (e.g. month is value / 100 % 100), then format the
three values normally, using std::ostream, and looking up the
date in a table. (This would obviously require some error
checking, since not all integral values yield valid dates.)
New answer to old question. This answer traffics through the C++11/14 <chrono> library instead of C's tm or boost::date_time. Otherwise it is very similar to the existing answers. It requires this free, open-source library for the parsing and formatting.
#include "tz.h"
#include <iostream>
#include <locale>
#include <sstream>
int
main()
{
auto date1 = 20111201;
std::stringstream stream;
stream.exceptions(std::ios::failbit);
stream << date1;
std::chrono::system_clock::time_point tp;
date::parse(stream, "%Y%m%d", tp);
auto str = date::format("%d%b%Y", tp);
auto& ct = std::use_facet<std::ctype<char>>(std::locale::classic());
ct.toupper(&str.front(), &str.back()+1);
std::cout << str << '\n';
}
I've included stream.exceptions(std::ios::failbit); to noisily detect invalid "integer dates". And I've included old C++98 code to convert the string to uppercase (the locale dance at the end).
01DEC2011
One of the advantages of using a modern C++ date/time library is the ease with which changes can be made. For example, what if now you need to parse the timestamp not with day-precision, but with millisecond precision? Here is how that might be done:
auto date1 = 20111201093357.275L;
std::stringstream stream;
stream.exceptions(std::ios::failbit);
stream << std::fixed << date1;
std::chrono::system_clock::time_point tp;
date::parse(stream, "%Y%m%d%H%M%S", tp);
auto str = date::format("%d%b%Y %T", tp);
auto& ct = std::use_facet<std::ctype<char>>(std::locale::classic());
ct.toupper(&str.front(), &str.back()+1);
std::cout << str << '\n';
which outputs:
01DEC2011 09:33:57.275000
Or perhaps these timestamps are known to originate from Chatham Island off the coast of New Zealand and you need them in UTC. Just add one line after the parse:
tp = date::locate_zone("Pacific/Chatham")->to_sys(tp);
And now the output is:
30NOV2011 19:48:57.275000
Taking into account arbitrary timezones and subsecond precision is currently beyond the capabilities of all other C++ libraries.