xlf EandA series


VBA functions :: Days Count, Actual, European and NASD methods


This is a code development module, providing some code logic ideas, presented here as an exercise.


0. Preliminary


The EXERCISE - implement the following



Function procedure


Write three VBA Function procedures to count the number of days between two days. Use day count methods of Actual, Days 360 European and US (NASD)
Syntax:

1. xlfDays(end_date, start_date). This is similar to the WS DAYS function

2. xlfDays360euro(start_date, end_date). This is similar to the WS DAYS360 function using the European method

3. xlfDays360NASD(start_date, end_date). This is similar to the WS DAYS360 function using the US NASD method

Each xlfDays... function returns the number of days (nights) between the start_date and end_date. The order of the parameters matches the Excel WS functions.

Each function has the following arguments:

  • end_date Required. An ISO 8601 date of the form yyyymmdd as a number
  • start_date Required. An ISO 8601 date of the form yyyymmdd as a number

Your code statements should be limited mostly to mathematical operators, logic statements, and code control procedures. Do not use any Excel or VBA Date and Time functions

 

1. The code


The following material is based on coding techniques covered presented 90045.

1.1 A version of Days Actual function


This code is based on xlfDateToSerialNumber with the addition of nested loops.



Code 1: the xlfDays function. An interpretation of the set task
Function xlfDays(end_date As Long, start_date As Long) As Long
' Syntax: xlfDays(end_date, start_date)
' Description: Returns the number of days between two dates (equivalent to WS DAYS function)
' Arguments: end_date required - entered as yyyymmdd, a number. ISO 8601 (basic format) _
'    19000101 to 99991231. "Start_date and End_date are the two dates between which you _
'    want to know the number of days."
'            start_date required - entered as yyyymmdd, a number. ISO 8601 (basic format) _
'    19000101 to 99991231. "Start_date and End_date are the two dates between which you _
'    want to know the number of days."
' Return value is type long. xlfDays includes the 29 February 1900 leap year error.
' If date arguments are invalid, xlfDays returns the Error Value -1
' Reference: https://support.microsoft.com/en-us/office/days-function-57740535-d549-4395-8728-0f07bff0b9df
' 14 October 2022, excelatfinance.com
' =================
Dim Arg(1 To 2) As Long
Dim Y(1 To 2) As Integer, M(1 To 2) As Integer, D(1 To 2) As Integer
Dim Days(1 To 2) As Long, DaysInMth(1 To 2) As Integer, MthDays(1 To 2) As Integer
Dim i As Integer, j As Integer
Dim Epoch As Long: Epoch = 19000101

Arg(1) = end_date: Arg(2) = start_date

' Dte integrity ===
For i = 1 To 2
    If Len(CStr(Arg(i))) <> 8 Then GoTo ErrHandler ' CStr conversion resolves 4 digit error with 99991231
        Y(i) = Left(Arg(i), 4): M(i) = Mid(Arg(i), 5, 2): D(i) = Right(Arg(i), 2)
    If Not (Y(i) >= 1900 And Y(i) <= 9999) Or _
       Not (M(i) >= 1 And M(i) <= 12) Or _
       Not (D(i) >= 1 And D(i) <= 31) Then GoTo ErrHandler
Next i
' Years ===========
    For i = 1 To 2
        For j = Left(Epoch, 4) To Y(i) - 1
        If Not (j Mod 4 = 0 And (j Mod 100 <> 0 Or j Mod 400 = 0)) Then
            Days(i) = Days(i) + 365
        Else
            Days(i) = Days(i) + 366
        End If
        Next j
    Next i
' Months ==========
    'If M > 1 Then
    For i = 1 To 2
        If M(i) > 1 Then
            For j = Mid(Epoch, 5, 2) To M(i) - 1
                Select Case j
                Case 1, 3, 5, 7, 8, 10, 12
                    DaysInMth(i) = 31
                    MthDays(i) = MthDays(i) + DaysInMth(i)
                Case 2
                    If Not (Y(i) Mod 4 = 0 And (Y(i) Mod 100 <> 0 Or Y(i) Mod 400 = 0)) Then
                        DaysInMth(i) = 28
                        MthDays(i) = MthDays(i) + DaysInMth(i)
                    Else
                        DaysInMth(i) = 29
                        MthDays(i) = MthDays(i) + DaysInMth(i)
                    End If
                Case 4, 6, 9, 11
                    DaysInMth(i) = 30
                    MthDays(i) = MthDays(i) + DaysInMth(i)
                End Select
            Next j
        End If
    Next i
    'End If
    For i = 1 To 2
        Days(i) = Days(i) + MthDays(i)
    Next i
' Days ============
    For i = 1 To 2
        Days(i) = Days(i) + D(i)
    Next i
    ' Include 1900 error
    For i = 1 To 2
        If Arg(i) > 19000228 Then Days(i) = Days(i) + 1
    Next i
' Return value ====
    xlfDays = Days(1) - Days(2)
Exit Function

ErrHandler:
    xlfDays = -1
End Function


1.2 A version of the Days 360 EURO function


Includes a Variant subtype error handler.



