Parse datetime with timezone using Boost.Date_Time? - c++

I want to parse date time with time-zone using Boost Date Time IO library.
#include <boost/date_time.hpp>
#include <ctime>
#include <sstream>
using namespace boost::gregorian;
using namespace boost::posix_time;
std::chrono::system_clock::time_point ParseDate(const std::wstring& dateText, const wchar_t* const format) {
ptime time;
std::wstringstream buffer(dateText);
buffer.imbue(std::locale(std::locale::classic(), new wtime_input_facet(format)));
buffer >> time;
auto timeInfo = to_tm(time);
auto result = std::chrono::system_clock::from_time_t(std::mktime(&timeInfo));
return result;
}
TEST_CLASS(DateUtilsTest) {
public:
TEST_METHOD(ShouldParseUtcDate) {
auto timePoint = ParseDate(L"2016-12-03T07:09:01-05:00", L"%Y-%m-%dT%H:%M:%S%Q");
auto time = std::chrono::system_clock::to_time_t(timePoint);
auto timePoint2 = ParseDate(L"2016-12-03T07:09:01", L"%Y-%m-%dT%H:%M:%S");
auto time2 = std::chrono::system_clock::to_time_t(timePoint2);
Assert::IsTrue(time != time2);
}
}
Here's an online example: https://wandbox.org/permlink/9GEhah5l4uzhgDta
The above test failed because time == time2.
It seems that the time-zone portion doesn't have any impact to the parsing result.
Do you know how to parse a date time string with time-zone using Boost?
The real case is string like "2017-12-21T10:47:58.299Z" (ISO 8601 format, generated by JavaScript: (new Date()).toISOString()), but I didn't find any doc for this format, any ideas?
Environment:
Boost-1.65.1
System is Windows 10
System time-zone is GMT+8
Visual Studio 2015U3

