I need to create a timestamp with boost in the format:
YYYYmmDDhhMMssFFFF (with F = Milliseconds).
I use
facet->format("%Y%m%d%H%M%S%F");
but the output includes always a dot between seconds and milliseconds. This is the output:
"20150202140830.779716"
Is there a better way to achieve my format than to replace the dot and cut the last two digits by hand?
In case you're interested, I implemented a custom facet the other day here: Format a posix time with just 3 digits in fractional seconds
You could use the same approach here:
Live On Coliru
#include "time_facet.hpp"
#include <boost/date_time/posix_time/posix_time.hpp>
int main()
{
using namespace boost::posix_time;
ptime const date_time = microsec_clock::local_time();
std::cout << date_time << std::endl;
auto facet = new time_facet("%Y-%b-%d %H:%M:%S%4 %z");
std::cout.imbue(std::locale(std::cout.getloc(), facet));
std::cout << date_time << std::endl;
}
Prints:
2015-Feb-02 22:01:00.926982
2015-Feb-02 22:01:009269
Related
I'm trying to create a logging file for my C++ program. My goal is to put two timestamps at two points of my program and print in a file the CPU time period between these two points. I'm doing this because I want to know which parts of my code are the most time consuming so I can make improvements (so there may be several chunks of code I want to measure). So far, I've made a function that, when called, prints a string that I pass as an argument, to a file:
#define LOGFILE "myprog.log"
void Log (std::string message){
std::ofstream ofs;
ofs.open(LOGFILE, std::ofstream::out | std::ios::app);
ofs << message << std::endl;
ofs.close();
}
However, I'm having difficulty figuring out how to print the CPU timestamp. Firstly, I don't know what time measurement format I should use (should I use the chrono or the time_t types?) I'm trying to print a time period so it would be helpful if there was a type for duration (I've tried chrono::duration but it seems to require C++11 support). Secondly, given I know what type to use, how do I print it to the file? Is there a way to cast that type to a string? Or can I pass it directly to my function and print it somehow?
This has troubled me a lot the last couple of days and I can't seem to figure it out, so any input would be really helpful. Thanks in advance!
Get a CPU Timestamp
You'll want to use std::chrono::system_clock to get this timestamp. Do not use std::chrono::steady_clock or std::chrono::high_resolution_clock, as those are for making high-precision timing measurements, and do not guarantee fidelity or accuracy to wall-clock time.
auto now = std::chrono::system_clock::now();
//now is a time_point object describing the instant it was recorded according to your system clock
Print this CPU Timestamp in a readable format
In C++20, this is pretty trivial.
std::string formatted_time = std::format("{0:%F_%T}", now);
ofs << formatted_time << ": " << message << std::endl;
%F is a substitute for %Y-%m-%D, which will output year-month-day in ISO format, i.e. 2018-10-09.
%T is the same for %H:%M:%S, which will output a time, i.e. 17:55:34.786
See the specification for std::format and std::formatter for more information about how to specify these parameters.
As of December 2020, no major compilers support the <format> library, yet, so as an alternative you can use fmt, which is a standalone implementation of the library.
Prior to C++20
Consider Howard Hinnant's date library, most of which is being incorporated into C++20 as a new part of the chrono library. The format function found in that library uses the same syntax as suggested above for the C++20 version, although without integration with std::format.
I'm usually use my implementation for such things.
#include <chrono>
#include <ctime>
// strftime format
#define LOGGER_PRETTY_TIME_FORMAT "%Y-%m-%d %H:%M:%S"
// printf format
#define LOGGER_PRETTY_MS_FORMAT ".%03d"
// convert current time to milliseconds since unix epoch
template <typename T>
static int to_ms(const std::chrono::time_point<T>& tp)
{
using namespace std::chrono;
auto dur = tp.time_since_epoch();
return static_cast<int>(duration_cast<milliseconds>(dur).count());
}
// format it in two parts: main part with date and time and part with milliseconds
static std::string pretty_time()
{
auto tp = std::chrono::system_clock::now();
std::time_t current_time = std::chrono::system_clock::to_time_t(tp);
// this function use static global pointer. so it is not thread safe solution
std::tm* time_info = std::localtime(¤t_time);
char buffer[128];
int string_size = strftime(
buffer, sizeof(buffer),
LOGGER_PRETTY_TIME_FORMAT,
time_info
);
int ms = to_ms(tp) % 1000;
string_size += std::snprintf(
buffer + string_size, sizeof(buffer) - string_size,
LOGGER_PRETTY_MS_FORMAT, ms
);
return std::string(buffer, buffer + string_size);
}
It returns current time in format: 2018-09-23 21:58:52.642.
Yes it requires --std=c++11 or above.
For the record:
If C++20 features are not available, as in my case, you can use the following:
#include <ctime>
#include <iomanip>
#include <iostream>
using namespace std ;
time_t now = time(nullptr) ;
cout << put_time(localtime(&now), "%T") << endl ;
put_time is defined in iomanip library, look at https://en.cppreference.com/w/cpp/io/manip/put_time, and time_t and localtime are from the ctime, https://en.cppreference.com/w/cpp/chrono/c/ctime
If you want a more manual approach, this is what I've used before
char buffer[MAX_BUFFER_SIZE];
time_t t = time(NULL);
struct tm *lt = localtime(&t);
snprintf(buffer, MAX_BUFFER_SIZE, "%02d/%02d/%02d %02d:%02d:%02d", lt->tm_mon+1, lt->tm_mday, lt->tm_year%100, lt->tm_hour, lt->tm_min, lt->tm_sec);
Then just output buffer, which now contains string representation of time, to your file.
I am working with date/time string in the %Y-%m-%d %H:%M:%S%f format.
I want to design a function that takes a date/time string in the America/New_York timezone and returns a date/time string in the Europe/Paris timezone.
I came up with the following
std::string ec2cet(const std::string &date_time_str)
{
using namespace boost::posix_time;
using namespace boost::gregorian;
using namespace boost::local_time;
tz_database tz_db;
time_zone_ptr et_tz = tz_db.time_zone_from_region("America/New_York");
time_zone_ptr cet_tz = tz_db.time_zone_from_region("Europe/Paris");
ptime absolute_time = time_from_string(date_time_str);
local_date_time ec_time(absolute_time, et_tz);
local_date_time cet_time = et_time.local_time_in(cet_tz);
return to_simple_string(cet_time);
}
When printing either et_time or cet_time with the input string 16:03:38.539000 I get
16:03:38.539000 UTC
I was expecting
et_time to be something different than UTC because I constructed it providing the et_tz timezone object.
cet_time to be in a different timezone as I constructed it with local_time_in and providing the cet_tz timezone object.
What am I doing wrong?
I don't know what is wrong with the boost implementation of ec2cet. However I can show how to do this with this free, open source library quite easily. First the code, and then the line-by-line explanation:
#include "tz.h"
#include <iostream>
#include <sstream>
#include <string>
std::string
ec2cet(const std::string& date_time_str)
{
using namespace std; // 1
using namespace std::chrono; // 2
using namespace date; // 3
constexpr auto fmt = "%F %T"; // 4
istringstream in{date_time_str}; // 5
local_time<microseconds> tp; // 6
in >> parse(fmt, tp); // 7
if (in.fail()) // 8
return std::string{}; // 9
auto et_time = make_zoned("America/New_York", tp); // 10
auto cet_time = make_zoned("Europe/Paris", et_time); // 11
cout << "et_time = " << et_time << '\n'; // 12
cout << "cet_time = " << cet_time << '\n'; // 13
return format(fmt, cet_time); // 14
}
Lines 1-3 just bring everything into the local space so things aren't quite so verbose.
Line 4: Just write the format string in one place. "%F %T" is equivalent to "%Y-%m-%d %H:%M:%S" and you could use that too. (I'm lazy).
Line 5: This library will parse out of any stream, so we need to turn the input string date_time_str into a stream (in).
Line 6: You say that the input string is known to represent the local date/time in "America/New_York". The type local_time<microseconds> is a chrono::time_point which can represent the local time in any time zone to microseconds precision. An instance of this (tp) is what we will parse date_time_str into.
Line 7: Parse into tp.
Line 8-9: Check for a parse error.
Line 10: Create a zoned_time<microseconds> using the "America/New_York" time_zone and the local_time tp. You can think of a zoned_time as simply a pair<time_zone, local_time> though the details are slightly more complex than that.
Line 11: Create a zoned_time<microseconds> from the time_zone "Europe/Paris" and the zoned_time et_time. This converts one local time to another, equating their UTC equivalents in the process.
Lines 12-13: Output these intermediate results (et_time and cet_time for debugging purposes).
Line 14: Format cet_time into the desired std::string and return it.
Running this with the following driver:
int
main()
{
auto s = ec2cet("2017-01-08 16:03:38.539000");
std::cout << "ec2cet = " << s << '\n';
}
Outputs:
et_time = 2017-01-08 16:03:38.539000 EST
cet_time = 2017-01-08 22:03:38.539000 CET
ec2cet = 2017-01-08 22:03:38.539000
This program tracks the current IANA timezone database, and will correctly follow the time zone rules for the complete history of the IANA database, which for these two timezones starts in the late 1800's.
If microseconds precision isn't what you want, you can change that (in one place on line 6). If the input string should be UTC instead of "America/New_York", that is also a one-line change on line 6 (make the type of tp sys_time<microseconds> instead of local_time<microseconds>).
I have string 20150410 121416 in c++.
I need to turn this into 20150410 12:14:16
How can i insert a colon to the string?
One can format date/times in C and C++ with strftime. There also exists a non-standard but common POSIX function called strptime one can use to parse times. One could use these to parse your date/time in your input format, and then format it back out in your desired format.
That is, assuming you didn't want to write the parsing code yourself.
If you have C++11, then you could use this free, open-source date/time library to help you do all this with strftime-like format strings. Such code could look like:
#include "tz.h"
#include <iostream>
#include <sstream>
int
main()
{
using namespace date;
std::string input = "20150410 121416";
std::stringstream stream{input};
stream.exceptions(std::ios::failbit);
sys_seconds tp;
parse(stream, "%Y%m%d %H%M%S", tp);
auto output = format("%Y%m%d %T", tp);
std::cout << output << '\n';
}
Output:
20150410 12:14:16
One advantage of using a date/time parsing/formatting library, as opposed to just treating these as generic strings, is that you can more easily alter the formatting, or manipulate the datetime during the format conversion (e.g. have it change timezones).
For example, next month the specification might change on you and now you're told that this is a timestamp representing local time in Moscow and you need to convert it to local time in London and output it in the form YYYY-MM-DD HH:MM:SS <UTC offset>. The above code hardly changes at all if you're using a good date/time library.
#include "tz.h"
#include <iostream>
#include <sstream>
int
main()
{
using namespace date;
std::string input = "20150410 121416";
std::stringstream stream{input};
stream.exceptions(std::ios::failbit);
local_seconds tp;
parse(stream, "%Y%m%d %H%M%S", tp);
auto moscow_time = make_zoned("Europe/Moscow", tp);
auto london_time = make_zoned("Europe/London", moscow_time);
auto output = format("%F %T %z", london_time);
std::cout << output << '\n';
}
2015-04-10 10:14:16 +0100
But if you started out just doing string manipulation, all of the sudden you've got a major task in front of you. Writing code that understands the semantics of the datetime "20150410 121416" is a significant leap above manipulating the characters of "20150410 121416" as a string.
<script type="text/javascript">
function formatTime(objFormField){
intFieldLength = objFormField.value.length;
if(intFieldLength==2 || intFieldLength == 2){
objFormField.value = objFormField.value + ":";
return false;
}
}
</script>
Enter time <input type="text" maxlength="5" minlength="5" onKeyPress="formatTime(this)"/>
The problems is:
I know how to get the local time in boost
the code:
boost::local_time::local_date_time currentTime(
boost::posix_time::second_clock::local_time(),
boost::local_time::time_zone_ptr());
std::cout << currentTime.local_time() << std::endl;
I know how to get the current time zone data from the machine (I hope it's a right way)
the code:
tzset();
// the var tzname will have time zone names
// the var timezone will have the current offset
// the var daylight should show me if there is daylight "on"
but still I can't get local_date_time with the current time_zone... Does someone know, how to do it?
OK, as for now I still don't know the whole answer
but there is code, which could help to print the current time zone offset
(based on an answer for a related question here (stackoverflow) and some inner boost code)
I'm absolutely not sure it's will work correctly on all machines, but for now it's better than nothing:
boost::posix_time::time_duration getUtcOffset(const boost::posix_time::ptime& utcTime)
{
using boost::posix_time::ptime;
const ptime localTime = boost::date_time::c_local_adjustor<ptime>::utc_to_local(utcTime);
return localTime - utcTime;
}
std::wstring getUtcOffsetString(const boost::posix_time::ptime& utcTime)
{
const boost::posix_time::time_duration td = getUtcOffset(utcTime);
const wchar_t fillChar = L'0';
const wchar_t timeSeparator = L':';
std::wostringstream out;
out << (td.is_negative() ? L'-' : L'+');
out << std::setw(2) << std::setfill(fillChar)
<< boost::date_time::absolute_value(td.hours());
out << L':';
out << std::setw(2) << std::setfill(fillChar)
<< boost::date_time::absolute_value(td.minutes());
return out.str();
}
int main()
{
const boost::posix_time::ptime utcNow =
boost::posix_time::second_clock::universal_time();
const std::wstring curTimeOffset = getUtcOffsetString(utcNow);
std::wcout << curTimeOffset.c_str() << std::endl; // prints -05:00 on my comp
}
So, if you also need to obtain correct UTC offset than my previous example should be modified a bit to produce correct timezone string:
static boost::posix_time::time_duration utc_offset(
second_clock::local_time() - second_clock::universal_time());
std::ostringstream ss_posix_tz_def;
// We don't care about real zone name so, just put any three letters.
ss_posix_tz_def << "LOC" << utc_offset;
string posix_tz_def = ss_posix_tz_def.str();
Another solution is writing a whole new timezone provider. Like this simple one:
// We assume local TZ doesn't have DST, UTC offset is calculated basing on
// local system clocks. Thus, using of this provider for time calculations for
// an arbitrary date is not a good idea.
class machine_time_zone : public boost::local_time::custom_time_zone {
public:
typedef boost::local_time::custom_time_zone base_type;
typedef base_type::time_duration_type time_duration_type;
machine_time_zone()
: boost::local_time::custom_time_zone(
time_zone_names("Local machine TZ", "LOC", "", ""),
GetUTCOffset(),
boost::local_time::dst_adjustment_offsets(
time_duration_type(0, 0, 0),
time_duration_type(0, 0, 0), time_duration_type(0, 0, 0)),
boost::shared_ptr<boost::local_time::dst_calc_rule>()) {
}
// This method is not precise, real offset may be several seconds more or less.
static const boost::posix_time::time_duration& GetUTCOffset() {
using boost::posix_time::second_clock;
static boost::posix_time::time_duration utc_offset(
second_clock::local_time() - second_clock::universal_time());
return utc_offset;
}
};
Just pass it when constructing local_date_time:
local_date_time ldt(second_clock::local_time(),
time_zone_ptr(new machine_time_zone()));
Since you say you have timezone information the only question is how to use boost for formatting string you need. Below is a sample code:
using namespace boost::local_time;
using namespace boost::posix_time;
// Composing this string is the most tricky part. Syntax see in:
// boost\date_time\local_time\posix_time_zone.hpp
string posix_tz_def("PST-5PDT01:00:00,M4.1.0/02:00:00,M10.1.0/02:00:00");
local_date_time ldt(second_clock::local_time(),
time_zone_ptr(new posix_time_zone(posix_tz_def)));
std::stringstream ss;
local_time_facet* output_facet = new local_time_facet();
ss.imbue(std::locale(std::locale::classic(), output_facet));
output_facet->format("%Y-%m-%dT%H:%M:%S %Q");
ss << ldt;
string formatted_datetime = ss.str(); // 2012-01-05T18:14:06 -05:00
In this approach most problematic part is posix timezone string. I think there should be databases with these strings for every timezone and boost provides a template for working with .csv file. If the only thing you need is offset in the string just setup DTS to 0:00:00 and don't care about the rest. For example use this string: PST-5:30PDT0,0,365 (forever PDT, shift 0). Substitute "-5:00" with the offset you need.
Though, C++/boost way would be implementing own timezone provider by deriving from date_time::time_zone_base.
More samples and ideas could be found here.
In C++ what is the simplest way to add one day to a date in this format:
"20090629-05:57:43"
Probably using Boost 1.36 - Boost::date, Boost::posix_date or any other boost or std library functionality, I'm not interested in other libraries.
So far I came up with:
format the string (split date and time parts as string op) to be able to initialize boost::gregorian::date, date expects format like:
"2009-06-29 05:57:43"
I have
"20090629-05:57:43"
add one day (boost date_duration stuff)
convert back to_simple_string and append the time part (string operation)
Is there any easier/niftier way to do this?
I am looking at run time efficiency.
Example code for the above steps:
using namespace boost::gregorian;
string orig("20090629-05:57:43");
string dday(orig.substr(0,8));
string dtime(orig.substr(8));
date d(from_undelimited_string(dday));
date_duration dd(1);
d += dd;
string result(to_iso_string(d) + dtime);
result:
20090630-05:57:43
That's pretty close to the simplest method I know of. About the only way to simplify it further would be using facets for the I/O stuff, to eliminate the need for string manipulation:
#include <iostream>
#include <sstream>
#include <locale>
#include <boost/date_time.hpp>
using namespace boost::local_time;
int main() {
std::stringstream ss;
local_time_facet* output_facet = new local_time_facet();
local_time_input_facet* input_facet = new local_time_input_facet();
ss.imbue(std::locale(std::locale::classic(), output_facet));
ss.imbue(std::locale(ss.getloc(), input_facet));
local_date_time ldt(not_a_date_time);
input_facet->format("%Y%m%d-%H:%M:%S");
ss.str("20090629-05:57:43");
ss >> ldt;
output_facet->format("%Y%m%d-%H:%M:%S");
ss.str(std::string());
ss << ldt;
std::cout << ss.str() << std::endl;
}
That's longer, and arguably harder to understand, though. I haven't tried to prove it, but I suspect it would be about equal runtime-efficiency that way.