I was looking for a function that calculates working days, minus holidays, for a small project I've been working on. I decided to write one myself, but below I've also demonstrated an alternate way that uses the ATP (Analysis ToolPak) functions for VBA.
Function WorkDays(StartDate As Variant, EndDate As Variant) As Long
' adapted from:
' http://www.exceltip.com/st/Calculate_the_count_of_workdays_between_two_dates_using_VBA_in_Microsoft_Excel/523.html
' and http://support.microsoft.com/kb/97757
'
' returns the count of days between StartDate - EndDate minus Saturdays and Sundays
' also checks for holidays (added by Jimmy Pena 6/17/2008)
Dim lCounter As Long
Dim DaysCount As Long
Dim HolidaysList()
Dim ArrMember
Dim bWasFound As Boolean
bWasFound = False
StartDate = DateValue(StartDate)
EndDate = DateValue(EndDate)
' define holiday list as an array which we will check against
' each day; if match is found, do not increase workday count
'
' edit/add/delete as needed
' these are US Federal Holidays
HolidaysList = Array("1/1/2007", "1/15/2007", "2/19/2007", "5/28/2007", "7/4/2007", "9/3/2007", "10/8/2007", "11/12/2007", _
"11/22/2007", "12/25/2007", "1/1/2008", "1/21/2008", "2/18/2008", "5/26/2008", "7/4/2008", "9/1/2008", "10/13/2008", _
"11/11/2008", "11/27/2008", "12/25/2008")
For lCounter = StartDate To EndDate
If Weekday(lCounter, vbMonday) < 6 Then
For Each ArrMember In HolidaysList
If Format(ArrMember, "mm/dd/yyyy") = Format(lCounter, "mm/dd/yyyy") Then
bWasFound = True
Exit For
Else
bWasFound = False
End If
Next ArrMember
If bWasFound = False Then
DaysCount = DaysCount + 1
End If
End If
Next lCounter
WorkDays = DaysCount
End FunctionHere we create an array of holidays, then loop through the dates passed as arguments to the function to see if they are not weekend days. The inner loop checks to see if the date matches one of the holidays in the array variable. If both conditions are met (i.e. a weekday and not a holiday) we add to the count (DaysCount variable).
The Boolean variable is used as a flag inside the loop — if the date matches a holiday date, the flag is raised and the loop immediately exits. We check the flag after the loop is complete and add to the count if it was left False.
To use:
MsgBox WorkDays("1/1/2008", "1/31/2008")
Or since the function returns a Long value, assign the result to a variable
Dim lCountOfDays As Long
lCountOfDays = WorkDays("1/1/2008", "1/31/2008")
Here's another way, using the ATP library for VBA. To set a reference, open the VBE and go to Tools > References and set a reference to "atpvbaen.xls".

You may also need to go back to Excel and go to Tools > Add-Ins and select "Analysis ToolPak – VBA".

Now you can use the NETWORKDAYS function directly in VBA like this:
Dim HolidaysList()
HolidaysList = Array("1/1/2007", "1/15/2007", "2/19/2007", "5/28/2007", "7/4/2007", "9/3/2007", "10/8/2007", "11/12/2007", _
"11/22/2007", "12/25/2007", "1/1/2008", "1/21/2008", "2/18/2008", "5/26/2008", "7/4/2008", "9/1/2008", "10/13/2008", _
"11/11/2008", "11/27/2008", "12/25/2008")
MsgBox networkdays("1/1/2008", "1/31/2008", HolidaysList)
Which should give you the same answer, and in my tests, at approximately the same speed.
Personally I like my way better, but that's only because I worked on it and I hate throwing away old code. Plus it uses built-in functions in case you don't want to add additional object libraries to your VBE. As you can see above, I already have a lot, so anything already built-in to VBA I can use is something I will prefer.
Enjoy,
JP





