Modified Following方式でTenorに対応する日付を取得する関数

```RequestTenor.bas
Option Explicit


Public Function RequestTenor(ByRef TARGETDATE As Date, _
                    ByRef TENOR As String, _
                    ByRef PUBLICHOLIDAY As Range, _
                    ByVal CONVENTION As String, _
                    Optional ByVal Holidayshift = 0, _
                    Optional ByVal ReverseFLG = True) As Date

'引数---------------------------------------------------------------------------------------------------------------
'TARGETDATE                  : DateAdd(単位,加算時間,日時)の第3引数に渡す。セル範囲が1つでない場合エラー。
'                            : weekday関数で曜日を取得する。
                             '0：vbUseSystemDayOfWeek(PCのOSのシステム時間)
                             '1：vbSunday (日曜日)
                             '2：vbMonday (月曜日)
                             '3：vbTuesday (火曜日)
                             '4：vbWednesday (水曜日)
                             '5：vbThursday (木曜日)
                             '6：vbFriday (金曜日)
                             '7：vbSaturday (土曜日)
'TENOR                       : テナー一覧をConstClassで辞書として定義。テナーに対応する値はDateAdd
'                              DateAdd関数の第1・2引数に渡す。セル範囲が1つでない場合エラー。
'PUBLICHOLIDAY               : セル範囲に指定した全ての日付を重複削除したうえで辞書に格納。
'                               キーにセル範囲の日付､値にTRUEを格納｡
'CONVENTION                  : Business Day Conventionの設定を行う。
'                              MF : Modified Following
'                              F  : Following
'Holidayshift                : 再帰呼び出し回数のカウンタ
'ByValReverseFLG             : Modified Followingで月末と判定された場合に過去方向に平日を探すように挙動

'戻り値--------------------------------------------------------------------------------------------------------------
'PUBLICHOLIDAYの辞書の格納値がTRUE､もしくはweekday(DateAdd(,TARGETDATE,TENOR))
    
Dim CntList As Object
Dim AddedDate As Date
Dim cell As Variant
Dim dicPubHol As Object

  
'定数クラスをインスタンス化
Set CntList = New CstClass

'テナーの分シフトさせる
AddedDate = DateAdd(CntList.TENORFORMAT.Item(TENOR), CntList.TENORNUM.Item(TENOR), TARGETDATE)

'祝日であることを表すフラグを設定する。
Set dicPubHol = CreateObject("Scripting.Dictionary")
For Each cell In PUBLICHOLIDAY.Cells
    If dicPubHol.Exists(cell.Value) = False Then
        dicPubHol.Add Key:=cell.Value, Item:=True
    End If
Next
    
'平日だった場合はそのままテナーの分シフトさせた値を返す。
Select Case Weekday(AddedDate, vbSunday)
Case Is = vbMonday, vbTuesday, vbWednesday, vbThursday, vbFriday
    If dicPubHol.Item(AddedDate) = True Then
        GoTo RECURSIVE
    Else
        RequestTenor = AddedDate
    End If
    
'休日・祝日だった場合は翌日にシフトさせたうえで、再帰的に関数呼び出し
Case Is = vbSunday, vbSaturday
RECURSIVE:
                  
    '再帰関数の呼び出し回数をカウント
    If Holidayshift = 0 Then
        Holidayshift = 1
    ElseIf Holidayshift > 0 Then
        Holidayshift = Holidayshift + 1
    End If
                  
    If CONVENTION = "F" Then
        TARGETDATE = DateAdd("d", 1, TARGETDATE)
        
    ElseIf CONVENTION = "MF" Then
        '翌日が月末でない場合
        If ReverseFLG = False And Month(TARGETDATE) = Month(DateAdd("d", 1, TARGETDATE)) Then
            TARGETDATE = DateAdd("d", 1, TARGETDATE)
        
        '翌日が月末である場合
        ElseIf ReverseFLG = False And Month(TARGETDATE) <> Month(DateAdd("d", 1, TARGETDATE)) Then
            ReverseFLG = True
            TARGETDATE = DateAdd("d", -1 * Holidayshift, TARGETDATE)
            
        '月末であると判定され、月初方向に巻き戻し場合
        ElseIf ReverseFLG = True Then
            TARGETDATE = DateAdd("d", -1, TARGETDATE)
        End If
    End If
            
    dicPubHol.RemoveAll '祝日定義のディクショナリーを初期化
    RequestTenor = RequestTenor(TARGETDATE, TENOR, PUBLICHOLIDAY, CONVENTION, Holidayshift)
End Select
End Function
```


