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;
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 a function which accepts second as argument and returns a string in HH:MM:SS format. Without std::chrono, I can implement it like this:
string myclass::ElapsedTime(long secs) {
uint32_t hh = secs / 3600;
uint32_t mm = (secs % 3600) / 60;
uint32_t ss = (secs % 3600) % 60;
char timestring[9];
sprintf(timestring, "%02d:%02d:%02d", hh,mm,ss);
return string(timestring);
}
Using std::chrono, I can convert the argument to std::chrono::seconds sec {seconds};.
But the how can I convert it to string with the format?
I saw the good video tutorial from Howard Hinnant in https://youtu.be/P32hvk8b13M. Unfortunately, there is no example for this case.
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);
}
Once C++20 implementations land, you'll be able to do the following (untested code):
std::chrono::hh_mm_ss<std::chrono::seconds> tod{std::chrono::seconds(secs)};
std::cout << tod;
See time.hms.overview for more info.
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 = [×tamp]() { 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);
}
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.
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);