Convert boost::posix_time::ptime to string using custom time zone - c++

I have a boost::posix_time::ptime instance and would like to convert ("format") it to a string using a given boost::local_time::time_zone_ptr instance. Below is a test program showing what I currently have. It converts a ptime to a local_date_time which, from my understanding, expresses a time zone in addition to the time information.
When running this program at 2011-08-18 12:00:00 UTC, I expect the output 2011-08-18 14.00.00 UTC+02:00. Instead it prints 2011-08-18 12:00:00 UTC+00:00. i.e. Relative to the printed time zone, the printed time is correct, but it's not in the time zone I used to create the boost::local_time::local_date_time instance.
I currently use the technique suggested in this question to use a custom format string.
#include <iostream>
#include <ctime>
#include <boost/date_time.hpp>
int main(int argc, char ** argv) {
using namespace std;
// Get current time, as an example
boost::posix_time::ptime dt = boost::posix_time::microsec_clock::universal_time();
// Create a time_zone_ptr for the desired time zone and use it to create a local_date_time
boost::local_time::time_zone_ptr zone(new boost::local_time::posix_time_zone("EST"));
boost::local_time::local_date_time dt_with_zone(dt, zone);
std::stringstream strm;
// Set the formatting facet on the stringstream and print the local_date_time to it.
// Ownership of the boost::local_time::local_time_facet object goes to the created std::locale object.
strm.imbue(std::locale(std::cout.getloc(), new boost::local_time::local_time_facet("%Y-%m-%d %H:%M:%S UTC%Q")));
strm << dt_with_zone;
// Print the stream's content to the console
cout << strm.str() << endl;
return 0;
}
How should I convert the local_date_time instance to a string so the date in the string is represented using the time zone specified by the time_zone_ptr instance?

I think boost does not know the time zone specifier. Replace
new boost::local_time::posix_time_zone("EST")
in your code by
new boost::local_time::posix_time_zone("EST-05:00:00")
and everything works fine. If you want to use the common standard names, you have to create a timezone database as described in the boost documentation.

Related

Conversion of date from human-readable format to epoch fails

I'd like to create a program that converts the date of a specific human-readable format to epoch.
So far I have the following code the first part of which creates this human-readable format and the second one converts it to epoch.
#include <time.h>
#include <iostream>
#include <ctime>
#include <string>
#include <cstring>
using namespace std;
int main(int argc, char const *argv[])
{
time_t timeNow;
struct tm *ltm = NULL;
time(&timeNow);
ltm = localtime(&timeNow);
char buffer[100];
strftime(buffer, sizeof(buffer), "%c %Z", ltm);
cout << "human readable timestamp is " << buffer << endl;
std::tm tmNow;
memset(&tmNow, 0, sizeof(tmNow));
strptime(buffer, "%c %Z", &tmNow);
cout << "epoch timestamp is " << mktime(&tmNow) << endl;
return 0;
}
So the printouts I get are the following :
human readable timestamp is Thu Sep 16 10:23:06 2021 EEST
epoch timestamp is 1631780586
My time zone is EEST as one can see but the epoch one is wrong because it is one hour ahead. The correct should have been 1631776986. I assume I'm doing wrong something with the local time. I've found third-party libraries examples like boost or poco that do this conversion, but I'd prefer the above conversion to be done by using native C++.
Does anyone see what I'm missing?
The C timing/calendrical API is very difficult to use correctly (which is why C++ is moving away from it).
From the C standard:
The value of tm_isdst is positive if Daylight Saving Time is in effect, zero if Daylight Saving Time is not in effect, and negative if the information is not available.
Set tmNow.tm_isdst = -1; prior to the call to mktime.

Java to C++ UTC time - finding the Utc Zone Time Offset in seconds

