How to create a date range in boost datetime? - c++

I would like to create a range of timestamps, between a start date and end date and a chosen frequency. For example given these parameters as start date, end date and the frequency:
2002-01-20 23:59:59.000
2002-01-21 04:59:59.000
Freq = Hour
It should return a vector/list of timestamps:
2002-01-21 00:00:00.000
2002-01-21 01:00:00.000
2002-01-21 02:00:00.000
2002-01-21 03:00:00.000
2002-01-21 04:00:00.000
Does the Boost:date_time library have a function to achieve this?

Boost has two related things:
time_period
time iterators
The first literally gives you a date-range. The second is what you were looking for I think.
time_period:
Sample:
#include "boost/date_time/posix_time/posix_time.hpp"
#include <iostream>
using namespace boost::posix_time;
using namespace boost::gregorian;
struct day_period : time_period {
day_period(date d) : time_period(ptime(d), ptime(d, hours(24))) {}
};
int main() {
const date d(2002, Feb, 1); // an arbitrary date
const day_period dp(d); // the containing day
ptime t(d, hours(3) + seconds(5)); // an arbitray time on that day
if (dp.contains(t)) {
std::cout << to_simple_string(dp) << " contains " << to_simple_string(t) << std::endl;
}
// a period that represents part of the day
time_period part_of_day(ptime(d, hours(0)), t);
// intersect the 2 periods and print the results
if (part_of_day.intersects(dp)) {
time_period result = part_of_day.intersection(dp);
std::cout
<< to_simple_string(dp) << " intersected with\n"
<< to_simple_string(part_of_day) << " is \n"
<< to_simple_string(result) << std::endl;
}
}
Prints
[2002-Feb-01 00:00:00/2002-Feb-01 23:59:59.999999999]
contains 2002-Feb-01 03:00:05
[2002-Feb-01 00:00:00/2002-Feb-01 23:59:59.999999999]
intersected with
[2002-Feb-01 00:00:00/2002-Feb-01 03:00:04.999999999]
is
[2002-Feb-01 00:00:00/2002-Feb-01 03:00:04.999999999]
Time Iterators
Sample:
#include "boost/date_time/posix_time/posix_time.hpp"
#include <iostream>
int main() {
using namespace boost::gregorian;
using namespace boost::posix_time;
ptime start(date(2000, Jan, 20)), end = start + hours(1);
time_iterator titr(start, minutes(15)); // increment by 15 minutes
// produces 00:00:00, 00:15:00, 00:30:00, 00:45:00
while (titr < end) {
std::cout << to_simple_string(*titr) << std::endl;
++titr;
}
std::cout << "Now backward" << std::endl;
// produces 01:00:00, 00:45:00, 00:30:00, 00:15:00
while (titr > start) {
std::cout << to_simple_string(*titr) << std::endl;
--titr;
}
}

Related

Performing date time arithmetic in custom date time class

