From: Tom van Stiphout on
On Fri, 14 May 2010 19:14:23 -0400, "(PeteCresswell)" <x(a)y.Invalid>
wrote:

How slow? FMS Inc has a BusinessDayAdd and BusinessDayDiff loosely
based on the same algorithm, and I have never had a problem. Probably
never had to run it over several years.

Do bonds really care about business days? That surprises me.

Yes, you could pre-populate a table overnight every night. Or perhaps
even populate a table with offsets on a more permanent basis. Go for
it! Maybe you even want to contribute it back to the greater good.
Just be careful about the oleaut32 bug reported here:
http://support.microsoft.com/kb/200299

-Tom.
Microsoft Access MVP


>Business days between two dates has got to involve a tblHoliday.
>
>Right now I've got such a table - mostly populated with bank
>holidays and I compute biz days between two dates by iterating
>through the days one-by-one: if it's a Saturday or Sunday it's
>not a biz day.... otherwise I do a table lookup - no hit, then
>it's a biz day.
>
>But this is *really* slow for processing a record set of, say,
>2000 records.
>
>I'm thinking a faster approach would involve re-creating a table
>of business days from some begin date to some end date each time
>tblHolidays is updated and then doing some SQL magic to bang a
>recordset up against that table and somehow bulk-calculate, for
>instance, the number of business days between a bond's maturity
>date and today's date... or whether or not a bond matures within
>60 business days.
>
>Has anybody come up with such an approach?
From: Bob Quintal on
"(PeteCresswell)" <x(a)y.Invalid> wrote in
news:7ulru5htgd47ait4t2ot7c86gblr0cprcu(a)4ax.com:

> Business days between two dates has got to involve a tblHoliday.
>
> Right now I've got such a table - mostly populated with bank
> holidays and I compute biz days between two dates by iterating
> through the days one-by-one: if it's a Saturday or Sunday it's
> not a biz day.... otherwise I do a table lookup - no hit, then
> it's a biz day.
>
> But this is *really* slow for processing a record set of, say,
> 2000 records.
>
> I'm thinking a faster approach would involve re-creating a table
> of business days from some begin date to some end date each time
> tblHolidays is updated and then doing some SQL magic to bang a
> recordset up against that table and somehow bulk-calculate, for
> instance, the number of business days between a bond's maturity
> date and today's date... or whether or not a bond matures within
> 60 business days.
>
> Has anybody come up with such an approach?

I calculate the business interval as follows.
weeks = datediff("ww",dtFrom,dtTo,vbmonday)
then add the days in the zeroeth week, subtract the days after dtTo
from the final week, and subtract the dCount() of the number of days
in tblHolidays where holiday between dtFrom and dtTo

this means one dCount per calculation, as opposed to, if I understand
you, a dLookup for each day in the interval.

From: Arvin Meyer [MVP] on
The case of bond maturity can run almosrt instantly since it doesn't need to
run either of those functions or the one at:

http://www.datastrat.com/Code/GetBusinessDay.txt

for every row. For bond maturity in say 60 business days, use the above link
to add 60 days to the current day, then store that date in a global
variable. In a query, use a function to compare that variable to the Bond
Maturity Date. Since it does need to run the function thousands of times,
it's very fast that way.
--
Arvin Meyer, MCP, MVP
http://www.datastrat.com
http://www.accessmvp.com
http://www.mvps.org/access


"Tom van Stiphout" <tom7744.no.spam(a)cox.net> wrote in message
news:e1asu59leelv0mdfog6ee1bdddsi0at27b(a)4ax.com...
> On Fri, 14 May 2010 19:14:23 -0400, "(PeteCresswell)" <x(a)y.Invalid>
> wrote:
>
> How slow? FMS Inc has a BusinessDayAdd and BusinessDayDiff loosely
> based on the same algorithm, and I have never had a problem. Probably
> never had to run it over several years.
>
> Do bonds really care about business days? That surprises me.
>
> Yes, you could pre-populate a table overnight every night. Or perhaps
> even populate a table with offsets on a more permanent basis. Go for
> it! Maybe you even want to contribute it back to the greater good.
> Just be careful about the oleaut32 bug reported here:
> http://support.microsoft.com/kb/200299
>
> -Tom.
> Microsoft Access MVP
>
>
>>Business days between two dates has got to involve a tblHoliday.
>>
>>Right now I've got such a table - mostly populated with bank
>>holidays and I compute biz days between two dates by iterating
>>through the days one-by-one: if it's a Saturday or Sunday it's
>>not a biz day.... otherwise I do a table lookup - no hit, then
>>it's a biz day.
>>
>>But this is *really* slow for processing a record set of, say,
>>2000 records.
>>
>>I'm thinking a faster approach would involve re-creating a table
>>of business days from some begin date to some end date each time
>>tblHolidays is updated and then doing some SQL magic to bang a
>>recordset up against that table and somehow bulk-calculate, for
>>instance, the number of business days between a bond's maturity
>>date and today's date... or whether or not a bond matures within
>>60 business days.
>>
>>Has anybody come up with such an approach?


