[Help]Calculating the number of work days..

I've been trying to write a program(in c++) for the past couple of hours that is supposed to calculate the number of work days between two, user created, dates.

The code calculates the number of days(with weekends) between dates perfectly, each time. But the problem occurs when calculating the work(business)days.
On some dates it runs fine, on others it outputs one day extra/less (ie. 16/10/'16 - 14/12/'16).

I'll paste the code here, and I'm already looking forward to seeing how would you guys solve this !

Final notes : This code is not designed to calculate dates from one year to another(yet), it can only work(somewhat) within 12 months of the same year.
The formula I used to calculate work days from total number of days is :
totalDays - (totalDays / 7) * 2 which is by no means perfect, because a hack must be implemented to deal with rounding up the number gotten by doing totalDays/7 division.
Code is written in Visual Studio 15, my platform of choice. I've used this online calculator to check my output : https://www.timeanddate.com/date/workdays.html
Source code below.
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
  #include<iostream>
#include<iomanip>
#include<cmath>

using namespace std;

struct Date{
	int d,m,y;
	void Input(){
		cout<<"Input Date in format : D/M/Y "<<endl;
		cin>>d>>m>>y;
	}
	void Output(){
		cout<<d<<"."<<m<<"."<<y<<". year."<<endl;
	}//this function will be implemented later on
};
int calcWorkDays(Date ,Date);

void main()
{
Date Date_start,Date_end;
float budget;

cout<<"Insert how much money you have in your budget : ";
cin>>budget;
cout<<"Insert Dates (FROM-TO) to calculate your maximum spending money per workday : "<<endl;
Date_start.Input();	Date_end.Input();
cout<<"Workdays : "<<calcWorkDays(Date_start,Date_end)<<"\t budget : "<<budget<<endl;
//cout<<"You can spend(on workdays) on average : "<<budget/calcWorkDays(Date_start,Date_end)<<" $."<<endl;
system("PAUSE>NULL");
}

int calcWorkDays(Date From, Date To){
	int days = 0; //total number of days, including weekends 
	int counter = 0; //checks if its the first month that is being counted 
	int month =From.m;
	while(month <=To.m){
		if(month < To.m){
			switch(month){
			case 2 :
					if(From.y % 4 == 0 && From.y % 100 && From.y %400)
					{
						if(counter == 0)
							days+=29- From.d;	
						else
							days+=29;
					}
					else{
						if(counter == 0)
							days+=28 -From.d;
						else
							days+=28;
					}
					break;
			case 1 :
			case 3 : 
			case 5 : 
			case 7 :
			case 8 :
			case 10 :
			case 12 : 
					if(counter == 0)
						days+=31 - From.d;
					else
						days+=31;
					break;
			  
			case 4 : 
			case 6 : 
			case 9 : 
			case 11 : 
					if(counter == 0)
						days+=30-From.d;
					else
						days+=30;
					break;
	} //END OF SWITCH STATEMENT
		}//End of condition that checks if the last month is being counted,
// if it is, it doesnt subtract anything, it just adds the days of 
//that month to the total sum of days.
		else{
			days+=To.d;
		}
		month++;
		counter++;
	};
	days+=1; //Include the last day in the count
	cout<<"DAYS(inc. weekends) : "<<days<<endl;

	double x = (double)days/7;
	int IntPartX = (int)x;
	float decPart = x - IntPartX;
	

	if(decPart>=0.5 || decPart *2 >=0.5)  // this if-else statement is supposed to serve a purpose
// of rounding up numbers (threshold at 0.5) to the closest larger integer
	{
		
		days-=IntPartX * 2;
		if(decPart *2 >=0.5)
			days-=2;
		else
			days-=1;
		return days;
	}
	else
		return days - IntPartX *2;


}
Last edited on
Hello noxBox,

I commented out line 85 and the program appears to work with out any problems. Seems adding that extra day made it one to many.

Hope that helps,

Andy
Hello noxBox,

After further testing I found that short time e.g., days 1 -15 produced the wrong number of work days. It was coming out one more than it should be. So, I uncommented line 85 and moved to after the cout statement then it showed the proper amount of work(business) days.

Andy
Hey Andy,

First of all thank you for taking a couple of minutes to try and solve this problem.
I have tried what you suggested, and It still doesn't work for the dates like 16th October to 14th December.
The program apparently has troubles counting the last day (14th December) as a work day, and instead of 43 workdays it outputs 42 workdays.

Hello noxBox,

I did not try a span that far,but I will look into it.

Andy
Hello noxBox,

Sorry for the delay. I have had problems with my wifi connection.

