Boost log file names in a different timezone - c++

I currently have a log process in boost
that is initialized with
keywords::file_name = (my_file_str + %Y-%m-%d_%H.%5N.log).c_str();
Is there any way to change the timezone of the datetime object? I would like to use a timezone that rolls at 5PM US/New_York namely EST-2EDT.

Yes, it is possible, here is a function generating a timestamp in US/NY:
#include <boost/date_time/local_time/local_time.hpp>
#include <sstream>
std::string getNYCTimestamp() {
boost::posix_time::ptime pt =
boost::posix_time::microsec_clock::universal_time();
boost::local_time::time_zone_ptr utc_zone(
new boost::local_time::posix_time_zone("UTC"));
boost::local_time::local_date_time utc_time(pt, utc_zone);
boost::local_time::time_zone_ptr nyc_zone(
new boost::local_time::posix_time_zone("EST-05:00:00EDT+01:00:00,M4.1.0/02:00:00,M10.5.0/02:00:00"));
boost::local_time::local_date_time nyc_time = utc_time.local_time_in(nyc_zone);
auto our_facet = new boost::gregorian::date_facet("%Y-%m-%d");
our_facet->format("%Y-%m-%d");
auto os = std::ostringstream();
os.imbue(std::locale(std::locale::classic(), our_facet));
boost::posix_time::time_duration t = nyc_time.time_of_day();
os << nyc_time.date() << "_"
<< std::setw(2) << std::setfill('0') << t.hours() << '-'
<< std::setw(2) << std::setfill('0') << t.minutes() << '-'
<< std::setw(2) << std::setfill('0') << t.seconds() << '.'
<< std::setw(6) << std::setfill('0') << t.fractional_seconds();
return os.str();
}
Note, that "%Y-%m-%d_%H.%5N" is most probably wrong, because:
it omits minutes and seconds
it tries to fit nanoseconds (9 digits) into 5 symbols.