I have a very naive struct representing date time which I would like to perform arithmetic on:
struct MyDateTime
{
MyDateTime(int year, int month, int day, uint64_t nanos);
int year;
int month;
int day;
uint64_t nanosSinceMidnight;
};
I'd like to be able to add/subtract MyDateTime from another MyDateTime.
My idea was to make my struct a wrapper and use Boost internally.
I looked at Boost Posix Time:
https://www.boost.org/doc/libs/1_55_0/doc/html/date_time/examples.html#date_time.examples.time_math
But this seems to only be doing time math (not accounting for the date component).
I looked at Boost Gregorian Date but I couldn't see any time argument in the constructors.
What is the simplest way to use Boost, so I can perform datetime arithmetic?
As you may have realized by now, dates cannot be added.
Dates and timestamps are mathematically akin to tensors, in that their difference type is in a different domain.
When you commented that time_duration doesn't include a date, you still had a point though.
Because the time_duration might be the time-domain difference type (the difference type ptime) but we need an analog for the date-part of ptime, which is boost::gregorian::date.
Boost Gregorian dates are basically blessed tuples of (yyyy,mm,dd).So a natural difference type would just be a signed integral number of days. And that's exactly* what boost::gregorian::date_duration is:
boost::gregorian::date_duration x = date{} - date{};
boost::posix_time::time_duration y = ptime{} - ptime{};
Because that type is implemented in the Gregorian module you will get correct differences, even with special cases like leap days and other anomalies: https://www.calendar.com/blog/gregorian-calendar-facts/
So, you could in fact use that type as a difference type, just for the ymd part.
Simplify
The good news is, you don't have to bother: boost::posix_time::ptime encapsulates a full boost::gregorian::date, hence when you get a boost::posix_time::time_duration from subtracting ptimes, you will already get the number of days ciphered in:
#include <boost/date_time.hpp>
int main() {
auto now = boost::posix_time::microsec_clock::local_time();
auto later = now + boost::posix_time::hours(3);
auto tomorrow = later + boost::gregorian::days(1);
auto ereweek = later - boost::gregorian::weeks(1);
std::cout << later << " is " << (later - now) << " later than " << now
<< std::endl;
std::cout << tomorrow << " is " << (tomorrow - later) << " later than " << later
<< std::endl;
std::cout << ereweek << " is " << (ereweek - now) << " later than " << now
<< std::endl;
}
Starting from the current time we add 3 hours, 1 day and then subtract a week. It prints: Live On Coliru:
2021-Mar-28 01:50:45.095670 is 03:00:00 later than 2021-Mar-27 22:50:45.095670
2021-Mar-29 01:50:45.095670 is 24:00:00 later than 2021-Mar-28 01:50:45.095670
2021-Mar-21 01:50:45.095670 is -165:00:00 later than 2021-Mar-27 22:50:45.095670
Note that 24h is 1 day, and -165h is (7*24 - 3) hours ago.
There's loads of smarts in the Gregorian calendar module:
std::cout << date{2021, 2, 1} - date{2020, 2, 1} << std::endl; // 366
std::cout << date{2020, 2, 1} - date{2019, 2, 1} << std::endl; // 365
Taking into account leap days. But also knowing the varying lengths of a calendar month in context:
auto term = boost::gregorian::months(1);
for (date origin : {date{2021, 2, 17}, date{2021, 3, 17}}) {
std::cout << ((origin + term) - origin) << std::endl;
};
Prints 28 and 31 respectively.
Applying It To Your Type
I'd suggest keeping with the library difference type, as clearly you had not previously given it any thought that you needed one. By simply creating some interconversions you can have your cake and eat it too:
struct MyDateTime {
MyDateTime(int year = 1970, int month = 1, int day = 1, uint64_t nanos = 0)
: year(year),
month(month),
day(day),
nanosSinceMidnight(nanos) {}
operator ptime() const {
return {date(year, month, day),
microseconds(nanosSinceMidnight / 1'000)};
}
explicit MyDateTime(ptime const& from)
: year(from.date().year()),
month(from.date().month()),
day(from.date().day()),
nanosSinceMidnight(from.time_of_day().total_milliseconds() * 1'000) {}
private:
int year;
int month;
int day;
uint64_t nanosSinceMidnight;
};
Now, I would question the usefulness of keeping your MyDateTime type, but I realize legacy code exists, and sometimes you require a longer time period while moving away from it.
Nanoseconds
Nanosecond precision is not enabled by default. You need to [opt in to use that](https://www.boost.org/doc/libs/1_58_0/doc/html/date_time/details.html#boost-common-heading-doc-spacer:~:text=To%20use%20the%20alternate%20resolution%20(96,the%20variable%20BOOST_DATE_TIME_POSIX_TIME_STD_CONFIG%20must%20be%20defined).
In the sample below I do.
Be careful that al the translation units in your project use the define, or you will cause ODR violations.
Live Demo
Adding some convenience operator<< as well:
Live On Coliru
#define BOOST_DATE_TIME_POSIX_TIME_STD_CONFIG
#include <boost/date_time.hpp>
#include <vector>
using boost::posix_time::ptime;
using boost::gregorian::date;
using boost::posix_time::nanoseconds;
struct MyDateTime {
MyDateTime(MyDateTime const&) = default;
MyDateTime& operator=(MyDateTime const&) = default;
MyDateTime(int year = 1970, int month = 1, int day = 1, uint64_t nanos = 0)
: year(year),
month(month),
day(day),
nanosSinceMidnight(nanos) {}
operator ptime() const {
return {date(year, month, day), nanoseconds(nanosSinceMidnight)};
}
/*explicit*/ MyDateTime(ptime const& from)
: year(from.date().year()),
month(from.date().month()),
day(from.date().day()),
nanosSinceMidnight(from.time_of_day().total_nanoseconds()) {}
private:
friend std::ostream& operator<<(std::ostream& os, MyDateTime const& dt) {
auto save = os.rdstate();
os << std::dec << std::setfill('0') << std::setw(4) << dt.year << "/"
<< std::setw(2) << dt.month << "/" << std::setw(2) << dt.day << " +"
<< dt.nanosSinceMidnight;
os.setstate(save);
return os;
}
int year;
int month;
int day;
uint64_t nanosSinceMidnight;
};
int main() {
namespace g = boost::gregorian;
namespace p = boost::posix_time;
using p::time_duration;
std::vector<time_duration> terms{p::seconds(30), p::hours(-168),
p::minutes(-15),
p::nanoseconds(60'000'000'000 * 60 * 24)};
for (auto mydt : {MyDateTime{2021, 2, 17}, MyDateTime{2021, 3, 17}}) {
std::cout << "---- Origin: " << mydt << "\n";
for (time_duration term : terms) {
mydt = ptime(mydt) + term;
std::cout << "Result: " << mydt << "\n";
}
};
}
Prints
---- Origin: 2021/02/17 +0
Result: 2021/02/17 +30000000000
Result: 2021/02/10 +30000000000
Result: 2021/02/09 +85530000000000
Result: 2021/02/10 +85530000000000
---- Origin: 2021/03/17 +0
Result: 2021/03/17 +30000000000
Result: 2021/03/10 +30000000000
Result: 2021/03/09 +85530000000000
Result: 2021/03/10 +85530000000000

Get year/month/day h/m/s/ns from Boost local_date_time object?

I have converted a timestamp (UTC) since Epoch to a boost::posix_time::ptime and applied a timezone, resulting in a boost::local_time::local_date_time object. However, I cannot retrieve the year, month, day, hh/mm/ss/ns or nanoseconds since midnight for the adjusted date time.
It appears the local_date_time object doesn't provide getters for these.
I can't get these from the boost::posix_time::ptime because it hasn't been shifted for timezone.
What's the best approach?
using namespace boost::gregorian;
using namespace boost::local_time;
using namespace boost::posix_time;
// Create timezone
tz_database tz_db;
tz_db.load_from_file("libs/date_time/data/date_time_zonespec.csv");
time_zone_ptr chicago_tz = tz_db.time_zone_from_region("America/Chicago");
// Create Epoch offset (seconds)
std::time_t btime_ = nanosSinceEpochUTC / 1E9;
ptime dateTime = boost::posix_time::from_time_t(btime_);
// Create local Chicago time at Epoch offset
const local_date_time chicago(dateTime, chicago_tz);
// I need to retrieve the year/month/day/hh/mm/ss etc from the adjusted time, not the ptime.
The time properties are in the time_of_day() sub object. The representation type of that subobject is time_duration and it has all the accessors you want:
Live On Coliru
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/date_time/tz_db_base.hpp>
int main() {
// Create timezone
boost::local_time::tz_database tz_db;
{
std::istringstream iss(R"("America/Chicago","CST","Central Standard Time","CDT","Central Daylight Time","-06:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00")");
tz_db.load_from_stream(iss);
}
auto chicago_tz = tz_db.time_zone_from_region("America/Chicago");
// Create Epoch offset (seconds)
std::time_t btime_ = 1596241091; // nanosSinceEpochUTC / 1E9;
auto dateTime = boost::posix_time::from_time_t(btime_);
std::cout << "ptime: " << dateTime << "\n";
// Create local Chicago time at Epoch offset
const boost::local_time::local_date_time chicago(dateTime, chicago_tz);
std::cout << "local_date_time: " << chicago << "\n";
// I need to retrieve the year/month/day/hh/mm/ss etc from the adjusted
// time, not the ptime.
std::cout << "year/m/d: " << chicago.local_time().date() << "\n";
auto tod = chicago.local_time().time_of_day();
std::cout << "time_of_day: " << tod << "\n";
std::cout << "hh, mm, ss: " <<
tod.hours() << ", " <<
tod.minutes() << ", " <<
tod.seconds() << "\n";
}
As you can see, it's important to access through the local_time() accessor. Note how all the items (day, month, hours) wrapped back correctly according to the time zone.
Prints:
ptime: 2020-Aug-01 00:18:11
local_date_time: 2020-Jul-31 19:18:11 CDT
year/m/d: 2020-Jul-31
time_of_day: 19:18:11
hh, mm, ss: 19, 18, 11
What I do is as follows: I am naming one class after the name of a time zone. In my case gmt and fetch everything via instances of my very own time as well as date class.
class gmt {
string get(string& s) {
this->dt= this->d.getDay();
this->tm= this->d.getTime();
s= "expires="+this->d.getName(this->d.getDayName())
+", " +this->dt.substr( 0, 2) +"-" +this->d.getMonthName() +"-" +this->dt.substr( 8, 2)
+" " +this->tm +" GMT";
return s;
}
friend class cookie;
string dt, tm;
MyDate d;
};
Okay, it isn't "boost" but you can solve it via its own std and isn't that contrary.

Create a time_point at seconds::max results in negative value

I'm trying to use a time_point to effectively represent forever by setting it to seconds::max which, I believe, should represent that much time since epoch. When doing this, though, I get -1 as the time since epoch in the resulting time_point. What am I not understanding?
#include <iostream>
#include <chrono>
using namespace std;
using namespace std::chrono;
int main() {
auto tp1 = system_clock::time_point( seconds::zero() );
auto tp2 = system_clock::time_point( seconds::max() );
cout << "tp1: " << duration_cast<seconds>(tp1.time_since_epoch()).count() << endl;
cout << "tp2: " << duration_cast<seconds>(tp2.time_since_epoch()).count() << endl;
return 0;
}
The output running that is:
tp1: 0
tp2: -1
Here's a little quick&dirty program to explore the limits of system_clock time_points at different precisions:
#include <chrono>
#include <iostream>
using days = std::chrono::duration
<int, std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>>;
using years = std::chrono::duration
<double, std::ratio_multiply<std::ratio<146097, 400>, days::period>>;
template <class Rep, class Period>
void
max_limit(std::chrono::duration<Rep, Period> d)
{
std::cout << "[" << Period::num << '/' << Period::den << "] ";
std::cout << years{d.max()}.count() + 1970 << '\n';
}
int
main()
{
using namespace std;
using namespace std::chrono;
max_limit(nanoseconds{});
max_limit(microseconds{});
max_limit(milliseconds{});
max_limit(seconds{});
}
This will output the year (in floating point) that time_point<system_clock, D> will max out at for any duration D. This program outputs:
[1/1000000000] 2262.28
[1/1000000] 294247
[1/1000] 2.92279e+08
[1/1] 2.92277e+11
Meaning system_clock based on nanoseconds overflows in the year 2262. If you coarsen that to microseconds, you overflow in the year 294,247. And so on.
Once you coarsen to seconds, the max goes out to a ridiculous range. But when you convert that back to system_clock::time_point, which is at least as fine as microseconds, and perhaps as fine as nanoseconds (depending on your platform), you just blow it out of the water.
To solve your problem I recommend:
auto M = system_clock::time_point::max();
Adding a few more diagnostics shows the issue (on my system):
#include <iostream>
#include <chrono>
using namespace std;
using namespace std::chrono;
int main() {
auto tp1 = system_clock::time_point( seconds::zero() );
auto tp2 = system_clock::time_point( seconds::max() );
using type = decltype(system_clock::time_point(seconds::zero()));
cout << type::duration::max().count() << endl;
cout << type::duration::period::den << endl;
cout << type::duration::period::num << endl;
cout << seconds::max().count() << endl;
cout << milliseconds::max().count() << endl;
cout << "tp1: " << duration_cast<seconds>(tp1.time_since_epoch()).count() << endl;
cout << "tp2: " << duration_cast<seconds>(tp2.time_since_epoch()).count() << endl;
return 0;
}
For me, the denominator value is 1,000,000 for the system_clock's time_point. Thus max seconds is going to overflow it when converted up.

Proper method of using std::chrono

While I realize this is probably one of many identical questions, I can't seem to figure out how to properly use std::chrono. This is the solution I cobbled together.
#include <stdlib.h>
#include <iostream>
#include <chrono>
typedef std::chrono::high_resolution_clock Time;
typedef std::chrono::milliseconds ms;
float startTime;
float getCurrentTime();
int main () {
startTime = getCurrentTime();
std::cout << "Start Time: " << startTime << "\n";
while(true) {
std::cout << getCurrentTime() - startTime << "\n";
}
return EXIT_SUCCESS;
}
float getCurrentTime() {
auto now = Time::now();
return std::chrono::duration_cast<ms>(now.time_since_epoch()).count() / 1000;
}
For some reason, this only ever returns integer values as the difference, which increments upwards at rate of 1 per second, but starting from an arbitrary, often negative, value.
What am I doing wrong? Is there a better way of doing this?
Don't escape the chrono type system until you absolutely have to. That means don't use .count() except for I/O or interacting with legacy API.
This translates to: Don't use float as time_point.
Don't bother with high_resolution_clock. This is always a typedef to either system_clock or steady_clock. For more portable code, choose one of the latter.
.
#include <iostream>
#include <chrono>
using Time = std::chrono::steady_clock;
using ms = std::chrono::milliseconds;
To start, you're going to need a duration with a representation of float and the units of seconds. This is how you do that:
using float_sec = std::chrono::duration<float>;
Next you need a time_point which uses Time as the clock, and float_sec as its duration:
using float_time_point = std::chrono::time_point<Time, float_sec>;
Now your getCurrentTime() can just return Time::now(). No fuss, no muss:
float_time_point
getCurrentTime() {
return Time::now();
}
Your main, because it has to do the I/O, is responsible for unpacking the chrono types into scalars so that it can print them:
int main () {
auto startTime = getCurrentTime();
std::cout << "Start Time: " << startTime.time_since_epoch().count() << "\n";
while(true) {
std::cout << (getCurrentTime() - startTime).count() << "\n";
}
}
This program does a similar thing. Hopefully it shows some of the capabilities (and methodology) of std::chrono:
#include <iostream>
#include <chrono>
#include <thread>
int main()
{
using namespace std::literals;
namespace chrono = std::chrono;
using clock_type = chrono::high_resolution_clock;
auto start = clock_type::now();
for(;;) {
auto first = clock_type::now();
// note use of literal - this is c++14
std::this_thread::sleep_for(500ms);
// c++11 would be this:
// std::this_thread::sleep_for(chrono::milliseconds(500));
auto last = clock_type::now();
auto interval = last - first;
auto total = last - start;
// integer cast
std::cout << "we just slept for " << chrono::duration_cast<chrono::milliseconds>(interval).count() << "ms\n";
// another integer cast
std::cout << "also known as " << chrono::duration_cast<chrono::nanoseconds>(interval).count() << "ns\n";
// floating point cast
using seconds_fp = chrono::duration<double, chrono::seconds::period>;
std::cout << "which is " << chrono::duration_cast<seconds_fp>(interval).count() << " seconds\n";
std::cout << " total time wasted: " << chrono::duration_cast<chrono::milliseconds>(total).count() << "ms\n";
std::cout << " in seconds: " << chrono::duration_cast<seconds_fp>(total).count() << "s\n";
std::cout << std::endl;
}
return 0;
}
example output:
we just slept for 503ms
also known as 503144616ns
which is 0.503145 seconds
total time wasted: 503ms
in seconds: 0.503145s
we just slept for 500ms
also known as 500799185ns
which is 0.500799 seconds
total time wasted: 1004ms
in seconds: 1.00405s
we just slept for 505ms
also known as 505114589ns
which is 0.505115 seconds
total time wasted: 1509ms
in seconds: 1.50923s
we just slept for 502ms
also known as 502478275ns
which is 0.502478 seconds
total time wasted: 2011ms
in seconds: 2.01183s

Get time difference with DST considered

I am using Boost.Date_time to get the time difference between two dates. I want the code to consider DST change as well during these days and give me the correct interval.
Consider this example. On 1-Nov-2015, the DST is going to change in USA. At 2:00 hours, the clock will be moved back to 1:00. The output of the below code doesn't reflect that. It gives 23 hours as the difference.
date d1(2015, 11, 1);
ptime nov1_00(d1, hours(0));
ptime nov1_23(d1, hours(23));
seconds = (nov1_23 - nov1_00).total_seconds();
Output:
2015-Nov-01 00:00:00. 2015-Nov-01 23:00:00. Seconds: 82800
Is there a way in boost to specify the DST requirement in this scenario?
You should be using local times:
Live On Coliru
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/date_time/local_time/local_date_time.hpp>
#include <boost/date_time/local_time/local_time_io.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
int main() {
namespace lt = boost::local_time;
namespace pt = boost::posix_time;
using date = boost::gregorian::date;
lt::tz_database db;
db.load_from_file("/home/sehe/custom/boost/libs/date_time/data/date_time_zonespec.csv");
//for (auto region : db.region_list()) std::cout << region << "\n";
auto NY = db.time_zone_from_region("America/New_York");
date const d1(2015, 11, 1);
lt::local_date_time nov1_00(d1, pt::hours(0), NY, true);
lt::local_date_time nov1_23(d1, pt::hours(23), NY, false);
lt::local_time_period period(nov1_00, nov1_23);
std::cout << "period: " << period << "\n";
std::cout << "duration: " << period.length() << "\n";
// if you insist:
auto seconds = (nov1_23 - nov1_00).total_seconds();
std::cout << "seconds: " << seconds << "\n";
}
Prints:
period: [2015-Nov-01 00:00:00 EDT/2015-Nov-01 22:59:59.999999 EST]
duration: 24:00:00
seconds: 86400