When I started dealing with case 2 I noticed the if condition to figure leap year is incorrect. It is better written:
if ((From.y % 4 == 0 && !From.y % 100 == 0) || (From.y % 400 == 0))
notice the extra ().

In the if else statements of the case parts the lines 44, 50, 63 and 73 I changed from:
days += numOfDaysPerMonth - From.d; to days += (numOfDaysPerMonth - From.d) + 1; to get the correct number for total days.

When I found problems with the program not calculating the proper number of work days I starting looking at the code from line 90 on as being the problem. The code you refer to as rounding is par f the problem, but not all the problem. After thinking about it for awhile I do not believe fixing the code is the answer, but finding a better way to figure the weekend dates to subtract from the total number of days.

Then I realized that you are working with numbers that look like dates not dates. The program has no idea what day of the week the start or end day is which makes it hard to figure the correct number of weekend days. The rounding code comes close, but it is not accurate all the time. There is no way to fix the code to account for every possibility.

I have not given up yet, but do not know which way to go yet.

Hope that helps,

Andy
Andy,

Thank you for your help, It's much appreciated. After thoroughly thinking about this problem I came to the same conclusion as you : the program cannot be done this way, because It can never figure out the correct number of weekend days, exactly for the reason you stated.

Not every possibility can be covered. So for now I decided to put a disclaimer at the beginning of execution that user has to input dates that start and end on workdays only.
I've cleaned the code up a bit (implemented a for instead of a while loop, cleaned up some extra un-needed variables etc..), and to my knowledge it seems to correctly output the number of workdays for dates starting and ending on workdays, respectively.

I plan to implement a function that can check the validity of the entered dates, and some sort of a menu as soon as I have time. But anyway, here's the new code, see if you can enter some dates(start and end on workdays) that can break it, I've found none so far.

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include<iostream>
#include<iomanip>
#include<cmath>
//IMPLEMENT MENU !!
using namespace std;

struct Date{
	int d,m,y;
	void Input(){
		cout<<"Input Date in format : D/M/Y "<<endl;
		cin>>d>>m>>y;
	}
	void Output(){
		cout<<d<<"."<<m<<"."<<y<<". year."<<endl;
	}//this function will be implemented later on
};
int calcWorkDays(Date ,Date, int&);

void main()
{
Date Date_start,Date_end;
float budget; int days = 0;

cout<<"Insert how much money you have in your budget : ";
cin>>budget;
cout<<"Insert Dates (FROM-TO) to calculate your maximum spending money per workday : "<<endl;
Date_start.Input();	Date_end.Input();
cout<<"Workdays : "<<calcWorkDays(Date_start,Date_end,days)<<"\t budget : "<<budget<<endl;
cout<<"Total number of days(inc. weekends) : "<<days<<endl;
cout<<"You can spend (at most) : $"<<setprecision(3)<<budget/calcWorkDays(Date_start,Date_end,days)<<endl;
system("PAUSE>NULL");
}

int calcWorkDays(Date From, Date To, int &days){ 


for (int i =From.m;i<=To.m;i++){
	if(i < To.m){
		switch(i){
		case 2 :
				if((From.y % 4 == 0 && !From.y % 100 == 0) || (From.y % 400 == 0))
				{
					if(i == From.m)
						days+=30- From.d;	
					else
						days+=29;
				}
				else{
					if(i == From.m)
						days+=29 -From.d;
					else
						days+=28;
				}
				break;
		case 1 :
		case 3 : 
		case 5 : 
		case 7 :
		case 8 :
		case 10 :
		case 12 : 
				if(i == From.m)
					days+=32 - From.d;
				else
					days+=31;
				break;
		  
		case 4 : 
		case 6 : 
		case 9 : 
		case 11 : 
				if(i == From.m)
					days+=31-From.d;
				else
					days+=30;
				break;
} //END OF SWITCH STATEMENT
	}//End of condition that checks if the last month is being counted,
	// if it is, it doesnt subtract anything, it just adds
	// the days of that month to the total sum of days.
	else if(From.m ==To.m)
	{
		days+=To.d-From.d+1;
	}
	else {
		days+=To.d;
	}
	}; //end of for loop
	
 
	double numberWeeks = (double)days/7;
	int IntPart = (int)numberWeeks;

	return days-IntPart*2;
}



Last edited on
Hello noxBox,

The first date range I entered broke it. Start date 1/15 which is Friday and end date 3/14 which is Monday the program said there are 44 work days when it should have been 42 work days. The second try of Fri 1/15 to Mon 2/15 the program returned two extra work days.

