Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

Holidays and business date calculations and translations

4.86/5 (9 votes)
28 May 2013CPOL3 min read 55.1K   943  
Add and subtract business days between dates. Calculate if date is a holiday.

Introduction  

The project is intended to help a reporting system determine valid dates to run reports. It has functions for determining if a date is a holiday, as well as functions to allow the reporting system to skip over weekends when you have requirements like I want this data by yesterday, today, or tomorrow. 

Background 

I started by reviewing a 2003 ASP article “Working with Business Dates Business Holidays Week”, I translated that and more into VB.NET. Of course everyone might celebrate different holidays, so you’ll have to change the code to fit your purpose. I added features I thought were useful, or were needed in the ASPX Live Reporting system that I’m working on.

There are two basic types of holidays: some are set to a particular day, like Christmas which are floating holidays. If one of these falls on a Saturday, then Friday is considered the holiday, Sunday turns into Monday, etc. Fixed holidays are on a specific day of the week. I didn’t cover Easter as it’s always on Sunday and already not a business day.

Since this is used for a reporting engine, and my clients work weekends, but don’t want them considered for the reports. The module redefines what the concept of Today is. Today is Monday on Saturday and Sunday. Thus Tomorrow is Tuesday on Saturday and Sunday. Conversely Yesterday is Friday on Sunday and Monday. If it’s Saturday and a Holiday falls on Monday then Tomorrow is Wednesday and Today is Tuesday. You have to be the ball to follow that one.

Using the code

Since this is all based on holidays, the isHoliday function is a good starting point.

IsHoliday

Returns Boolean True if holiday is found. There are global properties to turn off holidays with IsHolidayOK style names (IsNewYearsOK for example). They are all defaulted to true and programmatically changeable.

VB.NET
''' <summary>
''' Returns True if Date passed is a Holiday
''' </summary>
''' <param name="_Date">Date to Check</param>
''' <returns>Boolean True if Holiday</returns>
Public Function isHoliday(_Date As Date) As Boolean
    Dim ret As Boolean = False
    Dim iDay As Integer
    Dim iTmpDay As Integer
    Dim i As Integer
    iDay = Day(_Date)
    Select Case _Date.Month
        Case 1 ' Jan
            If IsNewYearsOK Then
                If iDay = 1 Then ' New Years
                    If isNotWeekend(_Date) Then
                        ret = True
                    End If
                ElseIf iDay = 2 Then 'Make Sure New Years isn't on a Sunday
                    If _Date.AddDays(-1).DayOfWeek = DayOfWeek.Sunday Then
                        ret = True
                    End If
                End If
            End If
            If IsMartinLutherKingOK Then
                'Is it Martin Luther King B-Day 3rd Monday of January
                If _Date.DayOfWeek = DayOfWeek.Monday Then
                    ' it might be
                    If _Date.Day > 14 AndAlso _Date.Day < 22 Then
                        ret = True
                    End If
                End If
            End If

        Case 2
            If IsPresidentsDayOK Then
                If _Date.DayOfWeek = DayOfWeek.Monday Then
                    'Might be Presidents Day 3rd Moday 
                    If _Date.Day > 14 AndAlso _Date.Day < 22 Then
                        ret = True
                    End If
                End If
            End If

        Case 3 ' March
        Case 4 'April (Easter is on Sunday)
        Case 5
            If IsMemorialDayOK Then
                If _Date.DayOfWeek = DayOfWeek.Monday Then
                    'Memorial Day Last Monday
                    If _Date.Day > 24 And _Date.Day < 32 Then
                        ret = True
                    End If

                End If
            End If

        Case 6 ' June
        Case 7 'July
            If Is4thOfJulyOk Then
                Select Case _Date.Day
                    Case 3
                        If _Date.DayOfWeek = DayOfWeek.Friday Then
                            ret = True
                        End If
                    Case 4
                        If isNotWeekend(_Date) Then
                            ret = True
                        End If
                    Case 5
                        If _Date.DayOfWeek = DayOfWeek.Monday Then
                            ret = True
                        End If
                    Case Else

                End Select
            End If

        Case 8 'August
        Case 9 'September
            If IsLaborDayOK Then
                If _Date.DayOfWeek = DayOfWeek.Monday Then
                    'Labor Day
                    If _Date.Day < 7 Then
                        Return True
                    End If
                End If
            End If

        Case 10 'October
            If IsColumbusDayOK Then
                If _Date.DayOfWeek = DayOfWeek.Monday Then
                    'Columbus Day, Second Monday of October
                    If _Date.Day > 7 AndAlso _Date.Day < 15 Then
                        ret = True
                    End If
                End If
            End If

        Case 11 'November
            If IsVeteransDayOK Then
                If _Date.Day >= 10 AndAlso _Date.Day <= 12 Then
                    Select Case _Date.Day
                        Case 10
                            If _Date.DayOfWeek = DayOfWeek.Friday Then
                                ret = True
                            End If
                        Case 11
                            If isNotWeekend(_Date) Then
                                ret = True
                            End If

                        Case 12
                            If _Date.DayOfWeek = DayOfWeek.Monday Then
                                ret = True
                            End If
                        Case Else

                    End Select
                End If
            End If
            If IsThanksgivingOK Then
                If _Date.Day > 20 AndAlso _Date.Day < 28 Then
                    'Thanksgiving is 4th Thursday
                    If _Date.DayOfWeek = DayOfWeek.Thursday Then
                        ret = True
                    End If
                End If
            End If

        Case 12 'December
            If IsChristmasOK Then
                If _Date.Day >= 24 AndAlso _Date.Day <= 26 Then
                    Select Case _Date.Day
                        Case 24
                            If _Date.DayOfWeek = DayOfWeek.Friday Then
                                ret = True
                            End If
                        Case 25
                            If isNotWeekend(_Date) Then
                                ret = True
                            End If

                        Case 26
                            If _Date.DayOfWeek = DayOfWeek.Monday Then
                                ret = True
                            End If
                        Case Else

                    End Select
                End If
            End If
            If IsNewYearsOK Then
                If _Date.Day = 31 AndAlso _Date.DayOfWeek = DayOfWeek.Friday Then
                    'New Years is on Saturday
                    ret = True
                End If
            End If

        Case Else
    End Select

    Return ret
End Function

GetBusinessToday 

GetBusinessToday has two overloads: one with no parameters to use NOW as the date, and one with parameters which can be called with a date. It first checks if we are on a weekend, then makes sure it doesn’t stop on a holiday.

VB.NET
''' <summary>
''' Returns the _Date or next valid business day
''' </summary>
''' <param name="_Date">Next Business Day after Internal GetBusinessToday</param>
''' <returns></returns>
Public Function GetBusinessToday(ByRef _Date As Date) As Date
    Dim ret As Date = _Date
    If ret.DayOfWeek = DayOfWeek.Sunday Then
        ret = ret.AddDays(+1)
    ElseIf ret.DayOfWeek = DayOfWeek.Saturday Then
        ret = ret.AddDays(+2)
    End If
    ret = HolidayAddDays(ret)
    Return ret