From: KenSheridan via AccessMonster.com on
You can either use a suitable function, which should not be unduly slow, or
by means of a JOIN. For the former I use the following function, which
allows for different sets of holiday dates for different countries as in the
UK each constituent country has differing public holidays, and the Republic
of Ireland also differs. It also gives the option of including or excluding
public holidays from the count:

Public Function WorkDaysDiff(varLastDate As Variant, _
varFirstDate As Variant, _
strCountry As String, _
Optional blnExcludePubHols As Boolean = False) As
Variant

Const conSATURDAY As Integer = 6
Const conSUNDAY As Integer = 7

Dim lngDaysDiff As Long, lngWeekendDays As Long
Dim intPubHols As Integer

If IsNull(varLastDate) Or IsNull(varFirstDate) Then
Exit Function
End If

' if first date is Sat or Sun start on following Monday
Select Case WeekDay(varFirstDate, vbMonday)
Case conSATURDAY
varFirstDate = varFirstDate + 2
Case conSUNDAY
varFirstDate = varFirstDate + 1
End Select

' if last date is Sat or Sun finish on following Monday
Select Case WeekDay(varLastDate, vbMonday)
Case conSATURDAY
varLastDate = varLastDate + 2
Case conSUNDAY
varLastDate = varLastDate + 1
End Select

' get total date difference in days
lngDaysDiff = DateDiff("d", varFirstDate, varLastDate)

' get date difference in weeks and multiply by 2
' to get number of weekend days
lngWeekendDays = DateDiff("ww", varFirstDate, varLastDate, vbMonday) * 2

' subtract number of weekend days from total date difference
' to return number of working days
WorkDaysDiff = lngDaysDiff - lngWeekendDays

' exclude public holidays if required
If blnExcludePubHols Then
intPubHols = DCount("*", "qryPubHols", "HolDate Between #" _
& Format(varFirstDate, "mm/dd/yy") & "# And #" & _
Format(varLastDate - 1, "mm/dd/yy") & "#" & _
" And Country = " & Chr(34) & strCountry & Chr(34))

WorkDaysDiff = WorkDaysDiff - intPubHols
End If

End Function

To do it with a JOIN you don't need to recreate a table each time; simply
create a calendar table with dates over a 10 or 20 year period say. You can
either include all dates and exclude the weekends from the count by calling
the Weekday function in a query, or you can just include Monday to Friday
dates. You can then either add a Boolean IsHoliday column to the table, or
you can join it to a Holidays table in a query to exclude the holidays from
the count. The following function, of which DAO and ADO versions are
included below, can be used to create a calendar table, and allows for
selected days of the week only to be included by passing a list of days into
the function as the final parameter array argument.

With ADO:

Public Function MakeCalendar(strTable As String, _
dtmStart As Date, _
dtmEnd As Date, _
ParamArray varDays() As Variant)

' Accepts: Name of calendar table to be created: String.
' Start date for calendar: DateTime.
' End date for calendar: DateTime.
' Days of week to be included in calendar
' as value list, e,g 2,3,4,5,6 for Mon-Fri
' (use 0 to include all days of week)

Dim cmd As ADODB.Command
Dim cat As New ADOX.Catalog
Dim tbl As ADOX.Table
Dim strSQL As String
Dim dtmDate As Date
Dim varDay As Variant
Dim lngDayNum As Long

Set cmd = New ADODB.Command
cmd.ActiveConnection = CurrentProject.Connection
cmd.CommandType = adCmdText

Set cat = New Catalog
cat.ActiveConnection = CurrentProject.Connection

' does table exist? If so get user confirmation to delete it
On Error Resume Next
Set tbl = cat(strTable)
If Err = 0 Then
If MsgBox("Replace existing table: " & _
strTable & "?", vbYesNo + vbQuestion, _
"Delete Table?") = vbYes Then
strSQL = "DROP TABLE " & strTable
cmd.CommandText = strSQL
cmd.Execute
Else
Exit Function
End If
End If
On Error GoTo 0

' create new table
strSQL = "CREATE TABLE " & strTable & _
"(calDate DATETIME, " & _
"CONSTRAINT PrimaryKey PRIMARY KEY (calDate))"
cmd.CommandText = strSQL
cmd.Execute

' refresh database window
Application.RefreshDatabaseWindow

' refresh catalog
cat.Tables.Refresh

If varDays(0) = 0 Then
' fill table with all dates
For dtmDate = dtmStart To dtmEnd
lngDayNum = lngDayNum + 1
strSQL = "INSERT INTO " & strTable & "(calDate) " & _
"VALUES(#" & Format(dtmDate, "mm/dd/yyyy") & "#)"
cmd.CommandText = strSQL
cmd.Execute
Next dtmDate
Else
' fill table with dates of selected days of week only
For dtmDate = dtmStart To dtmEnd
For Each varDay In varDays()
If Weekday(dtmDate) = varDay Then
lngDayNum = lngDayNum + 1
strSQL = "INSERT INTO " & strTable & "(calDate) " & _
"VALUES(#" & Format(dtmDate, "mm/dd/yyyy") & "#)"
cmd.CommandText = strSQL
cmd.Execute
End If
Next varDay
Next dtmDate
End If

