Date 计数X从输入日期算起的工作日数

Date 计数X从输入日期算起的工作日数,date,ms-access,vba,Date,Ms Access,Vba,我有一个Microsoft Access数据库,要求用户输入一个打开日期:值。一旦输入,这将触发另一个字段中的计算,截止日期(25 WD):。这通过后一个字段中的以下功能工作: =DateAdd("d",25,[Date opened]) 但是,我想做的是,从打开日期:中输入的日期算起,计算25个工作日。我有一个表holidays,其中包含截至2020年的英国假期列表 可以说,我如何合并为两个,以生成一个有效的截止日期(25 WD):值,该值不计算假日中列出的任何日期 例如,如果输入的日期是2

我有一个Microsoft Access数据库,要求用户输入一个
打开日期:
值。一旦输入,这将触发另一个字段中的计算,
截止日期(25 WD):
。这通过后一个字段中的以下功能工作:

=DateAdd("d",25,[Date opened])
但是,我想做的是,从
打开日期:
中输入的日期算起,计算25个工作日。我有一个表
holidays
,其中包含截至2020年的英国假期列表

可以说,我如何合并为两个,以生成一个有效的
截止日期(25 WD):
值,该值不计算
假日中列出的任何日期


例如,如果输入的日期是2015年1月1日,则该函数将从2015年1月1日算起25个工作日,这意味着它将排除该期间内的所有周末和任何银行假日,并且字段
截止日期(25 WD)
中的结果日期值也将是一个工作日(即,不是周末或银行假日)

你可能需要一个UDF来度过这段时间。比如

Function addWorkDays(addNumber As Long, Date2 As Date) As Date
'********************
'Code Courtesy of
'  Paul Eugin
'********************

    Dim finalDate As Date
    Dim i As Long, tmpDate As Date
    tmpDate = Date2
    i = 1
    Do While i <= addNumber
        If Weekday(tmpDate) <> 1 And Weekday(tmpDate) <> 7 And _
            DCount("*", "tbl_BankHolidays", "bankDate = " & Format(tmpDate, "\#mm\/dd\/yyyy\#")) = 0 Then i = i + 1
        tmpDate = DateAdd("d", 1, tmpDate)
    Loop

    Do While Weekday(tmpDate) = 1 Or Weekday(tmpDate) = 7 Or _
        DCount("*", "tbl_BankHolidays", "bankDate = " & Format(tmpDate, "\#mm\/dd\/yyyy\#")) <> 0
        tmpDate = DateAdd("d", 1, tmpDate)
    Loop

    addWorkDays = tmpDate
End Function
希望这有帮助


编辑:我添加了另一个循环,以查看结束日期是否在银行假日或周末,如果是,则将再添加一天,直到到达工作日。

您可以使用此功能:

Public Function DateAddWorkdays( _
    ByVal lngNumber As Long, _
    ByVal datDate As Date, _
    Optional ByVal booWorkOnHolidays As Boolean) _
    As Date

'   Adds lngNumber of workdays to datDate.
'   2014-10-03. Cactus Data ApS, CPH

    ' Calendar days per week.
    Const clngWeekdayCount  As Long = 7
    ' Workdays per week.
    Const clngWeekWorkdays  As Long = 5
    ' Average count of holidays per week maximum.
    Const clngWeekHolidays  As Long = 1
    ' Maximum valid date value.
    Const cdatDateRangeMax  As Date = #12/31/9999#
    ' Minimum valid date value.
    Const cdatDateRangeMin  As Date = #1/1/100#

    Dim aHolidays() As Date

    Dim lngDays     As Long
    Dim lngDiff     As Long
    Dim lngDiffMax  As Long
    Dim lngSign     As Long
    Dim datDate1    As Date
    Dim datDate2    As Date
    Dim datLimit    As Date
    Dim lngHoliday  As Long


    lngSign = Sgn(lngNumber)
    datDate2 = datDate

    If lngSign <> 0 Then
        If booWorkOnHolidays = True Then
            ' Holidays are workdays.
        Else
            ' Retrieve array with holidays between datDate and datDate + lngDiffMax.
            ' Calculate the maximum calendar days per workweek.
            lngDiffMax = lngNumber * clngWeekdayCount / (clngWeekWorkdays - clngWeekHolidays)
            ' Add one week to cover cases where a week contains multiple holidays.
            lngDiffMax = lngDiffMax + Sgn(lngDiffMax) * clngWeekdayCount
            datDate1 = DateAdd("d", lngDiffMax, datDate)
            aHolidays = GetHolidays(datDate, datDate1)
        End If
        Do Until lngDays = lngNumber
            If lngSign = 1 Then
                datLimit = cdatDateRangeMax
            Else
                datLimit = cdatDateRangeMin
            End If
            If DateDiff("d", DateAdd("d", lngDiff, datDate), datLimit) = 0 Then
                ' Limit of date range has been reached.
                Exit Do
            End If

            lngDiff = lngDiff + lngSign
            datDate2 = DateAdd("d", lngDiff, datDate)
            Select Case Weekday(datDate2)
                Case vbSaturday, vbSunday
                    ' Skip weekend.
                Case Else
                    ' Check for holidays to skip.
                    ' Ignore error when using LBound and UBound on an unassigned array.
                    On Error Resume Next
                    For lngHoliday = LBound(aHolidays) To UBound(aHolidays)
                        If Err.Number > 0 Then
                            ' No holidays between datDate and datDate1.
                        ElseIf DateDiff("d", datDate2, aHolidays(lngHoliday)) = 0 Then
                            ' This datDate2 hits a holiday.
                            ' Subtract one day before adding one after the loop.
                            lngDays = lngDays - lngSign
                            Exit For
                        End If
                    Next
                    On Error GoTo 0
                    lngDays = lngDays + lngSign
            End Select
        Loop
    End If

    DateAddWorkdays = datDate2