I've looked at it long and hard. It seems you're almost completely out of luck:
So you can try making it work with %ZP.
Doing The Heroics
I did the heroics, only to find out that the support for wtime_zone and friends is ... incomplete in the library.
Here it is in all it g(l)ory:
Live On Coliru
#include <boost/date_time.hpp>
#include <boost/date_time/local_time/local_time_io.hpp>
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/date_time/time_zone_base.hpp>
#include <ctime>
#include <chrono>
#include <sstream>
namespace DT = boost::date_time;
namespace LT = boost::local_time;
namespace PT = boost::posix_time;
template <typename CharT = wchar_t> struct TypeDefs {
using ptime = PT::ptime;
using tz_base = DT::time_zone_base<ptime, CharT>;
using tz_ptr = boost::shared_ptr<DT::time_zone_base<PT::ptime, CharT> >;
using ptz_t = LT::posix_time_zone_base<CharT>;
using ldt_t = LT::local_date_time_base<ptime, tz_base>;
};
namespace boost { namespace local_time {
//! input operator for local_date_time
template <class CharT, class Traits, typename Defs = TypeDefs<CharT>, typename local_date_time = typename Defs::ldt_t>
inline
std::basic_istream<CharT, Traits>&
operator>>(std::basic_istream<CharT, Traits>& is, local_date_time& ldt)
{
using time_zone_ptr = typename Defs::tz_ptr;
using posix_time_zone = typename Defs::ptz_t;
boost::io::ios_flags_saver iflags(is);
typename std::basic_istream<CharT, Traits>::sentry strm_sentry(is, false);
if (strm_sentry) {
try {
typedef typename local_date_time::utc_time_type utc_time_type;
typedef typename date_time::time_input_facet<utc_time_type, CharT> time_input_facet;
// intermediate objects
std::basic_string<CharT> tz_str;
utc_time_type pt(DT::not_a_date_time);
std::istreambuf_iterator<CharT,Traits> sit(is), str_end;
if(std::has_facet<time_input_facet>(is.getloc())) {
std::use_facet<time_input_facet>(is.getloc()).get_local_time(sit, str_end, is, pt, tz_str);
}
else {
time_input_facet* f = new time_input_facet();
std::locale l = std::locale(is.getloc(), f);
is.imbue(l);
f->get_local_time(sit, str_end, is, pt, tz_str);
}
if(tz_str.empty()) {
time_zone_ptr null_ptr;
// a null time_zone_ptr creates a local_date_time that is UTC
ldt = local_date_time(pt, null_ptr);
}
else {
time_zone_ptr tz_ptr(new posix_time_zone(tz_str));
// the "date & time" constructor expects the time label to *not* be utc.
// a posix_tz_string also expects the time label to *not* be utc.
ldt = local_date_time(pt.date(), pt.time_of_day(), tz_ptr, local_date_time::EXCEPTION_ON_ERROR);
}
}
catch(...) {
// mask tells us what exceptions are turned on
std::ios_base::iostate exception_mask = is.exceptions();
// if the user wants exceptions on failbit, we'll rethrow our
// date_time exception & set the failbit
if(std::ios_base::failbit & exception_mask) {
try { is.setstate(std::ios_base::failbit); }
catch(std::ios_base::failure&) {} // ignore this one
throw; // rethrow original exception
}
else {
// if the user want's to fail quietly, we simply set the failbit
is.setstate(std::ios_base::failbit);
}
}
}
return is;
}
} }
template <typename CharT = wchar_t> struct DateUtilsBase : TypeDefs<CharT> {
using base = TypeDefs<CharT>;
using typename base::ldt_t;
using typename base::tz_ptr;
using typename base::ptime;
static std::tm to_tm(ldt_t const& lt) {
std::tm v = PT::to_tm(lt.local_time());
v.tm_isdst = lt.is_dst()? 1:0;
return v;
}
static tz_ptr s_GMT;
static std::chrono::system_clock::time_point Parse(const std::basic_string<CharT>& dateText, const CharT* const format) {
ldt_t value(LT::special_values::not_a_date_time, s_GMT);
std::basic_istringstream<CharT> buffer(dateText);
buffer.imbue(std::locale(std::locale::classic(), new DT::time_input_facet<ptime, CharT>(format)));
std::basic_string<CharT> dummy;
if (buffer >> value && (buffer >> dummy).eof()) {
std::cout << "DEBUG: " << value.utc_time() << " EOF:" << buffer.eof() << "\n";
auto timeInfo = PT::to_tm(value.utc_time());
return std::chrono::system_clock::from_time_t(std::mktime(&timeInfo));
} else {
return std::chrono::system_clock::time_point::min();
}
}
};
template <> typename DateUtilsBase<wchar_t>::tz_ptr DateUtilsBase<wchar_t>::s_GMT { new ptz_t(L"GMT") } ;
template <> typename DateUtilsBase<char>::tz_ptr DateUtilsBase<char>::s_GMT { new ptz_t("GMT") } ;
#if 1
using DateUtils = DateUtilsBase<wchar_t>;
#define T(lit) L##lit
#else
using DateUtils = DateUtilsBase<char>;
#define T(lit) lit
#endif
int main() {
using namespace std::chrono_literals;
using C = std::chrono::system_clock;
std::cout << std::boolalpha << std::unitbuf;
C::time_point with_zone, without_zone;
// all three equivalent:
with_zone = DateUtils::Parse(T("2016-12-03T07:09:01 PST-05:00"), T("%Y-%m-%dT%H:%M:%S%ZP"));
with_zone = DateUtils::Parse(T("2016-12-03T07:09:01 -05:00"), T("%Y-%m-%dT%H:%M:%S%ZP"));
with_zone = DateUtils::Parse(T("2016-12-03T07:09:01-05:00"), T("%Y-%m-%dT%H:%M:%S%ZP"));
without_zone = DateUtils::Parse(T("2016-12-03T07:09:01"), T("%Y-%m-%dT%H:%M:%S"));
std::cout << "time_point equal? " << (with_zone == without_zone) << "\n";
{
std::time_t t_with_zone = C::to_time_t(with_zone);
std::time_t t_without_zone = C::to_time_t(without_zone);
std::cout << "time_t equal? " << (t_with_zone == t_without_zone) << "\n";
}
std::cout << (without_zone - with_zone) / 1h << " hours difference\n";
}
Yep. That's a bit of a monstrosity. It prints:
DEBUG: 2016-Dec-03 12:09:01 EOF:true
DEBUG: 2016-Dec-03 12:09:01 EOF:true
DEBUG: 2016-Dec-03 12:09:01 EOF:true
DEBUG: 2016-Dec-03 07:09:01 EOF:true
time_point equal? false
time_t equal? false
-5 hours difference
Back To Sanity
In fact, the library authors (wisely) decided that even though streams would be wide or narrow, the local_date_time (or really, just the strings in their time-zone representations) need not be. This is why the library supplied operator>> only supports local_date_time and employs the internal helper function convert_string_type to coerce to narrow-char timezone info:
time_zone_ptr tz_ptr(new posix_time_zone(date_time::convert_string_type<CharT,char>(tz_str)));
With that in mind let's remove a lot "generic-y" cruft. What remains is the addition of error-handling:
if (buffer >> value && (buffer >> dummy).eof()) {
//std::cout << "DEBUG: " << value.utc_time() << " EOF:" << buffer.eof() << "\n";
auto timeInfo = boost::posix_time::to_tm(value.utc_time());
return std::chrono::system_clock::from_time_t(std::mktime(&timeInfo));
} else {
return std::chrono::system_clock::time_point::min();
}
Live On Coliru
#include <boost/date_time.hpp>
#include <boost/date_time/local_time/local_time_io.hpp>
#include <ctime>
#include <chrono>
#include <sstream>
struct DateUtils {
using ptime = boost::posix_time::ptime;
using time_zone_ptr = boost::local_time::time_zone_ptr;
using local_date_time = boost::local_time::local_date_time;
template <typename CharT>
static std::chrono::system_clock::time_point Parse(const std::basic_string<CharT>& dateText, const CharT* const format) {
static time_zone_ptr s_GMT(new boost::local_time::posix_time_zone("GMT"));
local_date_time value(boost::local_time::special_values::not_a_date_time, s_GMT);
std::basic_istringstream<CharT> buffer(dateText);
buffer.imbue(std::locale(std::locale::classic(), new boost::date_time::time_input_facet<ptime, CharT>(format)));
std::basic_string<CharT> dummy;
if (buffer >> value && (buffer >> dummy).eof()) {
//std::cout << "DEBUG: " << value.utc_time() << " EOF:" << buffer.eof() << "\n";
auto timeInfo = boost::posix_time::to_tm(value.utc_time());
return std::chrono::system_clock::from_time_t(std::mktime(&timeInfo));
} else {
return std::chrono::system_clock::time_point::min();
}
}
};
#if 1
using CharT = wchar_t;
#define T(lit) L##lit
#else
using CharT = char;
#define T(lit) lit
#endif
int main() {
using namespace std::chrono_literals;
using C = std::chrono::system_clock;
std::cout << std::boolalpha << std::unitbuf;
C::time_point with_zone, without_zone;
// all three equivalent:
with_zone = DateUtils::Parse<CharT>(T("2016-12-03T07:09:01 PST-05:00"), T("%Y-%m-%dT%H:%M:%S%ZP"));
with_zone = DateUtils::Parse<CharT>(T("2016-12-03T07:09:01 -05:00"), T("%Y-%m-%dT%H:%M:%S%ZP"));
with_zone = DateUtils::Parse<CharT>(T("2016-12-03T07:09:01-05:00"), T("%Y-%m-%dT%H:%M:%S%ZP"));
without_zone = DateUtils::Parse<CharT>(T("2016-12-03T07:09:01"), T("%Y-%m-%dT%H:%M:%S"));
std::cout << "time_point equal? " << (with_zone == without_zone) << "\n";
{
std::time_t t_with_zone = C::to_time_t(with_zone);
std::time_t t_without_zone = C::to_time_t(without_zone);
std::cout << "time_t equal? " << (t_with_zone == t_without_zone) << "\n";
}
std::cout << (without_zone - with_zone) / 1h << " hours difference\n";
}
Whew. From 151 LoC down to 64 LoC. Better
Prints:
time_point equal? false
time_t equal? false
-5 hours difference
Summary:
read (all) the notes in the docs
use %ZP as the only supported input format
use local_date_time because that format string is ignored with ptime (it says so in the notes)
use error handling to make sure no "unparsed" things are left behind in the input

