VBA 指定された年月の日付シート作成
' 指定された年月の日付シートを作成する
'「main」シートのセル【C5】に年、セル【D5】に月をもとに
'その月の日付のシートを作成
'ユーザ誤って削除した日がある場合は、その日だけのシートを作成
' 作成したシートのセル【A3】に日付(yyyy/m/d)を入力する
' 並び順は「同月の既存シートの隣」に挿入して保つ
Public Sub CreateSheetsForMonth()
Const WS_DEF As String = "DEF"
Const WS_MAIN As String = "main"
Dim wsMain As Worksheet
Dim wsBase As Worksheet
Dim yearVal As Long
Dim monthVal As Long
Dim lastDay As Long
Dim d As Long
Dim currentDate As Date
Dim sheetName As String
Dim wasHidden As Boolean
Dim idxBefore As Long
Dim idxAfter As Long
On Error GoTo EH
Dim wb As Workbook
Set wb = ThisWorkbook
With wb
Set wsMain = .Worksheets(WS_MAIN)
Set wsBase = .Worksheets(WS_DEF)
End With
' === 年月の取得 ===
Dim yVal As Variant, mVal As Variant
With wsMain
yVal = .Range("C5").Value
mVal = .Range("D5").Value
End With
If Not (IsNumeric(yVal) And IsNumeric(mVal)) Then
MsgBox "年または月の入力が数値ではありません。", vbExclamation
Exit Sub
End If
yearVal = CLng(yVal)
monthVal = CLng(mVal)
If monthVal < 1 Or monthVal > 12 Or yearVal < 1900 Then
MsgBox "年または月の入力が不正です。", vbExclamation
Exit Sub
End If
lastDay = Day(DateSerial(yearVal, monthVal + 1, 0))
' === コピー元が非表示なら一時的に表示 ===
wasHidden = (wsBase.Visible <> xlSheetVisible)
If wasHidden Then wsBase.Visible = xlSheetVisible
Application.ScreenUpdating = False
' === シート作成&正しい位置へ挿入 ===
For d = 1 To lastDay
currentDate = DateSerial(yearVal, monthVal, d)
sheetName = BuildDateSheetName(currentDate)
If Not SheetExists(sheetName) Then
' 直前・直後の既存日付シート(同じ月)を探索
FindNeighborIndices yearVal, monthVal, d, idxBefore, idxAfter
If idxBefore > 0 Then
wsBase.Copy After:=wb.Worksheets(idxBefore)
ElseIf idxAfter > 0 Then
wsBase.Copy Before:=wb.Worksheets(idxAfter)
Else
' 同月のシートが1枚も無い場合は末尾へ
wsBase.Copy After:=wb.Worksheets(wb.Worksheets.Count)
End If
With wb.ActiveSheet
.Name = sheetName
.Range("A3").Value = Format$(currentDate, "yyyy/m/d")
End With
End If
Next d
' === 元の可視状態に戻す ===
If wasHidden Then wsBase.Visible = xlSheetHidden
Application.ScreenUpdating = True
wsMain.Activate
MsgBox "シート作成が完了しました。", vbInformation
Exit Sub
EH:
If wasHidden Then wsBase.Visible = xlSheetHidden
MsgBox "エラー: " & Err.Description, vbExclamation
Application.ScreenUpdating = True
End Sub
'=======================
' 補助関数
'=======================
Private Function SheetExists(ByVal sheetName As String) As Boolean
Dim ws As Worksheet
On Error Resume Next
Set ws = ThisWorkbook.Worksheets(sheetName)
SheetExists = Not (ws Is Nothing)
Set ws = Nothing
On Error GoTo 0
End Function
' シート名を "mm_dd(aaa)" で構成
Private Function BuildDateSheetName(ByVal theDate As Date) As String
BuildDateSheetName = Format$(theDate, "m_d") & "(" & Format$(theDate, "aaa") & ")"
End Function
' シート名が "mm_dd(...)" 形式かつ、指定の年月と一致するかを判定し、日を返す
Private Function ParseTargetMonthDay(ByVal sheetName As String, _
ByVal yearVal As Long, _
ByVal monthVal As Long, _
ByRef dayOut As Long) As Boolean
Dim mmPart As String
Dim ddPart As String
Dim mmVal As Long
Dim ddVal As Long
' 形式チェック:"##_##(*)"
If Not sheetName Like "##_##(*)" Then
ParseTargetMonthDay = False
Exit Function
End If
mmPart = Left$(sheetName, 2)
ddPart = Mid$(sheetName, 4, 2)
If Not IsNumeric(mmPart) Or Not IsNumeric(ddPart) Then
ParseTargetMonthDay = False
Exit Function
End If
mmVal = CLng(mmPart)
ddVal = CLng(ddPart)
If mmVal = monthVal Then
dayOut = ddVal
ParseTargetMonthDay = True
Else
ParseTargetMonthDay = False
End If
End Function
' 同じ年月で「ターゲット日 d」の直前(最も大きい<d)と直後(最も小さい>d)の
' 既存シートのインデックスを返す。なければ 0。
Private Sub FindNeighborIndices(ByVal yearVal As Long, _
ByVal monthVal As Long, _
ByVal d As Long, _
ByRef idxBefore As Long, _
ByRef idxAfter As Long)
Dim i As Long
Dim ws As Worksheet
Dim dayFound As Long
Dim bestBeforeDay As Long
Dim bestAfterDay As Long
idxBefore = 0
idxAfter = 0
bestBeforeDay = -1 ' 最大の(<d)
bestAfterDay = 9999 ' 最小の(>d)
For i = 1 To ThisWorkbook.Worksheets.Count
Set ws = ThisWorkbook.Worksheets(i)
If ParseTargetMonthDay(ws.Name, yearVal, monthVal, dayFound) Then
If dayFound < d Then
If dayFound > bestBeforeDay Then
bestBeforeDay = dayFound
idxBefore = i
End If
ElseIf dayFound > d Then
If dayFound < bestAfterDay Then
bestAfterDay = dayFound
idxAfter = i
End If
End If
End If
Next i
End Sub
コメント