Maxx
Maxx

Reputation: 602

Get the number of trading days in between two days

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

Answers (4)

user2164703
user2164703

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

Matt
Matt

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

Marcus W&#246;lk
Marcus W&#246;lk

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

small_duck
small_duck

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

Related Questions