{#post3800470} ++Introduction++

While I have been able to find an example of how to calculate the number of business days between two dates, I haven't been able to find a published method to add business days. Excel has recently added this function, but Microsoft has yet to give it to the Access world. So, I have undertaken this challenge. The function that I have created allows the user to specify what days are not included in the count (for weekends) and it also allows for a list of holidays to be excluded as well. Holidays must be entered in a table for this to work. I have created my own method for automatically getting this information from the Internet, but that is a subject for another post.

++Tables++

The best method that I could come up with was to have a list of available dates and then to count down the records for the specified number of days. This means that we need a list of dates. Instead of having to type them all into a table, we will let Access take care of this problem. All we need is a table with a single field in it:

1. ++tbl_ExpansionDigits++
2. Digit

Now add 10 records with the values 0 to 9

We also need a table of holidays. My table has a date field and a name field so that I can list the holiday name if I want to.

That is it for tables

++Queries++

We actually only need one query. This is what performs the magic of giving us dates. Mine is named qryPossibleDates.

1. SELECT Cdate([tbl_expansiondigits_1]![Digit]
2. +[tbl_expansiondigits_2]![Digit]*10
3. +[tbl_expansiondigits_3]![Digit]*100
4. +[tbl_expansiondigits_4]![Digit]*1000
5. +[tbl_expansiondigits_5]![Digit]*10000) AS CalcDate, Weekday(CalcDate) AS WeekDayNumber
6. FROM tbl_expansiondigits AS tbl_expansiondigits_1, tbl_expansiondigits AS tbl_expansiondigits_2, tbl_expansiondigits AS tbl_expansiondigits_3, tbl_expansiondigits AS tbl_expansiondigits_4, tbl_expansiondigits AS tbl_expansiondigits_5
7. WHERE ((([tbl_expansiondigits_1]![Digit]
8. +[tbl_expansiondigits_2]![Digit]*10
9. +[tbl_expansiondigits_3]![Digit]*100
10. +[tbl_expansiondigits_4]![Digit]*1000
11. +[tbl_expansiondigits_5]![Digit]*10000)>=36000));

This gives us dates from 7/24/1998 to 10/13/2173 (a total of 64,000 days) plus the weekday number (1 for Sunday, 7 for Saturday).

++Code++

First we are going to need a function to convert a binary-like number to a decimal value. I use this to for binary flags to know which days to exclude from the count.

1. Private Function ConvertToDecimal(lngBinary As Long) As Long
2. Dim strNumber As String
3. Dim i As Integer
4. Dim lngAccumulator As Long
5. Dim n As Integer
6. Dim s As String
7. Dim p As Integer
8. strNumber = Format(lngBinary, "00000000")
9. p = 1
10. For i = 8 To 1 Step -1
11. s = Mid(strNumber, p, 1)
12. If s = "1" Then
13. n = CInt(i) - 1
14. lngAccumulator = lngAccumulator + 2 ^ n
15. End If
16. p = p + 1
17. Next
18. ConvertToDecimal = lngAccumulator
19. End Function

Now for the main function.

1. Public Function AddDays(dteStart As Date, intInterval As Integer, lngExcludePattern As Long) As Date
2. Dim lngExcludeDates As Long
3. Dim strSelect As String
4. Dim strWhere As String
5. Dim strSQL As String
6. Dim db As DAO.Database
7. Dim rst As DAO.Recordset
8. Dim i As Integer
9. If lngExcludePattern <> 0 Then
10. lngExcludeDates = ConvertToDecimal(lngExcludePattern)
11. strSelect = "SELECT CalcDate As AddedDate FROM qryPossibleDates"
12. strWhere = "CalcDate = #" & dteStart & "# Or (CalcDate >= #" & dteStart & "# And CalcDate Not In(SELECT hDate FROM dbo_Holidays WHERE hDate >= #" & dteStart & "#)"
13. If (lngExcludeDates And 64) > 0 Then
14. strWhere = strWhere & " And WeekDayNumber <> 1"
15. End If
16. If (lngExcludeDates And 32) > 0 Then
17. strWhere = strWhere & " And WeekDayNumber <> 2"
18. End If
19. If (lngExcludeDates And 16) > 0 Then
20. strWhere = strWhere & " And WeekDayNumber <> 3"
21. End If
22. If (lngExcludeDates And 8) > 0 Then
23. strWhere = strWhere & " And WeekDayNumber <> 4"
24. End If
25. If (lngExcludeDates And 4) > 0 Then
26. strWhere = strWhere & " And WeekDayNumber <> 5"
27. End If
28. If (lngExcludeDates And 2) > 0 Then
29. strWhere = strWhere & " And WeekDayNumber <> 6"
30. End If
31. If (lngExcludeDates And 1) > 0 Then
32. strWhere = strWhere & " And WeekDayNumber <> 7"
33. End If
34. strWhere = strWhere & ")"
35. strSQL = strSelect & " WHERE " & strWhere
36. Debug.Print strSQL
37. Set db = CurrentDb
38. Set rst = db.OpenRecordset(strSQL, dbOpenDynaset, dbSeeChanges)
39. rst.Move intInterval
41. Set db = Nothing
42. rst.Close
43. Set rst = Nothing
44. Else
45. AddDays = dteStart + intInterval
46. End If
47. End Function

First, I test for the value in lngExcludePattern. If it is 0, then I'm just counting calendar days and it skips to the bottom. Otherwise, I build a query string that pulls from the query above adding the criteria. You will notice that it starts with CalcDate = #" & dteStart & "#"

. This is for situations where dteStart is not a day that is included in the query as it does mess with the days involved (I found this out after troubleshooting why my counts were off). It then has the criteria to only show records that are greater than or equal to dteStart and not in the list of holidays, again filtered to be greater than or equal to dteStart (for performance). I then test for the binary flags present to add additional criteria to exclude the days of the week that we don't want included. I then open the recordset and use the .Move method to go to the correct record in the recordset to get my date. And that is it!

。这适用于dteStart不是查询中包含的那一天的情况，因为它会影响所涉及的日期（我在排除了为什么我的计数关闭后发现了这一点）。然后它具有仅显示大于或等于dteStart且不在假日列表中的记录的标准，再次过滤为大于或等于dteStart（用于性能）。然后，我测试存在的二进制标志，以添加其他标准，以排除我们不想包含的一周中的日期。然后我打开记录集并使用.Move方法转到记录集中的正确记录以获取我的日期。就是这样！

++Use++

To use this function, you pass the start date to which you want to add x number of days (dteStart), the number of days that you want to add (intInterval) and then the days that you want to exclude (lngExcludePattern). The first two are pretty simple, but the Exclude Pattern needs more explanation. I have it setup to do it as binary flags. The first flag is Sunday and the last flag is Saturday. So if I want my weekends to be Saturday and Sunday, my Exclude Pattern would be 1000001. If I want Wednesdays excluded as well, then my pattern would be 1001001. If I just want Saturdays excluded it would be 0000001, which can also be entered as 1 because it is passed as a long integer. So if I want to add 5 days to 1/17/16, excluding Saturdays and Sundays, I would call my function like this:

++Conclusion++

There are probably other ways to do this, but this seemed like the most versatile method and it actually does what I want it to. If anyone has any critiques, I welcome them.

(未经同意，请勿转载)