Reputation: 602
i'm trying to get the number of trading dates between two dates which will only exclude the weekends and won't consider any holidays. I'm using Boost and c++11 standards.
using namespace boost::gregorian;
long dateDifference( string start_date, string end_date ) {
date _start_date(from_simple_string(start_date));
date _end_date(from_simple_string(end_date));
long difference = ( _start_date - _end_date ).days();
return difference;
}
This just returns the number of days between two dates without considering weekends. Can someone point me in the right direction. I can't seem to figure out the solution.
Thanks, Maxx
Upvotes: 5
Views: 2820
Reputation: 209
I didn't find any O(1) solutions which satisfied me so here is what I did:
int get_weekdays_count(const boost::gregorian::date& a,const boost::gregorian::date& b)
{
int na=(a<b) ? a.day_of_week().as_number() : b.day_of_week().as_number();
int diff=(a-b).days();
if(diff!=0){
if(diff<0) diff*=-1;
int rslt=diff/7; //number of saturdays
rslt*=2; // times 2 for sundays
rslt+= (diff%7) >=(boost::gregorian::Saturday-na)%7 ? 1 : 0; // handle special case for saturdays
rslt+= (diff%7) >=(boost::gregorian::Sunday-na)%7 ? 1 : 0; //special case for sundays
return 1+diff-rslt;
}
else return (na==boost::gregorian::Saturday || na==boost::gregorian::Sunday) ? 0 : 1;
};
This work even if a > b , just put two separate time and here you go.
Speed in a for loop: 25 nanosec/call on VS2012 Release mode // proc i5 4690K
Upvotes: 0
Reputation: 20776
O(1) solution with no loops:
#include <boost/date_time.hpp>
using namespace std;
using namespace boost::gregorian;
long countWeekDays( string d0str, string d1str ) {
date d0(from_simple_string(d0str));
date d1(from_simple_string(d1str));
long ndays = (d1-d0).days() + 1; // +1 for inclusive
long nwkends = 2*( (ndays+d0.day_of_week())/7 ); // 2*Saturdays
if( d0.day_of_week() == boost::date_time::Sunday ) ++nwkends;
if( d1.day_of_week() == boost::date_time::Saturday ) --nwkends;
return ndays - nwkends;
}
The basic idea is to first count all Saturdays, which is conveniently given by the formula (ndays+d0.day_of_week())/7
. Doubling this gives you all Saturdays and Sundays, except in the cases when the start and end dates may fall on a weekend, which is adjusted for by 2 simple tests.
To test it:
#include <iostream>
#include <cassert>
#include <string>
// January 2014
// Su Mo Tu We Th Fr Sa
// 1 2 3 4
// 5 6 7 8 9 10 11
// 12 13 14 15 16 17 18
// 19 20 21 22 23 24 25
// 26 27 28 29 30 31
int main()
{
assert(countWeekDays("2014-01-01","2014-01-01") == 1);
assert(countWeekDays("2014-01-01","2014-01-02") == 2);
assert(countWeekDays("2014-01-01","2014-01-03") == 3);
assert(countWeekDays("2014-01-01","2014-01-04") == 3);
assert(countWeekDays("2014-01-01","2014-01-05") == 3);
assert(countWeekDays("2014-01-01","2014-01-06") == 4);
assert(countWeekDays("2014-01-01","2014-01-10") == 8);
assert(countWeekDays("2014-01-01","2014-01-11") == 8);
assert(countWeekDays("2014-01-01","2014-01-12") == 8);
assert(countWeekDays("2014-01-01","2014-01-13") == 9);
assert(countWeekDays("2014-01-02","2014-01-13") == 8);
assert(countWeekDays("2014-01-03","2014-01-13") == 7);
assert(countWeekDays("2014-01-04","2014-01-13") == 6);
assert(countWeekDays("2014-01-05","2014-01-13") == 6);
assert(countWeekDays("2014-01-06","2014-01-13") == 6);
assert(countWeekDays("2014-01-07","2014-01-13") == 5);
cout << "All tests pass." << endl;
return 0;
}
This works for any date range in the Gregorian calendar, which boost currently supports for years 1400-10000. Note that different countries have adopted the Gregorian calendar at different times. For example the British switched from the Julian to the Gregorian calendar in September of 1752, so their calendar for that month looks like
September 1752
Su Mo Tu We Th Fr Sa
1 2 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Upvotes: 6
Reputation: 521
Just run a day iterator and calculate the weekdays manually:
#include <boost/date_time.hpp>
using namespace boost::gregorian;
long dateDifference( string start_date, string end_date )
{
date _start_date(from_simple_string(start_date));
date _end_date(from_simple_string(end_date));
// counter for weekdays
int cnt=0;
for(day_iterator iter = _start_date; iter!=_end_date; ++iter)
{
// increment counter if it's no saturday and no sunday
if( iter->day_of_week() != boost::date_time::Saturday
&& iter->day_of_week() != boost::date_time::Sunday)
++cnt;
}
return cnt;
}
Answer ported from this answer: https://stackoverflow.com/a/7342989/3187827
Upvotes: 5
Reputation: 3094
The simplest approach is to use the boost::gregorian::day_of_week() function and iterate through every day between your start date and your end date, incrementing only when it's not a Saturday nor a Sunday.
A more efficient approach would be to iterate from start_date to the next Monday (say), iterate from end_date to the previous Monday, then with a simple division find out how many week-ends you've got in between.
Finally, a "real world" solution would involve finding proper calendar data with the holidays that apply to your case, and integrate it with your algorithm.
Upvotes: 2