Related

Advice on converting timestamp string in "HH:MM:SS.microseconds" format

I'm given a list of timestamps (suppose we have a ready-made std::vector<std::string>) in a string format of a kind std::vector<std::string> = {"12:27:37.740002", "19:37:17.314002", "20:00:07.140902",...}. No dates, no timezones. What would be a preferable way to parse these strings to some kind of C++ type (std::chrono::time_point ?) to be able to perform some comparisons and sorting later.
For example: compare value, which was parsed from "20:00:07.140902" and value, was parsed from "20:00:07.000000".
C++17 is ok, but I can't use any third-party library (Boost, Date etc).
Keeping microseconds precision essential.
You can build this functionality completly with C++ standard library functionality.
For parsing the string use std::regex.
For time related datatypes use std::chrono
Example :
#include <stdexcept>
#include <regex>
#include <chrono>
#include <iostream>
auto parse_to_timepoint(const std::string& input)
{
// setup a regular expression to parse the input string
// https://regex101.com/
// each part between () is a group and will end up in the match
// [0-2] will match any character from 0 to 2 etc..
// [0-9]{6} will match exactly 6 digits
static const std::regex rx{ "([0-2][0-9]):([0-5][0-9]):([0-5][0-9])\\.([0-9]{6})" };
std::smatch match;
if (!std::regex_search(input, match, rx))
{
throw std::invalid_argument("input string is not a valid time string");
}
// convert each matched group to the corresponding value
// note match[0] is the complete matched string by the regular expression
// we only need the groups which start at index 1
const auto& hours = std::stoul(match[1]);
const auto& minutes = std::stoul(match[2]);
const auto& seconds = std::stoul(match[3]);
const auto& microseconds = std::stoul(match[4]);
// build up a duration
std::chrono::high_resolution_clock::duration duration{};
duration += std::chrono::hours(hours);
duration += std::chrono::minutes(minutes);
duration += std::chrono::seconds(seconds);
duration += std::chrono::microseconds(microseconds);
// then return a time_point (note this will not help you with correctly handling day boundaries)
// since there is no date in the input string
return std::chrono::high_resolution_clock::time_point{ duration };
}
int main()
{
std::string input1{ "20:00:07.140902" };
std::string input2{ "20:00:07.000000" };
auto tp1 = parse_to_timepoint(input1);
auto tp2 = parse_to_timepoint(input2);
std::cout << "start time = " << ((tp1 < tp2) ? input1 : input2) << "\n";
std::cout << "end time = " << ((tp1 >= tp2) ? input1 : input2) << "\n";
return 0;
}
I don't see why this shouldn't work. Using std::chrono::from_stream to parse the string into a time point, then just compare the two time points.
However, I've been trying it now with Visual Studio 2022 17.0.2 (Community Edition) and it fails to parse the string into a tp.
There is this answer from Ted Lyngmo's talking about a bug (fixed in VS2022 17.0.3) when parsing seconds with subseconds. I have to say though that his solution didn't work for me either in my VS2022.
Anyway, you may want to give it a try.
#include <chrono>
#include <iomanip> // boolalpha
#include <iostream> // cout
#include <sstream> // istringstream
#include <string>
auto parse_string_to_tp(const std::string& str)
{
std::istringstream iss{ str };
std::chrono::sys_time<std::chrono::microseconds> tp{};
std::chrono::from_stream(iss, "%H:%M:%S", tp); // or simply "%T"
return tp;
}
int main()
{
const std::string str1{ "12:27:37.740002" };
const std::string str2{ "13:00:00.500000" };
auto tp1{ parse_string_to_tp(str1) };
auto tp2{ parse_string_to_tp(str2) };
std::cout << "tp1 < tp2: " << std::boolalpha << (tp1 < tp2) << "\n";
std::cout << "tp2 < tp1: " << std::boolalpha << (tp2 < tp1) << "\n";
}
EDIT: it works if you just use durations instead of time points:
#include <chrono>
#include <iomanip> // boolalpha
#include <iostream> // cout
#include <sstream> // istringstream
#include <string>
auto parse_string_to_duration(const std::string& str)
{
std::istringstream iss{ str };
std::chrono::microseconds d{};
std::chrono::from_stream(iss, "%T", d);
return d;
}
int main()
{
const std::string str1{ "12:27:37.740002" };
const std::string str2{ "23:39:48.500000" };
auto d1{ parse_string_to_duration(str1) };
auto d2{ parse_string_to_duration(str2) };
std::cout << "d1 < d2: " << std::boolalpha << (d1 < d2) << "\n";
std::cout << "d2 < d1: " << std::boolalpha << (d2 < d1) << "\n";
}