Hi – down here in the southern hemisphere we run with different date formats ie day/month/year and I can not get this macro to calculate correctly.
I am trying to calculate the number of hours between when a quote was requested and when it was submitted – using 24 hours per day x total days – weekends – holidays.
I have been playing with both Workdays function and TimeDiff function VBA code but to no avail as yet. I have tried holiday dates in different formats in Sub.
eg Input data:
Date/time quote requested 5/02/2008 8:00 (i.e. in one field with format d/mm/yyyy h:mm)
Date/Time quote submitted 30/04/2008 17:00
NZ HolidaysList = Array("1/01/2007", "6/02/2007", "6/04/2007", "9/04/2007", "25/04/2007", "4/06/2007", "22/10/2007", "25/12/2007", "26/12/2007")
= 85.38 days – 24 days weekends – 4days holidays = 57.38 days, x24 = 1377 hours
Networkdays = 58 days, x 24 = 1392hours
With
Const ComeIn As Date = "0:00"
Const Leave As Date = "23:59"
TimeDiff = 1448 hours which has added more hours not subtracted for holidays or failed to find any holidays (which is what I thought it would do if could not match holidays with dates)?
I have also played with modified code from "Visual Basic Language Developer's Handbook" by Ken Getz and Mike Gilbert and once again can not get calculating correctly. I do not want to use american date formats for my quote data as it has NZ users.
Please can you help.
How do I modify it for a 6 day workweek? Sundays we are closed. Nice stuff. Thanks
I assume you mean Monday through Saturday? The Weekday function currently excludes Saturday and Sunday. Just make it exclude Sunday only.
Change the 6 to a 7 in this line:
If Weekday(lCounter, vbMonday) < 7 Then
I'll check it out and let you know. It would probably help if you send me the full code you are using. Just use the contact form and paste in the code:
http://www.jpsoftwaretech.com/Contact.html
–JP
I just noticed that your work date range is in 2008, but the holidays list is in 2007. If that is the case, the NETWORKDAYS function won't subtract the holidays from the total workdays.
If you can fix that, it should work. Let me know!
–JP
Curious… can anyone think of a method to go the opposite way? I have the code above in my macro (which works GREAT! Thank you!), but we often have contract modifications come in that don't provide formal end dates and instead simply give a set amount of hours. I'd like to be able to enter in the start date and amount of hours allotted and have it tell me when the contract modification would end (business days only, 8 hours a day, no holidays).
As an very simple example: Say I have a contract that starts on 1/12/09 and is for 80 hours. I'd like to put "1/12/09" in A1, 80 in A2, and have A3 calculate "1/26/09" (factors in weekends and MLK holiday).
Is that even possible?
I see what you mean. Currently I only have code samples that calculate the number of working hours or days between two dates, but not one that returns the date X hours after a given start date. Let me see what I can come up with, but in the meantime you might want to google "Excel calculate working hours" and browse some of the links on the first few pages.
I did the same googling actually, hoping I could hack it out myself before posting for advice. That said, I don't play in the Microsoft world much at all and wasn't sure how flexible Excel was with situations like that. I'm more on the unix/linux side… (still googling though)
Dave,
I finally created the code you're looking for, look for a new blog entry shortly.
Hi M White
I am working on a project in which i have used ur above macro. I am using above macro to track containers which are coming from NA to Eur I need to count no. of working days excuding the date of arrival for eg. if material comes at port on 13/02/2009(friday) and leave port on 19/02/2009(thursday) then no of working days should be 4 not 5(1st problem) another thing If material has reached the port and not gone out of port then =workdays(today()-startdate)(2nd problem).
can you please help me in editing that macro…
1. No. of working days for the long duration i.e. more than two weeks are not coming correct.
2. If end date is not there then it should automaticaly take Todays date as a end date.
Reagrds
Shikher,
What you can do is write a function to check if a particular day is a business day (i.e. not a weekend or holiday). I've written most of the code already, see this post:
http://www.jpsoftwaretech.com/blog/2009/02/calculate-working-days-from-start-date/
What you can do is call the original function to return the working day, subtract one from the result, and call the function you just wrote to see if that day is a working day.
For the first problem you listed (No. of working days for the long duration i.e. more than two weeks are not coming correct.) can you provide an example?
For the second problem you listed (If end date is not there then it should automaticaly take Todays date as a end date.) you have to make the second parameter optional, and then at the start of the macro, check if the end user passed in a value for that parameter. If not, then make it equal to todays date.
Just wanted to say thanks for the code to do this without the Analysis ToolPak. It's saved me a lot of work.