End Function

Public Function GetHolidays( _
    ByVal datDate1 As Date, _
    ByVal datDate2 As Date, _
    Optional ByVal booDesc As Boolean) _
    As Date()

'   Finds the count of holidays between datDate1 and datDate2.
'   The holidays are returned as an array of dates.
'   DAO objects are declared static to speed up repeated calls with identical date parameters.
'   2014-10-03. Cactus Data ApS, CPH

    ' The table that holds the holidays.
    Const cstrTable             As String = "tblHoliday"
    ' The field of the table that holds the dates of the holidays.
    Const cstrField             As String = "HolidayDate"
    ' Constants for the arrays.
    Const clngDimRecordCount    As Long = 2
    Const clngDimFieldOne       As Long = 0

    Static dbs              As DAO.Database
    Static rst              As DAO.Recordset

    Static datDate1Last     As Date
    Static datDate2Last     As Date

    Dim adatDays()  As Date
    Dim avarDays    As Variant

    Dim strSQL      As String
    Dim strDate1    As String
    Dim strDate2    As String
    Dim strOrder    As String
    Dim lngDays     As Long

    If DateDiff("d", datDate1, datDate1Last) <> 0 Or DateDiff("d", datDate2, datDate2Last) <> 0 Then
        ' datDate1 or datDate2 has changed since the last call.
        strDate1 = Format(datDate1, "\#yyyy\/mm\/dd\#")
        strDate2 = Format(datDate2, "\#yyyy\/mm\/dd\#")
        strOrder = Format(booDesc, "\A\s\c;\D\e\s\c")

        strSQL = "Select " & cstrField & " From " & cstrTable & " " & _
            "Where " & cstrField & " Between " & strDate1 & " And " & strDate2 & " " & _
            "Order By 1 " & strOrder

        Set dbs = CurrentDb
        Set rst = dbs.OpenRecordset(strSQL, dbOpenSnapshot)

        ' Save the current set of date parameters.
        datDate1Last = datDate1
        datDate2Last = datDate2
    End If

    lngDays = rst.RecordCount
    If lngDays = 0 Then
        ' Leave adatDays() as an unassigned array.
    Else
        ReDim adatDays(lngDays - 1)
        ' As repeated calls may happen, do a movefirst.
        rst.MoveFirst
        avarDays = rst.GetRows(lngDays)
        ' rst is now positioned at the last record.
        For lngDays = LBound(avarDays, clngDimRecordCount) To UBound(avarDays, clngDimRecordCount)
            adatDays(lngDays) = avarDays(clngDimFieldOne, lngDays)
        Next
    End If

    ' DAO objects are static.
    ' Set rst = Nothing
    ' Set dbs = Nothing

    GetHolidays = adatDays()

