概要
「発行日から5営業日以内に支払う」という契約条件をシステムに落とすとき、最初に突き当たるのが「営業日をどう数えるか」です。土日を除くだけなら DayOfWeek の判定で済みますが、実際の業務では祝日、年末年始の会社休業日、さらに「締日が休日なら前倒しか後ろ倒しか」という会社ごとのルールが絡んできます。Java 8 以降の LocalDate を使えば、この計算は外部ライブラリなしで実装できます。この記事では、営業日の加算メソッドを軸に、祝日リストとの突き合わせ、連休をまたぐ境界ケースの確認方法、締日スライドの考え方といった、実務で繰り返し出てくるパターンを再利用しやすい形に整理します。
使いどころ
「発行日から5営業日後」と定められた請求書の支払期日を、受発注システムで自動算出する
月次の締め処理バッチを毎月最終営業日の夜間に起動するため、スケジューラに渡す実行日を求める
EC サイトの注文確認画面で、土日祝を除いた出荷予定日・納品予定日を表示する
コード例
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.util.List;
import java.util.Set;
public class BusinessDayCalculator {
private static final Set<DayOfWeek> WEEKENDS =
Set.of(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY);
public static LocalDate addBusinessDays(
LocalDate start, int days, List<LocalDate> holidays) {
if (days < 0) {
throw new IllegalArgumentException(
"days は 0 以上を指定してください: " + days);
}
var current = start;
var remaining = days;
while (remaining > 0) {
current = current.plusDays(1);
if (!WEEKENDS.contains(current.getDayOfWeek())
&& !holidays.contains(current)) {
remaining--;
}
}
return current;
}
public static void main(String[] args) {
// 2025年の祝日(1月と GW 前後を抜粋)
var holidays = List.of(
LocalDate.of(2025, 1, 1), // 元日
LocalDate.of(2025, 1, 13), // 成人の日
LocalDate.of(2025, 4, 29), // 昭和の日
LocalDate.of(2025, 5, 3), // 憲法記念日
LocalDate.of(2025, 5, 4), // みどりの日
LocalDate.of(2025, 5, 5), // こどもの日
LocalDate.of(2025, 5, 6) // 振替休日
);
// 通常ケース: 金曜起点。三連休(土日 + 成人の日)をまたぐ
var friday = LocalDate.of(2025, 1, 10);
System.out.println(friday + " の 3 営業日後: "
+ addBusinessDays(friday, 3, holidays));
// → 2025-01-16(1/11〜13 を除外)
// 境界ケース: GW 直前起点。連休を丸ごとスキップ
var beforeGw = LocalDate.of(2025, 4, 30);
System.out.println(beforeGw + " の 3 営業日後: "
+ addBusinessDays(beforeGw, 3, holidays));
// → 2025-05-07(5/3〜6 の連休と土日を除外)
// 0 日加算は起点日をそのまま返す(仕様として明示しておく)
System.out.println(friday + " の 0 営業日後: "
+ addBusinessDays(friday, 0, holidays));
// → 2025-01-10
}
}Version Coverage
Stream.iterate で営業日の抽出をストリームベースに記述できる。var で型宣言が簡潔になる。
// Java 17: Stream.iterate で営業日をフィルタ抽出
return Stream.iterate(from.plusDays(1), d -> d.plusDays(1))
.filter(d -> isBusinessDay(d, holidays))
.limit(n)
.reduce((first, last) -> last)
.orElseThrow();Library Comparison
注意点
祝日リストは年ごとに更新が必要。振替休日の判定ルールも考慮すること。
会社独自の休業日がある場合は、祝日リストに追加する仕組みが必要。
年末年始やお盆休みは祝日ではないため、別途ルールとして管理する。
「締日が土日祝に当たる場合は前営業日か翌営業日か」はビジネス側との確認が必要。年度末や大型連休明けに仕様の認識ズレが発覚すると手戻りが大きい。
FAQ
契約や社内規程によって異なります。実装より先に決めるべき仕様で、この記事のコードは翌日起算(当日を含めない)です。テストケースに起算日の想定を明記しておくと認識ズレを防げます。
内閣府が公開している祝日 CSV を年次で取り込む方法が確実です。小規模なら定数テーブルでも回りますが、更新の担当と時期を決めておかないと翌年の祝日が漏れます。
祝日と土日が連続する年末年始とゴールデンウィークが最も崩れやすい境界です。振替休日を含む週をまたぐ加算のケースを必ず入れてください。