End Function

Or with DAO:

Public Function MakeCalendar_DAO(strTable As String, _
dtmStart As Date, _
dtmEnd As Date, _
ParamArray varDays() As Variant)

' Accepts: Name of calendar table to be created: String.
' Start date for calendar: DateTime.
' End date for calendar: DateTime.
' Days of week to be included in calendar
' as value list, e,g 2,3,4,5,6 for Mon-Fri
' (use 0 to include all days of week)

Dim dbs As DAO.Database, tdf As DAO.TableDef
Dim strSQL As String
Dim dtmDate As Date
Dim varDay As Variant
Dim lngDayNum As Long

Set dbs = CurrentDb

' does table exist? If so get user confirmation to delete it
On Error Resume Next
Set tdf = dbs.TableDefs(strTable)
If Err = 0 Then
If MsgBox("Replace existing table: " & _
strTable & "?", vbYesNo + vbQuestion, _
"Delete Table?") = vbYes Then
strSQL = "DROP TABLE " & strTable
dbs.Execute strSQL
Else
Exit Function
End If
End If
On Error GoTo 0

' create new table
strSQL = "CREATE TABLE " & strTable & _
"(calDate DATETIME, " & _
"CONSTRAINT PrimaryKey PRIMARY KEY (calDate))"
dbs.Execute strSQL

' refresh database window
Application.RefreshDatabaseWindow

If varDays(0) = 0 Then
' fill table with all dates
For dtmDate = dtmStart To dtmEnd
lngDayNum = lngDayNum + 1
strSQL = "INSERT INTO " & strTable & "(calDate) " & _
"VALUES(#" & Format(dtmDate, "mm/dd/yyyy") & "#)"

dbs.Execute strSQL
Next dtmDate
Else
' fill table with dates of selected days of week only
For dtmDate = dtmStart To dtmEnd
For Each varDay In varDays()
If Weekday(dtmDate) = varDay Then
lngDayNum = lngDayNum + 1
strSQL = "INSERT INTO " & strTable & "(calDate) " & _
"VALUES(#" & Format(dtmDate, "mm/dd/yyyy") & "#)"
dbs.Execute strSQL
End If
Next varDay
Next dtmDate
End If

End Function

To make a calendar over the next 10 years, of weekdays only for instance,
you'd call the function like this:

MakeCalendar "CalendarWeekDays", #2010-01-01#,#2020-12-31#,2,3,4,5,6

If you were to add an IsHoliday column to this you'd then simply count the
rows between two dates were the IsHoliday column is FALSE, e.g.

PARAMETERS [Enter start date:] DATETIME,
[Enter end date:] DATETIME;
SELECT COUNT(*) AS WorkDays
FROM CalendarWeekDays
WHERE calDate BETWEEN [Enter start date:]
AND [Enter end date:] AND NOT IsHoliday;

Ken Sheridan
Stafford, England

(PeteCresswell) wrote:
>Business days between two dates has got to involve a tblHoliday.
>
>Right now I've got such a table - mostly populated with bank
>holidays and I compute biz days between two dates by iterating
>through the days one-by-one: if it's a Saturday or Sunday it's
>not a biz day.... otherwise I do a table lookup - no hit, then
>it's a biz day.
>
>But this is *really* slow for processing a record set of, say,
>2000 records.
>
>I'm thinking a faster approach would involve re-creating a table
>of business days from some begin date to some end date each time
>tblHolidays is updated and then doing some SQL magic to bang a
>recordset up against that table and somehow bulk-calculate, for
>instance, the number of business days between a bond's maturity
>date and today's date... or whether or not a bond matures within
>60 business days.
>
>Has anybody come up with such an approach?

--
Message posted via AccessMonster.com
http://www.accessmonster.com/Uwe/Forums.aspx/access/201005/1

From: KenSheridan via AccessMonster.com on
Purely for consistency with other code. I don't do it in this function but
frequently, by starting the week on Monday, I can then test for a weekday
being <6 or a weekend >5. The custom constants, with values 6 and 7 are then
used for the weekend days as the built in ones are 7 and 1 for Saturday and
Sunday of course.

Ken Sheridan
Stafford, England

(PeteCresswell) wrote:
>Per KenSheridan via AccessMonster.com:
>> I use the following function
>
>That's exactly what I was looking for.
>
>With your permission, I will adapt it to my needs.
>
>One question: Why are you using vbMonday with the Weekday() calls
>and then using conSATURDAY and conSUNDAY instead of dropping the
>vbMonday and just using vbSaturday/vbSunday?
>
>Maybe I'll have it figured out by the time you read this... but
>maybe not...

--
Message posted via AccessMonster.com
http://www.accessmonster.com/Uwe/Forums.aspx/access/201005/1