I need to serialize a UTC date time instance from java to c++ using the minimal length of pure ByteBuffer of unsigned chars.
I need the time point with to be able to support minimum nanoseconds precision.
From the java side i have looked in the classes ZonedDateTime and OffsetDateTime and seen what kind of primitive they are using to store the time and all together this is equal to 17 bytes which is kind of a lot.
Please note that i am here not transferring the zone itself since i have not found a way to map those zone strings from IANA to integers. Thus i guess the zone offset will be enough. Since bought sides will have to convert their time point to a particular offset before sending it to the server lets say that will be UTC 0. I am looking for a solution that is queering the local time i.e. no NTP servers involved!
the java side looks like this:
// 4 byte
Integer year = offsetDateTime.getYear();
// 1 byte
Integer month = offsetDateTime.getMonthValue();
// 1 byte
Integer day = offsetDateTime.getDayOfMonth();
// 1 byte
Integer hour = offsetDateTime.getHour();
// 1 byte
Integer minutes = offsetDateTime.getMinute();
// 1 byte
Integer seconds = offsetDateTime.getSecond();
// 4 byte
Integer nanoSeconds = offsetDateTime.getNano();
// 4 byte
Integer utcZoneTimeTotalSecondsOffset = offsetDateTime.getOffset().getTotalSeconds();
on the c++ side i need to do the same as well as to be able to construct a time point from all those integers above.
I have found how to get the the above information except the utcOffset i.e. the last integer.
i would like to use chrono to consume this buffer and instantiate a time point from it. however i am not able to find a way to to get the time offset in c++. How do i get the time offset with chrono and then construct a time point from the above information?
auto now = std::chrono::system_clock::now();
time_t itt = std::chrono::system_clock::to_time_t(now);
tm utc_tm = *gmtime(&itt);
std::cout << utc_tm.tm_year + 1900 << '-';
std::cout << utc_tm.tm_mon + 1 << '-';
std::cout << utc_tm.tm_mday << ' ';
std::cout << utc_tm.tm_hour << ':';
std::cout << utc_tm.tm_min << ':';
std::cout << utc_tm.tm_sec << '\n';
? wher is the time zoneOffset here ?
if i use
utc_tm.tm_gmtoff
this is giving me the wrong information . at least in my case. So i believe gmtoff is not the way to go , but if gmtoff is not the way then what is?
Not a complete answer, but here is one way one might serialize a C++ local time point using Howard Hinnant's free, open source time zone library.
#include "date/tz.h"
#include <iostream>
#include <sstream>
std::string
to_string(date::zoned_time<std::chrono::nanoseconds> t)
{
using namespace std;
using namespace date;
ostringstream out;
out << t.get_sys_time().time_since_epoch().count()
<< ' ' << t.get_time_zone()->name();
return out.str();
}
date::zoned_time<std::chrono::nanoseconds>
from_string(const std::string& s)
{
using namespace std;
using namespace std::chrono;
using namespace date;
istringstream in{s};
long long ns;
std::string tz_name;
in >> ns >> tz_name;
return {tz_name, sys_time<nanoseconds>{nanoseconds{ns}}};
}
int
main()
{
using namespace std;
using namespace std::chrono;
using namespace date;
zoned_time<nanoseconds> zt{current_zone(), system_clock::now()};
cout << zt << '\n';
auto s = to_string(zt);
cout << s << '\n';
zt = from_string(s);
cout << zt << '\n';
}
I'm using a std::string to represent the serialization, which one might send across a network.
This example begins in main with creating a nanosecond-precision local time point, stored in date::zoned_time<nanoseconds>, using the computer's current local time zone, and the current UTC time. main first simply prints this time stamp out, which for me just output:
2019-09-11 12:37:04.846272000 EDT
which is my current local date/time and time zone abbreviation.
Next the program converts this to a string with a small (but not necessarily minimal) number of ASCII characters to completely describe the local time point. I've chosen the format:
<Number of nanoseconds since epoch> <IANA time zone name>
In this example, that string is:
1568219824846272000 America/New_York
This string is formed in to_string, which simply streams out the "time since epoch" of the system time, of the zoned_time. Then adds a space, and streams out the name of the time zone.
from_string reverses this operation by reading in the number of nanoseconds, and then reading in the time zone name. It then forms a zoned_time<nanoseconds> by pairing the time zone name, and forming a sys_time<nanoseconds> with the integer that was parsed.
main prints out the parsed zoned_time to ensure we have a loss-less round trip:
2019-09-11 12:37:04.846272000 EDT
In order to get a loss-less conversion that includes local time, one really needs to transmit the IANA time zone name, not just the current UTC offset. With only the UTC offset, one can recover the exact UTC time point. But one can not perform any local time arithmetic, or comparison with other local time points because one can not know the rules for when one changes the UTC offset. Only with the full IANA time zone name can one pass the information along about those rules.

How do I write a message timestamp to a log file?

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(&current_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.

`boost::local_time::date_time` from `std::string`

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>).

Convert SQLite Date Field to C++ Time_T

How Do I Convert A SQLite Date in the following format to a C++ Time_T Variable?
YYYY-MM-DD HH:MM:SS
You may wish to read this question, which discusses a few approaches. In particular, if you have access to Boost, you can use <boost/date_time/posix_time/posix_time.hpp> and create a boost::posix_time::ptime object from a string of the format in your question. (See the first answer in that question.)
Alternatively, you can use strptime().
Example:
#include <ctime>
#include <iostream>
std::size_t timestamp_to_seconds(const char* timestamp)
{
std::tm tm_struct;
strptime(timestamp, "%Y-%m-%d %H:%M:%S", &tm_struct);
// You can control daylight savings time if necessary.
tm_struct.tm_isdst = 1;
std::size_t t = std::mktime(&tm_struct);
return t;
}
int main()
{
std::cout << timestamp_to_seconds("2013-07-05 12:34:56") << std::endl;
return 0;
}
Running this example gives the following output:
$ ./a.out
1373042096
$ date -d "2013-07-05 12:34:56" +%s
1373042096
As you can see, it agrees with the date utility. (I assuming you are on a platform with access to strptime.) You may have to be careful when handling daylight savings or timezone info...I had to use tm_struct.tm_isdst = 1 to force recognition of daylight savings and get agreement with date.
time_t is a Unix timestamp (seconds since 1970-01-01), so you have to convert with strftime:
SELECT strftime('%s', '2013-07-05 12:34:56');
The result is a string, but you can read it as an integer value.