C++ RFC3339 timestamp with milliseconds using std::chrono

I'm creating an RFC3339 timestamp, including milliseconds and in UTC, in C++ using std::chrono like so:
#include <chrono>
#include <ctime>
#include <iomanip>
using namespace std;
using namespace std::chrono;
string now_rfc3339() {
const auto now = system_clock::now();
const auto millis = duration_cast<milliseconds>(now.time_since_epoch()).count() % 1000;
const auto c_now = system_clock::to_time_t(now);
stringstream ss;
ss << put_time(gmtime(&c_now), "%FT%T") <<
'.' << setfill('0') << setw(3) << millis << 'Z';
return ss.str();
}
// output like 2019-01-23T10:18:32.079Z
(forgive the usings)
Is there a more straight forward way of getting the milliseconds of now? It seems somewhat cumbersome to %1000 the now in milliseconds to get there. Or any other comments on how to do this more idiomatic?
You could also do this with subtraction:
string
now_rfc3339()
{
const auto now_ms = time_point_cast<milliseconds>(system_clock::now());
const auto now_s = time_point_cast<seconds>(now_ms);
const auto millis = now_ms - now_s;
const auto c_now = system_clock::to_time_t(now_s);
stringstream ss;
ss << put_time(gmtime(&c_now), "%FT%T")
<< '.' << setfill('0') << setw(3) << millis.count() << 'Z';
return ss.str();
}
This avoids the "magic number" 1000.
Also, there is Howard Hinnant's free, open source, single-header, header-only datetime library:
string
now_rfc3339()
{
return date::format("%FT%TZ", time_point_cast<milliseconds>(system_clock::now()));
}
This does the same thing but with easier syntax.

