Workdays excluding week ends and public holidays.

Anonymous
2025-05-02T14:05:53+00:00

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.

0 comments No comments
{count} votes

15 answers

Sort by: Most helpful
  1. Duane Hookom 26,555 Reputation points Volunteer Moderator
    2025-05-02T14:17:05+00:00

    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.

    0 comments No comments
  2. 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:

    https://www.dropbox.com/scl/fo/0scigd3r48hx5xrev2jrf/AB0-GMdTgMAO5O1cGdr3QW0?rlkey=ib6bs6g9jqcrywwzivur3265t&dl=0

    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.

    0 comments No comments
  3. Anonymous
    2025-05-03T03:34:19+00:00

    Thanks, Duance

    I will read the article.

    0 comments No comments
  4. 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

    0 comments No comments
  5. 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 |
    0 comments No comments