```CstClass.cls
Option Explicit

Private TenorNumber As Object
Private TenorUnit As Object

Private Sub Class_Initialize()
  Call SetTenorNum
  Call SetTenorFormat
End Sub

Public Property Get TENORNUM() As Object
  Set TENORNUM = TenorNumber
End Property

Public Property Get TENORFORMAT() As Object
  Set TENORFORMAT = TenorUnit
End Property

Private Sub SetTenorNum()
  Set TenorNumber = CreateObject("Scripting.Dictionary")
  TenorNumber.Add Key:="O/N", Item:="1"
  TenorNumber.Add Key:="T/N", Item:="2"
  TenorNumber.Add Key:="SPOT", Item:="3"
  TenorNumber.Add Key:="S/N", Item:="4"
  TenorNumber.Add Key:="1D", Item:="1"
  TenorNumber.Add Key:="2D", Item:="2"
  TenorNumber.Add Key:="3D", Item:="3"
  TenorNumber.Add Key:="1W", Item:="7"
  TenorNumber.Add Key:="2W", Item:="14"
  TenorNumber.Add Key:="3W", Item:="21"
  TenorNumber.Add Key:="1M", Item:="1"
  TenorNumber.Add Key:="2M", Item:="2"
  TenorNumber.Add Key:="3M", Item:="3"
  TenorNumber.Add Key:="4M", Item:="4"
  TenorNumber.Add Key:="5M", Item:="5"
  TenorNumber.Add Key:="6M", Item:="6"
  TenorNumber.Add Key:="7M", Item:="7"
  TenorNumber.Add Key:="8M", Item:="8"
  TenorNumber.Add Key:="9M", Item:="9"
  TenorNumber.Add Key:="10M", Item:="10"
  TenorNumber.Add Key:="11M", Item:="11"
  TenorNumber.Add Key:="12M", Item:="12"
  TenorNumber.Add Key:="1Y", Item:="1"
  TenorNumber.Add Key:="15M", Item:="15"
  TenorNumber.Add Key:="18M", Item:="18"
  TenorNumber.Add Key:="2Y", Item:="2"
  TenorNumber.Add Key:="3Y", Item:="3"
  TenorNumber.Add Key:="4Y", Item:="4"
  TenorNumber.Add Key:="5Y", Item:="5"
  TenorNumber.Add Key:="6Y", Item:="6"
  TenorNumber.Add Key:="7Y", Item:="7"
  TenorNumber.Add Key:="8Y", Item:="8"
  TenorNumber.Add Key:="9Y", Item:="9"
  TenorNumber.Add Key:="10Y", Item:="10"
  TenorNumber.Add Key:="12Y", Item:="12"
  TenorNumber.Add Key:="15Y", Item:="15"
  TenorNumber.Add Key:="20Y", Item:="20"
  TenorNumber.Add Key:="25Y", Item:="25"
  TenorNumber.Add Key:="30Y", Item:="30"
End Sub


Private Sub SetTenorFormat()
  Set TenorUnit = CreateObject("Scripting.Dictionary")
  TenorUnit.Add Key:="O/N", Item:="d"
  TenorUnit.Add Key:="T/N", Item:="d"
  TenorUnit.Add Key:="SPOT", Item:="d"
  TenorUnit.Add Key:="S/N", Item:="d"
  TenorUnit.Add Key:="1D", Item:="d"
  TenorUnit.Add Key:="2D", Item:="d"
  TenorUnit.Add Key:="3D", Item:="d"
  TenorUnit.Add Key:="1W", Item:="d"
  TenorUnit.Add Key:="2W", Item:="d"
  TenorUnit.Add Key:="3W", Item:="d"
  TenorUnit.Add Key:="1M", Item:="m"
  TenorUnit.Add Key:="2M", Item:="m"
  TenorUnit.Add Key:="3M", Item:="m"
  TenorUnit.Add Key:="4M", Item:="m"
  TenorUnit.Add Key:="5M", Item:="m"
  TenorUnit.Add Key:="6M", Item:="m"
  TenorUnit.Add Key:="7M", Item:="m"
  TenorUnit.Add Key:="8M", Item:="m"
  TenorUnit.Add Key:="9M", Item:="m"
  TenorUnit.Add Key:="10M", Item:="m"
  TenorUnit.Add Key:="11M", Item:="m"
  TenorUnit.Add Key:="12M", Item:="m"
  TenorUnit.Add Key:="1Y", Item:="yyyy"
  TenorUnit.Add Key:="15M", Item:="m"
  TenorUnit.Add Key:="18M", Item:="m"
  TenorUnit.Add Key:="2Y", Item:="yyyy"
  TenorUnit.Add Key:="3Y", Item:="yyyy"
  TenorUnit.Add Key:="4Y", Item:="yyyy"
  TenorUnit.Add Key:="5Y", Item:="yyyy"
  TenorUnit.Add Key:="6Y", Item:="yyyy"
  TenorUnit.Add Key:="7Y", Item:="yyyy"
  TenorUnit.Add Key:="8Y", Item:="yyyy"
  TenorUnit.Add Key:="9Y", Item:="yyyy"
  TenorUnit.Add Key:="10Y", Item:="yyyy"
  TenorUnit.Add Key:="12Y", Item:="yyyy"
  TenorUnit.Add Key:="15Y", Item:="yyyy"
  TenorUnit.Add Key:="20Y", Item:="yyyy"
  TenorUnit.Add Key:="25Y", Item:="yyyy"
  TenorUnit.Add Key:="30Y", Item:="yyyy"
End Sub

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274248/72da0e56-c649-c13c-5366-042138a64ef0.png)

```上記関数をコードで実行する方法.bas
Sub sample()
    Dim 基準日 As Range
    Set 基準日 = ThisWorkbook.Sheets("Sheet1").Range("C2")
    Dim テナー As Range
    Set テナー = ThisWorkbook.Sheets("Sheet1").Range("D1")
    Dim 祝日 As Range
    Set 祝日 = ThisWorkbook.Sheets("Sheet1").Range("A2:A51")

    MsgBox RequestTenor(aaaa.Value, bbbb.Value, cccc, "MF")
End Sub
```

機能要件は盛り込めた(つもり)ですが、この関数は性能面で計算に時間がかかるため、改善案をご教示いただける場合はコメントでお知らせ頂けますと幸いです。