Using Boost Karma to print boost::posix_time::time_duration

I am trying to write a simple generator for boost::posix_time::duration to use in another generator. The sticking point for me now is the "+" or "-" which I wanted printed. What I have so far is:
struct GetSign
{
template<typename> struct result { typedef char type; };
template<typename TimeDur>
const char operator()(const TimeDur& dur) const
{
return dur.is_negative() ? '-' : '+';
}
};
boost::phoenix::function<GetSign> phx_getsign;
struct TimeDurationGenerator
: boost::spirit::karma::grammar<boost::spirit::ostream_iterator, boost::posix_time::time_duration()>
{
TimeDurationGenerator()
: TimeDurationGenerator::base_type(start_)
{
namespace bsk = boost::spirit::karma;
namespace bpt = boost::posix_time;
start_
= sign_[bsk::_1 = phx_getsign(bsk::_val)]
<< bsk::right_align(2,'0')[bsk::int_[bsk::_1 = boost::phoenix::bind(&bpt::time_duration::hours, bsk::_val)]]
<< ':'
<< bsk::right_align(2,'0')[bsk::int_[bsk::_1 = boost::phoenix::bind(&bpt::time_duration::minutes,bsk::_val)]];
}
boost::spirit::karma::rule<boost::spirit::ostream_iterator, char()> sign_;
boost::spirit::karma::rule<boost::spirit::ostream_iterator, boost::posix_time::time_duration()> start_;
};
While this does compile (at least on clang) it does not work, as there is no output. When I include this generator in another generator, output always stops when it gets to here. If I remove the sign_[...] portion of the rule, then it work.
How can I get this to work?
You never defined sign_. In fact, you don't need it:
char_[_1 = phx_getsign(_val)]
But I'd refrain from forcing the square peg into the round hole. If you need that level of control, make a primitive generator that does it. In fact, IO stream manipulators have you covered:
Live On Coliru
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/spirit/include/karma.hpp>
#include <iomanip>
namespace bsk = boost::spirit::karma;
namespace bpt = boost::posix_time;
template <typename It = boost::spirit::ostream_iterator>
struct TimeDurationGenerator : bsk::grammar<It, bpt::time_duration()>
{
TimeDurationGenerator() : TimeDurationGenerator::base_type(start_) {
duration_ = bsk::stream;
start_ = duration_;
}
private:
struct wrap {
bpt::time_duration d;
wrap(bpt::time_duration const& d) : d(d) {}
friend std::ostream& operator<<(std::ostream& os, wrap const& w) {
return os << std::setfill('0') << std::internal
<< std::setw(3) << std::showpos << w.d.hours() << ":"
<< std::setw(2) << std::noshowpos << std::abs(w.d.minutes());
}
};
bsk::rule<It, bpt::time_duration()> start_;
bsk::rule<It, wrap()> duration_;
};
int main() {
for (auto str : { "-7:30", "7", "323:87:13" }) {
std::cout << format(TimeDurationGenerator<>{}, bpt::duration_from_string(str)) << "\n";
}
}
Prints
-07:30
+07:00
+324:27

Boost, how to parse following string to date/time

