【问题标题】:Count X Amount of Working Days from Date Entered从输入日期开始计算 X 个工作日
【发布时间】:2015-08-01 15:12:42
【问题描述】:

我有一个 Microsoft Access 数据库,用户需要在其中输入 Date Opened: 值。输入后,将触发另一个字段Deadline (25 WD): 中的计算。这通过后一个字段中的以下函数起作用:

=DateAdd("d",25,[Date opened])

然而,我想做的是从Date Opened: 中输入的日期算起 25 个工作天。我有一张表holidays,其中包含截至 2020 年的英国假期列表。

我如何合并到两个,可以这么说,以产生一个有效的Deadline (25 WD): 值,它不计算holidays 中列出的任何日期?

例如,如果输入的日期是 2015 年 1 月 1 日,那么该函数将从 2015 年 1 月 1 日开始计算 25 个工作日,这意味着它将排除该期间内的所有周末和任何银行假日,并且 Deadline (25 WD) 字段中生成的日期值也将是工作日(即不是周末或银行假日)。

【问题讨论】:

  • (我想)你计算Date OpenedDate Opened + 25 days之间的假期数,并将结果添加到Date Opened + 25 days
  • 是的,我认为可能会这样做。虽然问题是我还需要找出一种计算周末的方法。当然,除非我将周末添加到我的 holidays 表中。
  • 我不同意 - 这不是题外话,而且是非常相同的问题,没有额外的要求。问一个新问题几乎肯定会构成重复,因为要求将保持不变,唯一可能的结果是答案分布在 2 个问题上。经过一番调查后,它似乎并没有真正解决,因为该字段中的结果值有时仍会落在银行假期或周末,而这只是通过使用系统可能会出现的问题。如果答案(更新的或新的)解决了这个问题,那么我会相应地接受。
  • 好的,在这种情况下,将 25 个工作日添加到 2015 年 1 月 1 日,将是 2015 年 2 月 6 日,即星期五。假设是 26 天,那么您想要 2015 年 2 月 9 日?如果是这种情况,请检查我编辑的答案。

标签: date ms-access vba


【解决方案1】:

您可能需要一个 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

因此,当您将日期添加 25 天时,它将跳过存储在您的表中的所有周末和银行假日 - tbl_BankHolidays

? addWorkDays(25, Date())
  25/06/2015 

希望这会有所帮助!

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

【讨论】:

  • 我发现了一个问题-该字段包含的日期有时是周末,我希望它排除周末,结果也是工作日-这可能吗?最初我并没有意识到这一点,但是在对系统进行了更深入、更深入的测试之后,出现了这个问题。我还将更新现有问题以包含此问题。谢谢。
  • 感谢您的更新,保罗弗朗西斯。我不得不再次不同意,因为我没有新问题 - 一旦我实施了您的答案,初始问题的一个新方面已经浮出水面,并且在关于同一问题的两个问题之间拆分答案有点违反直觉。但是,您更新的答案完全符合预期,因此,我重新接受了您的(更新的)答案。感谢您对此的帮助。
  • 很高兴听到您解决了这个问题。我撤回了我的投票。祝你好运。 :)
【解决方案2】:

你可以使用这个功能:

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

【讨论】:

    猜你喜欢
    • 2022-12-11
    • 1970-01-01
    • 2014-03-30
    • 1970-01-01
    • 1970-01-01
    • 2023-01-12
    • 2010-11-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多