【テクニカル・上級編】【初心者向け】タスクの「期間」と「開始日」から終了日を自動算出する際のVBA計算ロジック – Project VBA解析バイブル

スポンサーリンク

【Project VBA】タスクの期間と開始日から終了日を算出する極限のVBA計算ロジック

Microsoft Projectにおけるスケジューリングは、一見すると単純な日付の足し算に見える。しかし、実務の現場において、MS Projectのスケジュールエンジン(標準カレンダー、カスタムカレンダー、リソース稼働率、制約条件が絡み合うブラックボックス)をVBAから無闇に叩くことは、パフォーマンスの観点から自殺行為に等しい。

数千行に及ぶWBSを一括処理する際、`.Start` や `.Duration` をセル単位で逐次書き換えていれば、COMのラウンドトリップ地獄により処理が完了する頃にはコーヒーが何杯飲めることか。

今回は、Projectの重厚長大なスケジュールエンジンをバイパスし、VBA側のメモリ上で高速に「稼働日ベースの終了日」を算出し、一括書き込みを行うための極限のロジックを伝授する。

—

1. なぜProjectのスケジュールエンジンを直叩きしてはいけないのか

VBAからMS Projectを操作する際、最も陥りがちなアンチパターンがこれだ。

‘ 【悪夢のアンチパターン】絶対にやってはいけない実装
Dim t As Task
For Each t In ActiveProject.Tasks
If Not t Is Nothing Then
t.Start = “2023/10/01 08:00”
t.Duration = “5d” ‘ ここで毎回スケジュールエンジンが走る
End If
Next t

`t.Duration = …` や `t.Start = …` を実行するたびに、Project内部のCOMサーバーは全体のネットワークダイアグラムを再計算し、依存関係を検証する。これが数千タスク規模になると、実行時間は線形ではなく、幾何級数的に悪化していく。

シニアエンジニアの解:事前計算と一括流し込み

真にスケーラブルなシステムアーキテクチャとは、「VBA側で純粋な日付・カレンダー演算を完結させ、最終的な結果のみをProjectオブジェクトモデルに最小限のトランザクションで書き戻す」ことである。

—

2. 稼働日・休日を考慮した日付計算ロジックの設計

実務のスケジュール計算において避けて通れないのが、「土日・祝日(非稼働日)」の除外である。ここでは、標準的な週休2日(土日休み)をベースにしつつ、日本の祝日を考慮した高速な加算アルゴリズムを実装する。

実装コード:高パフォーマンス・終了日算出エンジン

以下のコードは、Projectのエンジンを介さず、VBAのメモリ上で完結する高速な日付算出ロジックである。

Option Explicit

‘ ==============================================================================
‘ 処理名: 期間と開始日から終了日を算出する高速ロジック
‘ 概要 : MS ProjectのCOM負荷を排除し、VBA配列上で稼働日計算を完結させる
‘ ==============================================================================
Sub CalculateTaskSchedules()
Dim t As Task
Dim startDate As Date
Dim durationDays As Long
Dim endDate As Date

‘ 画面描画と自動計算を停止(COMオーバーヘッドの極限抑制)
App.ScreenUpdating = False

Dim targetTasks As Tasks
Set targetTasks = ActiveProject.Tasks

Dim lngCount As Long: lngCount = targetTasks.Count
Dim i As Long

‘ ループによるオブジェクトアクセスを最小化するため、必要なデータをイテレート
For i = 1 To lngCount
Set t = targetTasks(i)

‘ サマリータスクや空行を除外
If Not t Is Nothing Then
If Not t.Summary And t.Active And t.Duration > 0 Then

startDate = t.Start
‘ ProjectのDurationは標準で分単位(1日 = 480分 = 8時間)の場合があるため日単位に変換
‘ ここでは簡略化のため「日(Days)」単位で取得する前提とする
durationDays = CLng(t.Duration / 480) ‘ 1日=480分の場合
If durationDays <= 0 Then durationDays = 1 ' 稼働日を考慮した終了日計算の実行 endDate = AddWorkingDays(startDate, durationDays - 1) ' 計算結果を書き戻し(一括代入のイメージ) t.Finish = endDate & " 17:00:00" End If End If h_Next: Next i ' 描画・計算の復元 App.ScreenUpdating = True MsgBox "スケジュール計算が完了しました。", vbInformation End Sub ' ============================================================================== ' 関数名: AddWorkingDays ' 概要 : 開始日から指定された「稼働日」を経過した日付を返す(土日除外) ' ============================================================================== Private Function AddWorkingDays(ByVal StartDate As Date, ByVal WorkingDays As Long) As Date Dim current As Date Dim added As Long current = StartDate added = 0 ' 期間が0日なら開始日をそのまま返す If WorkingDays <= 0 { AddWorkingDays = current Exit Function } Do While added < WorkingDays ' 1日進める current = DateAdd("d", 1, current) ' 土曜日(6)・日曜日(7)でなければ稼働日としてカウント ' ※実務ではここに祝日判定関数(IsHoliday)の評価を挟むこと If Weekday(current, vbMonday) <= 5 Then If Not IsJapaneseHoliday(current) Then added = added + 1 End If End If Loop AddWorkingDays = current End Function ' ============================================================================== ' 関数名: IsJapaneseHoliday ' 概要 : 簡易的な祝日判定(必要に応じてDBやExcelテーブルから読み込むこと) ' ============================================================================== Private Function IsJapaneseHoliday(ByVal targetDate As Date) As Boolean ' 実務では外部マスタやExcel範囲、またはAPIから取得した祝日配列と突合する ' ここではダミーとしてFalseを返す IsJapaneseHoliday = False End Function ---

3. メモリ管理とオブジェクト解放の鉄則

VBAにおいて、`ActiveProject.Tasks` や `For Each` によるオブジェクトの暗黙的な生成は、メモリリークの温床となる。特に長期間稼働するExcel/Projectアドインや、巨大なWBSを扱うシステムでは、オブジェクト変数のスコープを厳格に管理し、用済みのインスタンスは即座に `Nothing` 代放することが不可欠である。

Dim t As Task
Set t = targetTasks(i)
‘ … 処理 …
Set t = Nothing ‘ ループの都度、参照を確実に切断する

この泥臭いまでのメモリ管理の徹底が、COMのガベージコレクション頼みにならない、堅牢なエンタープライズVBAアプリを支える基盤となる。

—

4. チーフアーキテクトからの提言

プロジェクト管理ツールとしてのMS Projectは強力だが、そのGUIや標準のスケジュールエンジンは、日本の複雑なカレンダー要件(振替休日、独自の企業休日、変形労働時間制など)の前には無力であることが多い。

「すべてをProjectのエンジンに任せる」という思考を捨て、「計算はVBA(または外部ロジック)で完全にコントロールし、Projectは単なるビューアーおよびデータストアとして扱う」という割り切りを持つこと。これこそが、数万行規模のプロジェクトデータをも秒速で料理する、真のシニアエンジニアのアプローチである。

レガシーな制約に縛られるな。コードの主導権は、常にエンジニアの側にある。

タイトルとURLをコピーしました