Excel - VBA

특정일이 공휴일이면 이전/이후의 근무일 찾기 FindWorkDay

EGTools 2023. 7. 27. 00:13

어느 날짜가 공휴일이면 이후 첫번째 근무일을 찾는 경우가 있습니다.

이를 함수로 구현해 봅니다.

 

함수의 인수로는 지정일자,  주말휴일,  공휴일 목록, 검색방향 4개를 사용합니다.

주말 휴일은 NetWorkDays.Intl함수에서와 같은 주말휴일 옵션을 사용합니다.

Public Function FindWorkDay(StartDate As Date, _
                            Optional Weekend As Variant = 1, _
                            Optional Holidays As Variant, _
                            Optional Direction As Integer = 1)

 

내부적으로 사용할 변수를 지정합니다.

    Dim sWeekend As String     '// 요일별 근무일/휴무일 지정
    Dim vHolidays As Variant   '// 휴일 목록
    Dim d As Date              '// 점검할 날짜
    Dim iWeekday As Integer    '// 점검할 날짜의 요일

 

함수에 입력된 인수들을 점검합니다.

휴일목록이 입력되지 않으면 Empty로 기본값을 설정하고, 범위를 지정했으면 배열로 전환합니다.

범위나 배열이 아닌 경우 강제로 배열로 만들어 이후 처리를 배열로만 처리하도록 조정합니다.

    '// 인수 점검
    If IsMissing(Holidays) Then Holidays = Empty
    If TypeName(Holidays) = "Ragne" Then vHolidays = Holidays.Value2 Else vHolidays = Holidays
    If Not isArray(vHolidays) Then ReDim vHolidays(0): vHolidays(0) = Holidays

 

주말지정이 근무일을 표시하는 "0"과 휴일을 표시하는 "1"로만 구성된 "0000011"형태가 아니면 오류 처리

    '// 주말을 "0000011"형태의 문자열 인수 형태로 통일
    If Application.isNumber(Weekend) Then
        Select Case CLng(Weekend)       '// 1비근무일, 0근무일 -> NetWorkDay.Intl 함수의 옵션과 같음
        Case 1:  sWeekend = "0000011"   '//토,일
        Case 2:  sWeekend = "1000001"   '//일,월
        Case 3:  sWeekend = "1100000"   '//월,화
        Case 4:  sWeekend = "0110000"   '//화,수
        Case 5:  sWeekend = "0011000"   '//수,목
        Case 6:  sWeekend = "0001100"   '//목,금
        Case 7:  sWeekend = "0000110"   '//금,토
        Case 11: sWeekend = "0000001"   '//일
        Case 12: sWeekend = "1000000"   '//월
        Case 13: sWeekend = "0100000"   '//화
        Case 14: sWeekend = "0010000"   '//수
        Case 15: sWeekend = "0001000"   '//목
        Case 16: sWeekend = "0000100"   '//금
        Case 17: sWeekend = "0000010"   '//토
        Case Else: FindWorkDay = CVErr(xlErrValue): Exit Function
        End Select
    Else
        '// 문자열로 강제 변경하고 변경이 안되는 것은 오류 처리
        On Error Resume Next
        sWeekend = CStr(Weekend)
        If Err.Number <> 0 Then FindWorkDay = CVErr(xlErrValue): Exit Function
        On Error GoTo 0
        '// "0"과 "1"로만 구성되어 있지 않으면 오류처리
        If Not IsNumeric(sWeekend) Then FindWorkDay = CVErr(xlErrValue): Exit Function
        If Replace(Replace(sWeekend, "0", ""), "1", "") <> "" Then FindWorkDay = CVErr(xlErrValue): Exit Function
    End If
    If sWeekend = "1111111" Or Len(sWeekend) <> 7 Then FindWorkDay = CVErr(xlErrValue): Exit Function

 

진행방향에 따라 날짜를 하루씩 변경해가며서 근무일이면 날짜를 출력하고, 근무일을 못찾고 1년을 경과하면 오류 처리합니다. (ArrayExist 함수는 여기 참조)

    For d = StartDate To StartDate + IIf(Direction > 0, 365, -365) Step IIf(Direction > 0, 1, -1)
        iWeekday = Weekday(d, vbMonday)
        If Not ArrayExists(Holidays, d) Then
            If Mid(sWeekend, iWeekday, 1) = "0" Then
                FindWorkDay = d
                Exit Function
            End If
        End If
    Next d
    
     FindWorkDay = CVErr(xlErrNA)
     
 End Function

 

진행방향을 명확히 지정하는 NextWorkDay와 PrevWorkDay라는 Wrapper 함수도 만들어 줍니다.

Public Function NextWorkDay(StartDate As Date, Optional Weekend As Variant = 1, Optional Holidays As Variant)
    NextWorkDay = FindWorkDay(StartDate, Weekend, Holidays, 1)
End Function

Public Function PrevWorkDay(StartDate As Date, Optional Weekend As Variant = 1, Optional Holidays As Variant)
    PrevWorkDay = FindWorkDay(StartDate, Weekend, Holidays, -1)
End Function