End Function

GetBusinessYesterday 

GetBusinessYesterday also has two overloads and gets the first prior date from either Today (GetBusinessToday) or a past date.

VB.NET
Public Function GetBusinessYesterday(ByRef _Date As Date) As Date
    Dim ret As Date = _Date
    ret = HolidaySubtractDays(ret.AddDays(-1)) ' 5/28/2013 DVF moved part from else
    If ret.DayOfWeek = DayOfWeek.Monday Then
        ret = ret.AddDays(-3)
    ElseIf ret.DayOfWeek = DayOfWeek.Sunday Then
        ret = ret.AddDays(-2)
    Else

    End If
    
    Return ret
End Function

GetBusinessTomorrow

GetBusinessTomorrow returns the next valid business day, again with two overloads.

VB.NET
Public Function GetBusinessTomorrow() As Date
    Dim ret As Date = GetBusinessToday()
    If ret.DayOfWeek = DayOfWeek.Friday Then
        ret = ret.AddDays(+3)
    ElseIf ret.DayOfWeek = DayOfWeek.Saturday Then
        ret = ret.AddDays(+2)
    Else
        ret = ret.AddDays(+1)
    End If
    ret = HolidayAddDays(ret)

    Return ret
End Function

AddBusinessDays

AddBusinessDays takes both weekends and holidays into consideration when adding days to a date. It checks each date to see if it’s a holiday and skips the holiday from the count using a callback function.

VB.NET
Public Function AddBusinessDays(ByVal StartDate As Date, ByVal WorkDays As Integer) As Date
    Dim ret As Date = StartDate
    Dim direction As Integer = 0
    If WorkDays > 0 Then
        direction = 1
    ElseIf WorkDays < 0 Then
        direction = -1
    End If
    If WorkDays <> 0 Then
        For i = 1 To Math.Abs(WorkDays)
            If direction > 0 Then
                ret = HolidayAddDays(ret.AddDays(direction))
            Else
                ret = HolidaySubtractDays(ret.AddDays(direction))
            End If
            While (ret.DayOfWeek = _
              DayOfWeek.Saturday OrElse ret.DayOfWeek = DayOfWeek.Sunday)
                ret = ret.AddDays(direction)
            End While
        Next
    End If

    Return ret
End Function

HolidayAddDays

HolidayAddDays is an internal function to find the next non-holiday day from a date. It is recursive, although the odds are high it would never need to do that. I thought someone might consider the day after Thanksgiving a holiday and wanted to try to code for that.

VB.NET
Private Function HolidayAddDays(ByVal _Date As Date) As Date
    Dim ret As Date = _Date
    If isHoliday(_Date) Then
        ret = _Date.AddDays(1)
        HolidayAddDays(ret)
    End If
    Return ret

End Function

HolidaySubtractDays

It’s the same as HolidayAddDays, but moving the other direction, they likely should have been combined. It recursively removes days from a particular date looking for a non-holiday.

VB.NET
Private Function HolidaySubtractDays(ByVal _Date As Date) As Date
    Dim ret As Date = _Date
    If isHoliday(_Date) Then
        ret = _Date.AddDays(-1)
        HolidaySubtractDays(ret)
    End If
    Return ret

End Function

AddBusinessDaysIgnoreHoliday

I added this just in case we wanted to ignore holidays in the Business day calculation. It will not return a holiday, it does ignore them while counting through the days.

LastBusinessDayOfMonth

LastBusinessDayOfMonth returns the last business day of the month passed as a parameter.

VB.NET
Public Function LastBusinessDayOfMonth(ByVal _Date As Date) As Date
    Dim ret As Date = _Date
    ret = GetBusinessYesterday(New DateTime(_Date.Year, _Date.Month + 1, 1))
    Return ret
End Function

In the Module there are also functions for the last day of the month, and just because I’m lazy, first day of month and first business day of month.

The End

That pretty much covers what I did. If I missed anything or something doesn’t work the way it should, please let me know. Of course feel free to make it better or teach me code! 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)