Code 2: the xlfDays360euro function. An interpretation of the set task
Function xlfDays360euro(start_date As Variant, end_date As Variant) As Variant
' Syntax: xlfDays360euro(start_date, end_date)
' Description: Returns the number of days between two dates (equivalent to XL.WS DAYS360 European function)
' Arguments: end_date required - entered as yyyymmdd, a number or text. ISO 8601 (basic format) _
'    19000101 to 99991231. "Start_date and End_date are the two dates between which you _
'    want to know the number of days."
'            start_date required - entered as yyyymmdd, a number or text. ISO 8601 (basic format) _
'    19000101 to 99991231. "Start_date and End_date are the two dates between which you _
'    want to know the number of days."
' Return value is type long.
' If date arguments are invalid, xlfDays360euro returns the Error #NUM!
' Date: 14 October 2022, excelatfinance.com
' =================
Dim Arg(1 To 2) As Long
Dim Y(1 To 2) As Integer, M(1 To 2) As Integer, D(1 To 2) As Integer
Dim tmp As Long, i As Integer

On Error GoTo ErrHandler2
' Determine Variant subtype
If TypeName(start_date) = "Long" Or _
    TypeName(start_date) = "String" Or _
    TypeName(start_date) = "Range" Then Arg(1) = start_date
If TypeName(end_date) = "Long" Or _
    TypeName(end_date) = "String" Or _
    TypeName(end_date) = "Range" Then Arg(2) = end_date

' Dte integrity ===
For i = 1 To 2
    If Len(CStr(Arg(i))) <> 8 Then GoTo ErrHandler1 ' CStr conversion resolves 4 digit error with 99991231
        Y(i) = Left(Arg(i), 4): M(i) = Mid(Arg(i), 5, 2): D(i) = Right(Arg(i), 2)
    If Not (Y(i) >= 1900 And Y(i)  <= 9999) Or _
       Not (M(i) >= 1 And M(i)  <= 12) Or _
       Not (D(i) >= 1 And D(i)  <= 31) Then GoTo ErrHandler1
Next i

' Euro ============
With Application.WorksheetFunction
    D(1) = .Min(D(1), 30)
    D(2) = .Min(D(2), 30)
End With

' Return value ====
    tmp = (Y(2) - Y(1)) * 360 + (M(2) - M(1)) * 30 + (D(2) - D(1))
    xlfDays360euro = tmp    ' return as subtype Long
Exit Function

ErrHandler1:
    xlfDays360euro = VBA.CVErr(xlErrNum)    ' error number 2036
Exit Function
ErrHandler2:
    xlfDays360euro = VBA.CVErr(xlValue)     ' error number 2015
End Function


1.3 A version of the Days 360 NASD function


Another version of code 3 NASD method is available at Emulate the DAYS360 function.



Code 3: the xlfDays360NASD function. An interpretation of the set task
Function xlfDays360NASD(start_date As Long, end_date As Long) As Variant
' Syntax: xlfDays360NASD(start_date, end_date)
' Description: Returns the number of days between two dates (equivalent to XL.WS DAYS360 NASD function)
' Arguments: end_date required - entered as yyyymmdd, a number or text. ISO 8601 (basic format) _
'    19000101 to 99991231. "Start_date and End_date are the two dates between which you _
'    want to know the number of days."
'            start_date required - entered as yyyymmdd, a number or text. ISO 8601 (basic format) _
'    19000101 to 99991231. "Start_date and End_date are the two dates between which you _
'    want to know the number of days."
' Return value is type long.
' If date arguments are invalid, xlfDays360NASD returns the Error #NUM!
' Date: 14 October 2022, excelatfinance.com
' =================
Dim Arg(1 To 2) As Long
Dim Y(1 To 2) As Integer, M(1 To 2) As Integer, D(1 To 2) As Integer
Dim tmp As Long, i As Integer

Arg(1) = start_date: Arg(2) = end_date

' Dte integrity ===
For i = 1 To 2
    If Len(CStr(Arg(i))) <> 8 Then GoTo ErrHandler ' CStr conversion resolves 4 digit error with 99991231
        Y(i) = Left(Arg(i), 4): M(i) = Mid(Arg(i), 5, 2): D(i) = Right(Arg(i), 2)
    If Not (Y(i) >= 1900 And Y(i) <= 9999) Or _
       Not (M(i) >= 1 And M(i) <= 12) Or _
       Not (D(i) >= 1 And D(i) <= 31) Then GoTo ErrHandler
Next i


' NASD ============
    If D(1) = 31 Then D(1) = 30
    If D(2) = 31 And D(1) >= 30 Then D(2) = 30
    If D(2) = 31 And D(1) < 30 Then D(2) = 1: M(2) = M(2) + 1

' Return value ====
    tmp = (Y(2) - Y(1)) * 360 + (M(2) - M(1)) * 30 + (D(2) - D(1))
    xlfDays360NASD = tmp    ' return as subtype Long
Exit Function

ErrHandler:
    xlfDays360NASD = VBA.CVErr(xlErrNum)     ' error number 2015
End Function



1.2 Procedure testing

The testing platform is worksheet based. Performance is slow because of the large number of formulas, and some including extensive loops.


References