I am not 100% sure this will suffice (since I don't know what your requirements are) but here : http://boost-log.sourceforge.net/libs/log/doc/html/log/tutorial/attributes.html is a discussion of log attributes. It seems to me that you may be able to add it a secondary timestamp that is adjusted fo timezone.

Related

Problem when converting from time_t to tm then back to time_t

I have a time_t value of 1530173696 which represents Thursday, June 28, 2018 8:14:56 AM.
I want to round down the time to the nearest hour. Specifically, down to 1530172800, which represent Thursday, June 28, 2018 8:00:00 AM. So, my idea is to convert this time_t to a tm struct, and then assign its sec and min values to 0.
However, after I do that, and after I convert the modified tm back to a time_t value, the value I get is way off. I get a value of 1530158400 which represents Thursday, June 28, 2018 4:00:00 AM. That's 4 hours off. Even checking values of up to 8:59:59 AM still gives the rounded down value of 4:00:00 AM.
I wrote the code below to demonstrate the problem. I use VisulStudio 2017.
I don't understand what I am doing wrong. I appreciate any help. Thanks.
#include <iostream>
#include <time.h>
bool equalTMs(tm& tm1, tm& tm2);
void printTM(tm& myTM);
int main()
{
tm myTM;
time_t datetime = 1530173696;
//datetime = 1530176399; // to check the time_t value of 8:59 AM
gmtime_s(&myTM, &datetime);
myTM.tm_sec = 0;
myTM.tm_min = 0;
time_t myTime_T = mktime(&myTM);
tm sanityCheckTM;
time_t roundedDownToNearestHour = 1530172800;
gmtime_s(&sanityCheckTM, &roundedDownToNearestHour);
time_t sanityCheckTimeT = mktime(&sanityCheckTM);
std::cout << "datetime: " << datetime << std::endl;
std::cout << "myTime_T: " << myTime_T << std::endl;
std::cout << std::endl;
std::cout << "roundedDownToNearestHour: " << roundedDownToNearestHour << std::endl;
std::cout << "sanityCheckTimeT: " << sanityCheckTimeT << std::endl;
std::cout << std::endl;
std::cout << "myTM and sanityCheckTM equal? " << (equalTMs(myTM, sanityCheckTM) ? "true" : "false") << std::endl;
std::cout << "\nmyTM:-\n\n";
printTM(myTM);
std::cout << "\nsanityCheckTM:-\n\n";
printTM(sanityCheckTM);
std::cout << "\n";
time_t _time_t = 1530158400;
tm _tm;
gmtime_s(&_tm, &_time_t);
std::cout << "_time_t: " << _time_t << std::endl;
std::cout << "_tm and sanityCheckTM equal? " << (equalTMs(_tm, sanityCheckTM) ? "true" : "false") << std::endl;
std::cout << "\n_tm:-\n\n";
printTM(_tm);
}
void printTM(tm& myTM)
{
std::cout << "tm_sec: " << myTM.tm_sec << std::endl;
std::cout << "tm_min: " << myTM.tm_min << std::endl;
std::cout << "tm_hour: " << myTM.tm_hour << std::endl;
std::cout << "tm_mday: " << myTM.tm_mday << std::endl;
std::cout << "tm_mon: " << myTM.tm_mon << std::endl;
std::cout << "tm_year: " << myTM.tm_year << std::endl;
std::cout << "tm_wday: " << myTM.tm_wday << std::endl;
std::cout << "tm_yday: " << myTM.tm_yday << std::endl;
std::cout << "tm_isdst: " << myTM.tm_isdst << std::endl;
}
bool equalTMs(tm& tm1, tm& tm2)
{
return (tm1.tm_sec == tm2.tm_sec)
&& (tm1.tm_min == tm2.tm_min)
&& (tm1.tm_hour == tm2.tm_hour)
&& (tm1.tm_mday == tm2.tm_mday)
&& (tm1.tm_mon == tm2.tm_mon)
&& (tm1.tm_year == tm2.tm_year)
&& (tm1.tm_wday == tm2.tm_wday)
&& (tm1.tm_yday == tm2.tm_yday)
&& (tm1.tm_isdst == tm2.tm_isdst);
}
gmtime_s() returns a tm that is expressed in UTC time. You pass that to mktime(), which expects the tm to be expressed in LOCAL time instead. Your StackOverflow profile says you are located in Abu Dhabi, whose time zone is GMT+4. That is why you have a 4-hour discrepancy.
Use localtime_s() instead of gmtime_s().
Since 1530173696 is being used as a Unix Time (UTC excluding leap seconds), this can be solved without involving time zones.
Howard Hinnant's date/time library can be used to solve this problem, and to check that you're getting the right answer. However, skip to the end of this answer if you want to see how to do this very simply without the use of any library at all.
1530173696 is a count of seconds since 1970-01-01 UTC. If you want to convert this into a human readable date/time, one can:
#include "date/date.h"
#include <iostream>
int
main()
{
time_t datetime = 1530173696;
date::sys_seconds tp{std::chrono::seconds{datetime}};
using date::operator<<;
std::cout << tp << '\n';
}
which outputs:
2018-06-28 08:14:56
This does nothing but validate the input. Furthermore tp is nothing more than a std::chrono::time_point based on system_clock but with a precision of seconds. You can round this down to the hour with:
tp = floor<std::chrono::hours>(tp);
Here floor can be grabbed from "date.h" under namespace date, or if you have C++17 or later, you can use std::chrono::floor. You can use "date.h" to print tp out again and you will get:
2018-06-28 08:00:00
(as desired). To turn this back into a time_t, simply extract the duration, and then the count:
time_t myTime_T = tp.time_since_epoch().count();
This will have the value 1530172800 as expected.
Finally, if you do not need to print these time stamps out in a human readable form, you can do the math quite easily yourself:
time_t myTime_T = datetime / 3600 * 3600;
This is essentially the same operation as:
tp = floor<std::chrono::hours>(tp);
except that the floor version will continue to get the correct answer when the input is negative (a timestamp prior to 1970-01-01 00:00:00 UTC). The "manual" implementation will round up to the next hour when given a negative input.

C++ - Convert AM/PM time string to posix time ptime

I'm trying to convert a time string to boost::posix_time::ptime object, but the conversion is not working.This is the function thats being used.
std::string Parser::getFormattedDate(std::string datetime)
{
std::stringstream date_strm, date_res;
boost::posix_time::ptime pt;
boost::posix_time::time_input_facet *facet = new boost::posix_time::time_input_facet( "%Y-%b-%d %H:%M:%S %p" );
date_strm.imbue( std::locale( std::locale(), facet ));
date_strm << datetime;
date_strm >> pt;
date_res << pt.date().year() << "-" << std::setw(2) << std::setfill('0') << pt.date().month().as_number()
<< "-" << std::setw(2) << std::setfill('0') << pt.date().day() << " "
<< pt.time_of_day().hours() << ":" << pt.time_of_day().minutes() << ":" << pt.time_of_day().seconds();
return date_res.str();
}
With a input time string of 2016-Feb-29 2:00:00 AM, this function is returning Thu Dec 3 04:00:54 287564 which is obviously not correct. How can i get the correct date time from that input ? In this case the correct date time should be 2016-02-29 02:00:00
The time_input_facet thats being used in this function for the required conversion is "%Y-%b-%d %H:%M:%S %p"
The documentation says:
The exclamation mark means:
The following tables list the all the flags available for both date_time IO as well as strftime. Format flags marked with a single asterisk (*) have a behavior unique to date_time. Those flags marked with an exclamation point (!) are not usable for input (at this time). The flags marked with a hash sign (#) are implemented by system locale and are known to be missing on some platforms. The first table is for dates, and the second table is for times.
So you'll have to manually parse the am/pm part if you must support this using just Boost Datetime
Maybe you can look at Boost Locale for this task: http://www.boost.org/doc/libs/1_49_0/libs/locale/doc/html/formatting_and_parsing.html
This works for me:
#include <boost/date_time/posix_time/posix_time_io.hpp>
#include <boost/locale.hpp>
static std::locale s_loc = boost::locale::generator{}.generate("");
std::string getFormattedDate(std::string datetime) {
boost::posix_time::ptime pt;
using namespace boost::locale;
std::stringstream ss(datetime);
ss.imbue(s_loc);
date_time dt;
if (ss >> as::ftime("%Y-%b-%d %I:%M:%S %p") >> dt) {
ss.str("");
ss.clear();
ss << as::ftime("%Y-%m-%d %H:%M:%S") << dt;
return ss.str();
}
throw std::bad_cast();
}
int main() {
std::locale::global(s_loc);
for (auto s : { "2016-Feb-29 02:06:22 AM", "2016-Mar-29 02:06:22 PM" })
std::cout << s << " -> " << getFormattedDate(s) << "\n";
std::cout << "Bye\n";
}
Prints
2016-Feb-29 02:06:22 AM -> 2016-02-29 02:06:22
2016-Mar-29 02:06:22 PM -> 2016-03-29 14:06:22
Bye

Time stamp for saving file or folder?

Is there a simpler way to do a time stamp for saving a file/creating a directory as a date time stamp ?
only using standard library (not boost). Is there a faster way to do it ?
This is my current code
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::time_t tt = std::chrono::system_clock::to_time_t(now);
tm utc_tm = *gmtime(&tt);
oname.str("");
oname << (utc_tm.tm_year + 1900) << '-' << std::setfill('0') << std::setw(2) << (utc_tm.tm_mon + 1) << '-' << std::setfill('0') << std::setw(2) << utc_tm.tm_mday << " " << std::setfill('0') << std::setw(2)<< utc_tm.tm_hour <<':' << std::setfill('0') << std::setw(2) << utc_tm.tm_min <<':' << std::setfill('0') << std::setw(2) << utc_tm.tm_sec;
ts = oname.str();
There is a less tortuous way:
#include <string>
#include <ctime>
std::string get_timestamp()
{
auto now = std::time(nullptr);
char buf[sizeof("YYYY-MM-DD HH:MM:SS")];
return std::string(buf,buf +
std::strftime(buf,sizeof(buf),"%F %T",std::gmtime(&now)));
}
It is very probably also faster, because it is less tortuous, but that is
also very probably immaterial in a setting where disc I/O is in play.
This gives you the same timestamps as your own code, e.g.
2015-03-28 10:48:45
See std::time and
std::strftime to
understand how the desired formatting is achieved and note that std::strftime
returns the length of the string it has composed, excluding its nul-terminator.
This code is standard, but if you are working with MS VC++ 2013 or later then
you could also consider the use of std::put_time,
as in:
#include <iomanip>
#include <sstream>
#include <string>
#include <ctime>
std::string get_timestamp()
{
auto now = std::time(nullptr);
std::ostringstream os;
os << std::put_time(std::gmtime(&now),"%F %T");
return os.str();
}
which is simpler still. (I have not tested that.) std::put_time however
is unsupported by gcc as of 4.9.
Seemingly you want your timestamps formatted as YYYY-MM-DD HH:MM:SS. If they
are to be used in filenames, it would be more prudent to keep them free of spaces:
perhaps YYYY-MM-DD_HH:MM:SS.

Most simple way to get string containing time interval

I'm new to std::chrono and I'm looking for a simple way to construct a string containing a time interval formatted hhh:mm:ss (yes, 3 hour figures), indicating the difference between a start time point and now.
How would I go about this using a steady_clock? The examples on Cppreference don't quite fit this problem.
Any time you find yourself manually applying conversion factors among units with the <chrono> library, you should be asking yourself:
Why am I converting units manually? Isn't this what <chrono> is
supposed to do for me?!
A "conversion factor" is 60, or 1000, or 100, or whatever. If you see it in your code, you're opening yourself up to conversion factor errors.
Here is sasha.sochka's code rewritten without these conversion factors. And just to throw in how general this technique is, milliseconds are added for flare:
#include <chrono>
#include <string>
#include <sstream>
#include <iomanip>
#include <iostream>
int main() {
using namespace std::chrono;
steady_clock::time_point start;
steady_clock::time_point now = steady_clock::now();
auto d = now -start;
auto hhh = duration_cast<hours>(d);
d -= hhh;
auto mm = duration_cast<minutes>(d);
d -= mm;
auto ss = duration_cast<seconds>(d);
d -= ss;
auto ms = duration_cast<milliseconds>(d);
std::ostringstream stream;
stream << std::setfill('0') << std::setw(3) << hhh.count() << ':' <<
std::setfill('0') << std::setw(2) << mm.count() << ':' <<
std::setfill('0') << std::setw(2) << ss.count() << '.' <<
std::setfill('0') << std::setw(3) << ms.count();
std::string result = stream.str();
std::cout << result << '\n';
}
There are other ways to do this without exposed conversion factors, this way is only an example. My main point is: avoid hardcoding unit conversion factors in your code. They are error prone. Even if you get it right when you first code it, conversion factors are vulnerable to future code maintenance. You can future-proof your code by demanding that all unit conversions happen within the <chrono> library.
As Joachim Pileborg noted higher in the comments there is no function for format a string from a duration object. But you can do it using duration_cast to convert time difference first to hours and then minutes and seconds.
After that using C++11 to_string function you can concatenate them to get the resulting string.
#include <chrono>
#include <string>
#include <sstream>
#include <iomanip>
int main() {
using namespace std::chrono;
steady_clock::time_point start = /* Some point in time */;
steady_clock::time_point now = steady_clock::now();
int hhh = duration_cast<hours>(now - start).count();
int mm = duration_cast<minutes>(now - start).count() % 60;
int ss = duration_cast<seconds>(now - start).count() % 60;
std::ostringstream stream;
stream << std::setfill('0') << std::setw(3) << hhh << ':' <<
std::setfill('0') << std::setw(2) << mm << ':' <<
std::setfill('0') << std::setw(2) << ss;
std::string result = stream.str();
}

Are boost ptime's always UTC?

Are boost ptime instances always UTC? I can't see any time zone info on them.
The ptime has no associated timezone information. It does not know whether the content is in UTC or local time. In fact, you could do silly things like:
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/c_local_time_adjustor.hpp>
#include <iostream>
int main()
{
using namespace boost::posix_time;
ptime pt1 = microsec_clock::local_time();
std::cout << "Local: " << pt1 << std::endl;
std::cout << "UTC: " << microsec_clock::universal_time() << std::endl;
// !!!
ptime pt2 = boost::date_time::c_local_adjustor<ptime>::utc_to_local(pt1);
std::cout << "Oops: " << pt2 << std::endl;
}
and it will happily create a meaningless time for you.
They are whatever time zone you want them to be. As long as you are consistent in your calculations, it shouldn't matter what time zone the values represent.