Other tries like: Mon 1/11 to Wed 3/16 and Wed 2/10 to Wed 4/20 returned the correct numbers. Not sure if there is a pattern there or not, but the two tries that started on Friday returned the wrong work days while start dates eariler in the week are correct.

I noticed two problems with the cout on line 30. First when I use "setprecision" I like to precede it with "fixed". The other choice is "scientific". With "fixed" the "setprecision" will deal with what is to the right of the decimal point. What you have used acts on the entire output cutting the number short.

Second before the second call to "calcWorkDays" you need to zero "days" before you use it again or you will get the wrong amount for budget per day.

Hope that helps,

Andy
Hello Andy,

After almost throwing the towel in, I figured I'll give it one more shot, this time coming from more of a mathematical standpoint. I've debugged the code several times and concluded that when the "decpart" variable is around 0.5 (or 0.5714 specifically), both "days" and "Intpart" variables should be composite numbers for the code to work using this return :

 
return days-2-IntPart*2;


Else, function should return days-IntPart*2.

So I implemented a function that checks if a number is prime, and ran the code with several dates, including those you've suggested (thank you) and several of mine that I specifically picked for testing purposes. The code worked excellent, and so far I didn't run into any problems. I'm pasting the whole code below so see if you can break it again. Thanks !

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include<iostream>
#include<iomanip>
#include<cmath>
//IMPLEMENT MENU !!
using namespace std;

struct Date{
	int d,m,y;
	void Input(){
		cout<<"Input Date in format : D/M/Y "<<endl;
		cin>>d>>m>>y;
	}
	void Output(){
		cout<<d<<"."<<m<<"."<<y<<". year."<<endl;
	}//this function will be implemented later on
};
int calcWorkDays(Date ,Date, int&);

bool isPrime(int number){

    if(number < 2) return false;
    if(number == 2) return true;
    if(number % 2 == 0) return false;
    for(int i=3; (i*i)<=number; i+=2){
        if(number % i == 0 ) return false;
    }
    return true;

}//checks if the number is prime

void main()
{
Date Date_start,Date_end;
float budget; int days = 0;

cout<<"Insert how much money you have in your budget : ";
cin>>budget;
cout<<"Insert Dates (FROM-TO) to calculate your maximum spending money per workday : "<<endl;
Date_start.Input();	Date_end.Input();
cout<<"Workdays : "<<calcWorkDays(Date_start,Date_end,days)<<"\t budget : "<<budget<<endl;

cout<<"Total number of days(inc. weekends) : "<<days<<endl;
days = 0; //zeroing days
cout<<fixed; //preceding the setprecision
cout<<"You can spend (at most) : $"<<setprecision(2)<<budget/calcWorkDays(Date_start,Date_end,days)<<endl;
system("PAUSE>NULL");
}

int calcWorkDays(Date From, Date To, int &days){ 


for (int i =From.m;i<=To.m;i++){
	if(i < To.m){
		switch(i){
		case 2 :
				if((From.y % 4 == 0 && !From.y % 100 == 0) || (From.y % 400 == 0))
				{
					if(i == From.m)
						days+=30- From.d;	
					else
						days+=29;
				}
				else{
					if(i == From.m)
						days+=29 -From.d;
					else
						days+=28;
				}
				break;
		case 1 :
		case 3 : 
		case 5 : 
		case 7 :
		case 8 :
		case 10 :
		case 12 : 
				if(i == From.m)
					days+=32 - From.d;
				else
					days+=31;
				break;
		  
		case 4 : 
		case 6 : 
		case 9 : 
		case 11 : 
				if(i == From.m)
					days+=31-From.d;
				else
					days+=30;
				break;
} //END OF SWITCH STATEMENT
	}//End of condition that checks if the last month is being counted,
	// if it is, it doesnt subtract anything, it just adds
	// the days of that month to the total sum of days.
	else if(From.m ==To.m)
	{
		days+=To.d-From.d+1;
	}
	else {
		days+=To.d;
	}
	}; //end of for loop
	
 
	double numberWeeks = (double)days/7;
	int IntPart = (int)numberWeeks;
	float decPart = numberWeeks - IntPart;
	
	if(decPart >=0.5 && !isPrime(days) && !isPrime(IntPart))
		return days-2-IntPart*2;
	else
	return days-IntPart*2;
}

Hello noxBox,

Good Job looks like it works.

Everything I have tried so far has worked correctly. Even a range from 01/01 to 09/30.

I like your solution at the end of calcWorkDays. I was focused on other ways and never thought of anything like that.

Good work,

Andy
Thanks, you've been very helpful. I'm marking the thread as "solved" now.
Topic archived. No new replies allowed.