65.9K
CodeProject 正在变化。 阅读更多。
Home

节假日和工作日计算及转换

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (9投票s)

2013年5月25日

CPOL

3分钟阅读

viewsIcon

55742

downloadIcon

976

在日期之间添加和减去工作日。计算日期是否为节假日。

介绍  

该项目的目的是帮助报告系统确定运行报告的有效日期。它具有确定日期是否为节假日的函数,以及允许报告系统跳过周末的函数,以满足诸如“我需要昨天、今天或明天的数据”之类的需求。

背景 

我首先回顾了 2003 年的 ASP 文章“处理工作日、节假日和周末”,我将其翻译并进一步扩展到 VB.NET。 当然,每个人庆祝的节日可能不同,因此您需要修改代码以适应您的目的。 我添加了我认为有用的功能,或者是我正在开发的 ASPX Live Reporting 系统中需要的功能。

节假日有两种基本类型:一些是设置为特定日期的,例如圣诞节,属于浮动假日。 如果其中一个落在星期六,那么星期五就被认为是假日,星期日变成星期一,等等。 固定假日是每周的特定一天。 我没有涵盖复活节,因为它总是在星期天,而且本来就不是工作日。

由于这用于报告引擎,并且我的客户周末工作,但不希望将它们纳入报告的考虑范围。 该模块重新定义了“今天”的概念。 周六和周日,“今天”就是星期一。 因此,周六和周日,“明天”就是星期二。 反之,周日和周一,“昨天”就是星期五。 如果是星期六,并且节假日落在星期一,那么“明天”就是星期三,“今天”就是星期二。 你需要全神贯注才能理解这一点。

使用代码

由于这一切都基于节假日,因此 isHoliday 函数是一个很好的起点。

IsHoliday

如果找到节假日,则返回布尔值 True。 有一些全局属性可以通过 IsHolidayOK 样式名称(例如 IsNewYearsOK)来关闭节假日。 它们都默认为 true,并且可以通过编程方式更改。

''' <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 有两个重载:一个没有参数,使用 NOW 作为日期;另一个带有参数,可以用日期调用。 它首先检查我们是否在周末,然后确保它不会停在节假日。

''' <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 也有两个重载,并从今天(GetBusinessToday)或过去的日期获取第一个之前的日期。

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 返回下一个有效工作日,同样有两个重载。

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 在向日期添加天数时,会同时考虑周末和节假日。 它检查每个日期,看看是否是节假日,并使用回调函数从计数中跳过节假日。

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 是一个内部函数,用于查找日期之后的下一个非节假日。 它是递归的,尽管很可能永远不需要这样做。 我认为有人可能会认为感恩节后的第二天是节假日,并想尝试为此编写代码。

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

它与 HolidayAddDays 相同,但是朝着相反的方向移动,它们很可能应该合并。 它递归地从特定日期中删除天数,以寻找非节假日。

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

我添加这个功能只是为了以防万一我们想在工作日计算中忽略节假日。 它不会返回节假日,而是在计算天数时忽略它们。

LastBusinessDayOfMonth

LastBusinessDayOfMonth 返回作为参数传递的月份的最后一个工作日。

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

在该模块中,还有用于该月的最后一天的函数,以及因为我懒惰,该月的第一天和该月的第一个工作日。

结束

这几乎涵盖了我所做的一切。 如果我遗漏了任何东西,或者某些东西没有按预期工作,请告诉我。 当然,请随时改进它或教我代码!

© . All rights reserved.