I would create a calendar table with every date you will ever need. Add fields for the attributes you will need when reporting/querying. Here is an article on how/why using SQL server. I typically use Excel. There are many other resources for creating and using the table.
Workdays excluding week ends and public holidays.
Good day, I have set up a date table and a holiday table, The purpose of this is to produce a report to show many working days are available each month.
I have a function. which runs in three phases.
Phase 1, to see how many working days are available each month, this phase is working fine.
Phase 2, to subtract fixed public holidays from the results of phase 1, this phase is also running fine.
The issue is with phase 3, to subtract non fixed public holiday from a holiday table, The issue I am experiencing is that as well as subtracting from that month, it also subtracts for the months below that month in my table.
Public Function MyWorkdays(StartDate As Date, EndDate As Date) As Long
Dim D As Date, OKToAdd As Boolean, H As Long
MyWorkdays = 0
D = StartDate
While D <= EndDate
OKToAdd = False
' Monday to Friday.Fixed date holidays (Christmas day)
If Weekday(D) > 1 And Weekday(D) < 7 Then OKToAdd = True
If Month(D) = 1 And Day(D) = 1 Then OKToAdd = False 'New Years Day
If Month(D) = 3 And Day(D) = 21 Then OKToAdd = False 'Human Rights Day
If OKToAdd Then MyWorkdays = MyWorkdays + 1
D = D + 1
Wend
' Holidays in Table. Non fixed holidays (Easter)
H = Nz(DCount("\*", "HolidayTBL", "HolidayDate Between #" & StartDate & \_
"# AND #" & EndDate & "#"), 0)
MyWorkdays = MyWorkdays - H
End Function
I then try the function with just the two phases. Phase 1, to see how many working days are available each month, this phase is working fine. Phase 2, to subtract public holiday from a holiday table, this is still giving the same issue, that as well as subtracting from that month, it also subtracts for the months below that month in my table. It does not affect the months above.
Public Function WorkDays(ByRef StartDate As Date, _
ByRef EndDate As Date, \_
Optional ByRef strHolidayTBL As String = "HolidayTBL" \_
) As Integer
' Returns the number of workdays between StartDate
' and EndDate inclusive, Workdays excludes weekends and
' holidays. Optionally, press this function the name of table
' or query as the third argument. If you don't the default
' Is "Holidays".
On Error GoTo WorkDays\_Error
Dim nWeekdays As Integer
Dim nHolidays As Integer
Dim strWhere As String
'DateValue returns the date part only.
StartDate = DateValue(StartDate)
EndDate = DateValue(EndDate)
nWeekdays = Weekdays(StartDate, EndDate)
If nWeekdays = -1 Then
WorkDays = -1
GoTo Workdays\_Exit
End If
strWhere = "[HolidayDate] >= #" & StartDate \_
& "# AND [HolidayDate] <= #" & EndDate & "#"
' Count the number of holidays.
nHolidays = DCount(Expr:="[HolidayDate]", \_
Domain:=strHolidayTBL, \_
Criteria:=strWhere)
WorkDays = nWeekdays - nHolidays
Workdays_Exit:
Exit Function
WorkDays_Error:
WorkDays = -1
MsgBox "Error " & Err.Number & "; " & Err.Description, \_
vbCritical, "WorkDays"
Resume Workdays\_Exit
End Function
Public Function Weekdays(ByRef StartDate As Date, _
ByRef EndDate As Date \_
) As Integer
On Error GoTo Weekdays\_Error
Const ncNumberOfWeekendDays As Integer = 2
Dim varDays As Variant
Dim varWeekendDays As Variant
Dim dtmx As Date
If EndDate < StartDate Then
dtmx = StartDate
StartDate = EndDate
EndDate = dtmx
End If
varDays = DateDiff(Interval:="D", \_
date1:=StartDate, \_
date2:=EndDate) + 1
varWeekendDays = (DateDiff(Interval:="ww", \_
date1:=StartDate, \_
date2:=EndDate) \_
\* ncNumberOfWeekendDays) \_
+ IIf(DatePart(Interval:="w", \_
Date:=StartDate) = vbSunday, 1, 0) \_
+ IIf(DatePart(Interval:="w", \_
Date:=EndDate) = vbSaturday, 1, 0)
Weekdays = (varDays - varWeekendDays)
Weekdays_Exit:
Exit Function
Weekdays_Error:
Weekdays = -1
MsgBox "Error " & Err.Number & ": " & Err.Description, \_
vbCritical, "Weekdays"
Resume Weekdays\_Exit
End Function
I entered the below holiday, and all dates from the 24/09/2025 where subtract by one day. (column X)
| | | HolidayTBL | | HolidayID | HolidayDate | HolidayName | | --- | --- | --- | | 14 | 24/09/2025 | Hertage Day |
| | | | Query1 | | StartDate | EndDate | X | ActualWorkdays | | --- | --- | --- | --- | | 01/01/2025 | 31/01/2025 | 23 | 23 | | 01/02/2025 | 28/02/2025 | 20 | 20 | | 01/03/2025 | 31/03/2025 | 21 | 21 | | 01/04/2025 | 30/04/2025 | 22 | 22 | | 01/05/2025 | 31/05/2025 | 22 | 22 | | 01/06/2025 | 30/06/2025 | 21 | 21 | | 01/07/2025 | 31/07/2025 | 23 | 23 | | 01/08/2025 | 31/08/2025 | 21 | 21 | | 01/09/2025 | 30/09/2025 | 21 | 22 | | 01/10/2025 | 31/10/2025 | 22 | 23 | | 01/11/2025 | 30/11/2025 | 19 | 20 | | 01/12/2025 | 31/12/2025 | 22 | 23 |
Your assistance and advice will be greatly appreciated.
Many thanks
Microsoft 365 and Office | Access | For business | Other
Locked Question. This question was migrated from the Microsoft Support Community. You can vote on whether it's helpful, but you can't add comments or replies or follow the question.
15 answers
Sort by: Most helpful
-
-
Anonymous
2025-05-02T16:58:55+00:00 You might like to take a look at Workdays.zip in my Dropbox public databases folder at:
This little demo file illustrates a number of functions for returning working days, and includes examples for use in a single country, or in multiple countries, such as the constituent countries of the UK, and the Republic of Ireland, with differing public holiday regimes.
As Duane says, using auxiliary calendar tables would be the best way forward. See Calendar.zip in my same Dropbox folder for how to build calendar tables of various types.
As regards the removal of non-fixed holidays, if, in addition to a basic calendar table from which public holidays have been excluded, you have a table of non-fixed holidays, then it is a simple matter to create a query which returns a result table which also excludes the non-fixed holidays by using the NOT EXISTS predicate
Alternatively you might have a calendar table of all dates, one of weekend dates, one of public holiday dates, and one of non-fixed holiday dates. Again the working days could be returned by means of the NOT EXISTS predicate.
It would of course be possible to return the working days per individual by having a table of which models the many-to-many relationship type between non-fixed holidays and employees.
-
Anonymous
2025-05-03T03:34:19+00:00 Thanks, Duance
I will read the article.
-
Anonymous
2025-05-03T03:51:27+00:00 Thanks Ken.
i will have a look at your folder, I did set up two tables and a query.
Date Table
| | | DateTBL | | ID | StartDate | EndDate | | --- | --- | --- | | 1 | 01/01/2025 | 31/01/2025 | | 2 | 01/02/2025 | 28/02/2025 | | 3 | 01/03/2025 | 31/03/2025 | | 4 | 01/04/2025 | 30/04/2025 | | 5 | 01/05/2025 | 31/05/2025 | | 6 | 01/06/2025 | 30/06/2025 | | 7 | 01/07/2025 | 31/07/2025 | | 8 | 01/08/2025 | 31/08/2025 | | 9 | 01/09/2025 | 30/09/2025 | | 10 | 01/10/2025 | 31/10/2025 | | 11 | 01/11/2025 | 30/11/2025 | | 12 | 01/12/2025 | 31/12/2025 |
Holiday Table
| | | HolidayTBL | | HolidayID | HolidayDate | HolidayName | | --- | --- | --- | | 15 | 01/01/2025 | New Year's Day | | 16 | 21/03/2025 | Human Rights Day | | 17 | 18/04/2025 | Good Friday | | 18 | 27/05/2025 | Easter Monday | | 19 | 01/05/2025 | Workers Day | | 20 | 16/06/2025 | Youth Day | | 21 | 09/08/2025 | National Women's Day | | 22 | 24/09/2025 | Hertage Day | | 23 | 16/12/2025 | Day of Reconcilation | | 24 | 25/12/2025 | Christmas Day | | 25 | 26/12/2025 | Boxing Day |
Word Days Query
-
Anonymous
2025-05-03T07:47:45+00:00 Your approach is right, it just misses the final touch. You can study my workday functions in module **DateWork.bas** at my repository [VBA.Date](https://github.com/GustavBrock/VBA.Date "github.com"). This also holds a demo database with a query to return your requested results: **WorkdaysMonthsDateRange**. Enter 01-01-2025 and 01-01-2026 as the date range, and it returns (having only Hertage Day registered in the Holiday table): | | | | | | | | | **WorkdaysMonthsDateRange** | | Id | DateStart | DateEnd | Year | Month | DateFrom | DateTo | Days | Workdays | | --- | --- | --- | --- | --- | --- | --- | --- | --- | | 0 | 01-01-2025 | 01-01-2026 | 2025 | 1 | 01-01-2025 | 01-02-2025 | 31 | 23 | | 1 | 01-01-2025 | 01-01-2026 | 2025 | 2 | 01-02-2025 | 01-03-2025 | 28 | 20 | | 2 | 01-01-2025 | 01-01-2026 | 2025 | 3 | 01-03-2025 | 01-04-2025 | 31 | 21 | | 3 | 01-01-2025 | 01-01-2026 | 2025 | 4 | 01-04-2025 | 01-05-2025 | 30 | 22 | | 4 | 01-01-2025 | 01-01-2026 | 2025 | 5 | 01-05-2025 | 01-06-2025 | 31 | 22 | | 5 | 01-01-2025 | 01-01-2026 | 2025 | 6 | 01-06-2025 | 01-07-2025 | 30 | 21 | | 6 | 01-01-2025 | 01-01-2026 | 2025 | 7 | 01-07-2025 | 01-08-2025 | 31 | 23 | | 7 | 01-01-2025 | 01-01-2026 | 2025 | 8 | 01-08-2025 | 01-09-2025 | 31 | 21 | | 8 | 01-01-2025 | 01-01-2026 | 2025 | 9 | 01-09-2025 | 01-10-2025 | 30 | 21 | | 9 | 01-01-2025 | 01-01-2026 | 2025 | 10 | 01-10-2025 | 01-11-2025 | 31 | 23 | | 10 | 01-01-2025 | 01-01-2026 | 2025 | 11 | 01-11-2025 | 01-12-2025 | 30 | 20 | | 11 | 01-01-2025 | 01-01-2026 | 2025 | 12 | 01-12-2025 | 01-01-2026 | 31 | 23 |