End Function
公共功能DateAddWorkdays(_
ByVal LGNNUMBER尽可能长_
ByVal datDate作为日期_
可选的ByVal booworkinholidays(作为布尔值)_
截止日期
'将工作日数添加到datDate。
'2014-10-03.仙人掌数据ApS,CPH
'每周日历天数。
Const clngWeekdayCount作为Long=7
“每周工作日。
常数clngWeekWorkdays的长度=5
'每周最多的平均假日数。
Const clngWeekHolidays长度=1
'最大有效日期值。
Const cdatDateRangeMax As Date=#1999年12月31日#
'最小有效日期值。
常数cdatDateRangeMin作为日期=#1/1/100#
Dim aHolidays()作为日期
暗淡的长发
模糊lngDiff与长
Dim lngDiffMax尽可能长
暗号只要长
Dim datDate1作为日期
Dim datDate2作为日期
日期限制为日期
朦胧的白天和漫长的白天一样
lngSign=Sgn(lngNumber)
datDate2=datDate
如果lngSign为0,则
如果booworknholidays=True,则
“假期就是工作日。
其他的
'检索datDate和datDate+lngDiffMax之间有假日的数组。
'计算每个工作周的最大日历天数。
lngDiffMax=lngNumber*CLNGWEEKKDAYCOUNT/(clngWeekWorkdays-clngWeekHolidays)
'添加一周以涵盖一周包含多个假期的情况。
lngDiffMax=lngDiffMax+Sgn(lngDiffMax)*clngWeekdayCount
datDate1=DateAdd(“d”,lngDiffMax,datDate)
aHolidays=GetHolidays(datDate,datDate1)
如果结束
直到lngDays=lngNumber
如果lngSign=1,则
datLimit=cdatDateRangeMax
其他的
datLimit=CDATDATERAGEMIN
如果结束
如果DateDiff(“d”,DateAdd(“d”,lngDiff,datDate),datLimit)=0,则
'已达到日期范围的限制。
退出Do
如果结束
lngDiff=lngDiff+lngSign
datDate2=DateAdd(“d”,lngDiff,datDate)
选择案例工作日(datDate2)
案件:星期六、星期日
“跳过周末。
其他情况
“检查是否可以跳过假期。
'在未分配数组上使用LBound和UBound时忽略错误。
出错时继续下一步
对于lngHoliday=LBound(aHolidays)到UBound(aHolidays)
如果错误编号>0,则
“datDate和datDate1之间没有假日。
ElseIf DateDiff(“d”,datDate2,aHolidays(lngHoliday))=0
“这是一个假日。
'在循环后加一之前减去一天。
lngDays=lngDays-lngSign
退出
如果结束
下一个
错误转到0
lngDays=lngDays+lngSign
结束选择
环
如果结束
DateAddWorkdays=datDate2
端函数
公众假期(_
ByVal DatDate 1作为日期_
ByVal DatDate 2作为日期_
可选的ByVal booDesc(作为布尔值)_
截止日期()
'查找datDate1和datDate2之间的假日计数。
'假日以日期数组的形式返回。
'DAO对象被声明为静态,以加快使用相同日期参数的重复调用。
'2014-10-03.仙人掌数据ApS,CPH
“放假日的桌子。
常量cstrTable为String=“tblHoliday”
'表中包含假日日期的字段。
Const cstrField As String=“HolidayDate”
'数组的常量。
Const clngDimRecordCount的长度=2
Const clngDimFieldOne的长度=0
作为DAO.Database的静态数据库
静态rst作为DAO.Recordset
静态DatDate1最后为日期
静态datDate2Last As Date
Dim adatDays()作为日期
暗淡的前卫
作为字符串的Dim strSQL
作为字符串的Dim strDate1
作为字符串的Dim strDate2
作为字符串的Dim strOrder
暗淡的长发
如果DateDiff(“d”,datDate1,datDate1Last)0或DateDiff(“d”,datDate2,datDate2Last)0,则
'自上次调用以来,datDate1或datDate2已更改。
strDate1=格式(datDate1,\\\ yyyy\/mm\/dd\\)
strDate2=格式(datDate2,\\\ yyyy\/mm\/dd\\)
strOrder=格式(booDesc,“\A\s\c;\D\e\s\c”)
strSQL=“选择”&cstrField&“来自”&cstrTable&”
Public Function DateAddWorkdays( _
    ByVal lngNumber As Long, _
    ByVal datDate As Date, _
    Optional ByVal booWorkOnHolidays As Boolean) _
    As Date

'   Adds lngNumber of workdays to datDate.
'   2014-10-03. Cactus Data ApS, CPH

    ' Calendar days per week.
    Const clngWeekdayCount  As Long = 7
    ' Workdays per week.
    Const clngWeekWorkdays  As Long = 5
    ' Average count of holidays per week maximum.
    Const clngWeekHolidays  As Long = 1
    ' Maximum valid date value.
    Const cdatDateRangeMax  As Date = #12/31/9999#
    ' Minimum valid date value.
    Const cdatDateRangeMin  As Date = #1/1/100#

    Dim aHolidays() As Date

    Dim lngDays     As Long
    Dim lngDiff     As Long
    Dim lngDiffMax  As Long
    Dim lngSign     As Long
    Dim datDate1    As Date
    Dim datDate2    As Date
    Dim datLimit    As Date
    Dim lngHoliday  As Long


    lngSign = Sgn(lngNumber)
    datDate2 = datDate

    If lngSign <> 0 Then
        If booWorkOnHolidays = True Then
            ' Holidays are workdays.
        Else
            ' Retrieve array with holidays between datDate and datDate + lngDiffMax.
            ' Calculate the maximum calendar days per workweek.
            lngDiffMax = lngNumber * clngWeekdayCount / (clngWeekWorkdays - clngWeekHolidays)
            ' Add one week to cover cases where a week contains multiple holidays.
            lngDiffMax = lngDiffMax + Sgn(lngDiffMax) * clngWeekdayCount
            datDate1 = DateAdd("d", lngDiffMax, datDate)
            aHolidays = GetHolidays(datDate, datDate1)
        End If
        Do Until lngDays = lngNumber
            If lngSign = 1 Then
                datLimit = cdatDateRangeMax
            Else
                datLimit = cdatDateRangeMin
            End If
            If DateDiff("d", DateAdd("d", lngDiff, datDate), datLimit) = 0 Then
                ' Limit of date range has been reached.
                Exit Do
            End If

            lngDiff = lngDiff + lngSign
            datDate2 = DateAdd("d", lngDiff, datDate)
            Select Case Weekday(datDate2)
                Case vbSaturday, vbSunday
                    ' Skip weekend.
                Case Else
                    ' Check for holidays to skip.
                    ' Ignore error when using LBound and UBound on an unassigned array.
                    On Error Resume Next
                    For lngHoliday = LBound(aHolidays) To UBound(aHolidays)
                        If Err.Number > 0 Then
                            ' No holidays between datDate and datDate1.
                        ElseIf DateDiff("d", datDate2, aHolidays(lngHoliday)) = 0 Then
                            ' This datDate2 hits a holiday.
                            ' Subtract one day before adding one after the loop.
                            lngDays = lngDays - lngSign
                            Exit For
                        End If
                    Next
                    On Error GoTo 0
                    lngDays = lngDays + lngSign
            End Select
        Loop
    End If

    DateAddWorkdays = datDate2

End Function

Public Function GetHolidays( _
    ByVal datDate1 As Date, _
    ByVal datDate2 As Date, _
    Optional ByVal booDesc As Boolean) _
    As Date()

'   Finds the count of holidays between datDate1 and datDate2.
'   The holidays are returned as an array of dates.
'   DAO objects are declared static to speed up repeated calls with identical date parameters.
'   2014-10-03. Cactus Data ApS, CPH

    ' The table that holds the holidays.
    Const cstrTable             As String = "tblHoliday"
    ' The field of the table that holds the dates of the holidays.
    Const cstrField             As String = "HolidayDate"
    ' Constants for the arrays.
    Const clngDimRecordCount    As Long = 2
    Const clngDimFieldOne       As Long = 0

    Static dbs              As DAO.Database
    Static rst              As DAO.Recordset

    Static datDate1Last     As Date
    Static datDate2Last     As Date

    Dim adatDays()  As Date
    Dim avarDays    As Variant

    Dim strSQL      As String
    Dim strDate1    As String
    Dim strDate2    As String
    Dim strOrder    As String
    Dim lngDays     As Long

    If DateDiff("d", datDate1, datDate1Last) <> 0 Or DateDiff("d", datDate2, datDate2Last) <> 0 Then
        ' datDate1 or datDate2 has changed since the last call.
        strDate1 = Format(datDate1, "\#yyyy\/mm\/dd\#")
        strDate2 = Format(datDate2, "\#yyyy\/mm\/dd\#")
        strOrder = Format(booDesc, "\A\s\c;\D\e\s\c")

        strSQL = "Select " & cstrField & " From " & cstrTable & " " & _
            "Where " & cstrField & " Between " & strDate1 & " And " & strDate2 & " " & _
            "Order By 1 " & strOrder

        Set dbs = CurrentDb
        Set rst = dbs.OpenRecordset(strSQL, dbOpenSnapshot)

        ' Save the current set of date parameters.
        datDate1Last = datDate1
        datDate2Last = datDate2
    End If

    lngDays = rst.RecordCount
    If lngDays = 0 Then
        ' Leave adatDays() as an unassigned array.
    Else
        ReDim adatDays(lngDays - 1)
        ' As repeated calls may happen, do a movefirst.
        rst.MoveFirst
        avarDays = rst.GetRows(lngDays)
        ' rst is now positioned at the last record.
        For lngDays = LBound(avarDays, clngDimRecordCount) To UBound(avarDays, clngDimRecordCount)
            adatDays(lngDays) = avarDays(clngDimFieldOne, lngDays)
        Next
    End If

    ' DAO objects are static.
    ' Set rst = Nothing
    ' Set dbs = Nothing

    GetHolidays = adatDays()

End Function