I have the following milli/micro second accuracy string to parse into some sort of boost datetime.
std::string cell ="20091201 00:00:04.437";
I have seen documentation regarding facets. Something like this
date_input_facet* f = new date_input_facet();
f->format("%Y%m%d %F *");
but I don't know how to use them.
I tried this program with code scavenged from StackOverflow, but I can't get the millis to show:
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <map>
#include <boost/algorithm/string.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time.hpp>
namespace bt = boost::posix_time;
const std::locale formats[] =
{
std::locale(std::locale::classic(),new bt::time_input_facet("%Y%m%d %H:%M:%S.f")),
std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d %H:%M:%S")),
std::locale(std::locale::classic(),new bt::time_input_facet("%Y/%m/%d %H:%M:%S")),
std::locale(std::locale::classic(),new bt::time_input_facet("%d.%m.%Y %H:%M:%S")),
std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d"))
};
const size_t formats_n = sizeof(formats) / sizeof(formats[0]);
std::time_t pt_to_time_t(const bt::ptime& pt)
{
bt::ptime timet_start(boost::gregorian::date(1970,1,1));
bt::time_duration diff = pt - timet_start;
return diff.ticks()/bt::time_duration::rep_type::ticks_per_second;
}
void seconds_from_epoch(const std::string& s)
{
bt::ptime pt;
for(size_t i = 0; i < formats_n; ++i)
{
std::istringstream is(s);
is.imbue(formats[i]);
is >> pt;
if(pt != bt::ptime()) break;
}
bt::time_duration td = pt.time_of_day();
long fs = td.fractional_seconds();
std::cout << " ptime is " << pt << '\n';
std::cout << " seconds from epoch are " << pt_to_time_t(pt) << " " << fs << '\n';
}
int main(int, char *argv[])
{
std::string cell ="20091201 00:00:04.437";
seconds_from_epoch(cell);
int enterAnumber;
std::
cin >> enterAnumber;
}
boost::posix_time::time_from_string is very rigid when it comes to parsing formats.
You are looking for a different way to create a boost::posix_time::ptime from an std::string. You want to imbue a stringstream with the format, as such:
const std::string cell = "20091201 00:00:04.437";
const std::locale loc = std::locale(std::locale::classic(), new boost::posix_time::time_input_facet("%Y%m%d %H:%M:%S%f"));
std::istringstream is(cell);
is.imbue(loc);
boost::posix_time::ptime t;
is >> t;
Then
std::cout << t << std::endl;
gives
2009-Dec-01 00:00:04.437000

How to convert boost local_date_time to time_t

I have:
time_t dataFromTodayByAddingYearsMonthsDays(int years, int months, int days)
{
using namespace boost::local_time;
local_date_time local = local_sec_clock::local_time(time_zone_ptr());
local += boost::gregorian::years(years);
local += boost::gregorian::months(months);
local += boost::gregorian::days(days);
return ???;
}
How do I convert this boost local_date_time beast to time_t?
Here's the beef of the answer:
time_t to_time_t(boost::posix_time::ptime const& pt) //! assumes UTC
{
return (pt - boost::posix_time::from_time_t(0)).total_seconds();
}
I'd write the date calculation more succinctly, while you're at it:
int main()
{
using namespace boost::local_time;
auto now = local_sec_clock::local_time(time_zone_ptr()),
then = now + ymd_duration { 1, 3, -4 };
std::cout << now << ", " << to_time_t(now.utc_time()) << "\n";
std::cout << then << ", " << to_time_t(then.utc_time()) << "\n";
}
See it Live On Coliru, prints
2014-May-12 21:50:06 UTC, 1399931406
2015-Aug-08 21:50:06 UTC, 1439070606
Full Code:
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/date_time/local_time/local_time_io.hpp>
struct ymd_duration { int years, months, day; };
template <typename T>
T operator+(T const& pt, ymd_duration delta)
{
using namespace boost::gregorian;
return pt + years(delta.years) + months(delta.months) + days(delta.day);
}
time_t to_time_t(boost::posix_time::ptime const& pt) //! assumes UTC
{
return (pt - boost::posix_time::from_time_t(0)).total_seconds();
}
int main()
{
using namespace boost::local_time;
auto now = local_sec_clock::local_time(time_zone_ptr()),
then = now + ymd_duration { 1, 3, -4 };
std::cout << now << ", " << to_time_t(now.utc_time()) << "\n";
std::cout << then << ", " << to_time_t(then.utc_time()) << "\n";
}
time_t dataFromTodayByAddingYearsMonthsDays(int years, int months, int days)
{
using namespace boost::local_time;
using namespace boost::posix_time;
local_date_time local = local_sec_clock::local_time(time_zone_ptr());
local += boost::gregorian::years(years);
local += boost::gregorian::months(months);
local += boost::gregorian::days(days);
ptime utc = local.utc_time();
ptime epoch(boost::gregorian::date(1970, 1, 1));
time_duration::sec_type diff = (utc - epoch).total_seconds();
return time_t(diff);
}