Javaで日付や時刻を扱うプログラムを開発する際、特定の年が「うるう年」であるかどうかを確認しなければならない場面は多々あります。
特に、カレンダー機能の実装や期間の計算、金利計算といった精緻な処理が求められるシステムにおいて、2月29日の有無は非常に重要な要素となります。
Javaには古くから存在するカレンダー機能から、Java 8で導入されたモダンなDate-Time APIまで、うるう年を判定するための方法が複数用意されています。
本記事では、初心者の方でも理解しやすいように、Javaの標準機能を用いたスマートな判定方法から、プログラミングの基礎となるアルゴリズムを用いた実装まで詳しく紹介します。
2026年現在の開発現場で推奨されるベストプラクティスを学び、バグのない確実な日付処理を実装できるようになりましょう。
うるう年の定義と判定の基本ルール
Javaのコードを書く前に、まず「うるう年」がどのようなルールで定められているのかを再確認しておくことが大切です。
現在広く使われているグレゴリオ暦では、以下の3つの条件に基づいてうるう年かどうかが決定されます。
- 西暦年が4で割り切れる年はうるう年とする。
- ただし、西暦年が100で割り切れる場合はうるう年ではない。
- しかし、西暦年が400で割り切れる場合はうるう年とする。
このルールにより、例えば「2000年」は400で割り切れるためうるう年ですが、「1900年」や「2100年」はうるう年ではないということになります。
この3つの条件を論理演算子で組み合わせることが、独自のアルゴリズムを組む際のポイントです。
西暦とグレゴリオ暦の関係
Javaが内部で採用している日付処理の多くは、このグレゴリオ暦に準拠しています。
歴史的な背景から異なる暦も存在しますが、一般的なビジネスアプリケーション開発においては上記のルールを前提として問題ありません。
java.time.Yearクラスを使用した判定
現代のJava開発(Java 8以降)において、最も推奨されるのがjava.time.Yearクラスを使用する方法です。
このクラスには、そのものズバリの名称であるisLeapメソッドが用意されています。
isLeapメソッドの基本的な使い方
Year.isLeap(long year)は静的メソッドであり、インスタンス化せずに直接呼び出すことが可能です。
以下のサンプルコードを見てみましょう。
import java.time.Year;
public class LeapYearExample {
public static void main(String[] args) {
// 判定したい年を指定
int targetYear = 2024;
// YearクラスのisLeapメソッドを使用して判定
boolean isLeap = Year.isLeap(targetYear);
if (isLeap) {
System.out.println(targetYear + "年はうるう年です。");
} else {
System.out.println(targetYear + "年はうるう年ではありません。");
}
}
}
2024年はうるう年です。
このメソッドを使用する最大のメリットは、コードの意図が明確であり、読み手に「うるう年の判定をしている」ことが一目で伝わる点にあります。
自分で複雑な計算式を書く必要がないため、ケアレスミスによるバグを防ぐこともできます。
java.time.LocalDateクラスを使用した判定
すでに日付情報(LocalDate)のインスタンスが存在する場合は、そのインスタンスから直接判定することも可能です。
特定の「日」を扱っている最中に、その年がうるう年かどうかを知りたい場合に便利です。
日付インスタンスからの判定方法
LocalDateクラスには、isLeapYear()というメソッドが備わっています。
import java.time.LocalDate;
public class LocalDateLeapYear {
public static void main(String[] args) {
// 現在の日付を取得
LocalDate today = LocalDate.now();
// その年がうるう年かどうかを判定
boolean isLeap = today.isLeapYear();
System.out.println("今年はうるう年ですか? : " + isLeap);
// 特定の日付を指定して判定
LocalDate sampleDate = LocalDate.of(2026, 5, 10);
System.out.println(sampleDate.getYear() + "年はうるう年ですか? : " + sampleDate.isLeapYear());
}
}
今年はうるう年ですか? : false
2026年はうるう年ですか? : false
このように、日付オブジェクト自体が自分の年の状態を知っているため、オブジェクト指向らしい直感的な記述が可能になります。
算術演算による独自の判定アルゴリズム
ライブラリが制限されている環境や、アルゴリズムの学習目的、あるいは非常に高いパフォーマンスが要求される場面では、自前で計算式を書くこともあります。
前述した「4で割り切れる、かつ100で割り切れない、または400で割り切れる」という条件をコードに落とし込みます。
論理演算子を用いた条件式の作成
Javaの算術演算子%(剰余)と論理演算子(&&, ||)を組み合わせます。
public class CustomLeapYear {
public static void main(String[] args) {
int year = 2100;
// うるう年の判定アルゴリズム
// 1. 4で割り切れる
// 2. かつ「100で割り切れない」または「400で割り切れる」
boolean isLeap = (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0);
if (isLeap) {
System.out.println(year + "年はうるう年です。");
} else {
System.out.println(year + "年はうるう年ではありません。");
}
}
}
2100年はうるう年ではありません。
このコードは、標準APIが提供している判定ロジックと実質的に同じ動きをします。
year % 100 != 0 || year % 400 == 0の部分を括弧で囲むことで、演算の優先順位を明確にするのがポイントです。
過去のAPI(GregorianCalendar)を利用する方法
Java 8より前のバージョンや、古いライブラリとの互換性を保つ必要があるプロジェクトでは、java.util.GregorianCalendarを使用することがあります。
現在ではjava.timeの使用が強く推奨されていますが、既存資産のメンテナンスなどのために知っておいて損はありません。
import java.util.GregorianCalendar;
public class LegacyLeapYear {
public static void main(String[] args) {
GregorianCalendar cal = new GregorianCalendar();
int year = 2000;
// GregorianCalendarのisLeapYearメソッドを使用
if (cal.isLeapYear(year)) {
System.out.println(year + "年はうるう年です。");
} else {
System.out.println(year + "年はうるう年ではありません。");
}
}
}
2000年はうるう年です。
なお、java.util.Dateやjava.util.Calendar(抽象クラス)自体にはこの判定メソッドは含まれていないため、具象クラスであるGregorianCalendarへキャストするか、インスタンスを生成する必要があります。
各判定方法の比較まとめ
これまで紹介した方法を、状況に応じて使い分けるための比較表にまとめました。
| 手法 | 利用パッケージ | 推奨度 | 特徴 |
|---|---|---|---|
Year.isLeap() | java.time | 高 | 最もシンプルで読みやすい。モダンなJava開発の標準。 |
LocalDate.isLeapYear() | java.time | 高 | 日付オブジェクトを操作している場合に最適。 |
| 独自アルゴリズム | なし(基本演算) | 中 | 依存関係を最小限にしたい場合や学習用に適している。 |
GregorianCalendar | java.util | 低 | 古いシステム(Java 7以前)との互換性維持にのみ使用。 |
基本的には、「Yearクラス」または「LocalDateクラス」を使用するのが現在のJava開発における正解と言えます。
ユニットテストによる判定ロジックの検証
うるう年の判定は、境界値テストの格好の題材です。
特に自前で計算ロジックを実装した場合は、JUnitなどのテスティングフレームワークを用いて、正しく判定できるか確認するべきです。
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import java.time.Year;
class LeapYearTest {
@Test
void testLeapYears() {
// 代表的なうるう年のテスト
assertTrue(Year.isLeap(2000), "400で割り切れる2000年はうるう年");
assertTrue(Year.isLeap(2024), "4で割り切れる2024年はうるう年");
// うるう年ではない年のテスト
assertFalse(Year.isLeap(1900), "100で割り切れるが400で割り切れない1900年は平年");
assertFalse(Year.isLeap(2026), "4で割り切れない2026年は平年");
}
}
このように、「100の倍数だが400の倍数ではない年」という例外的な条件が正しく処理されているかをテストすることが非常に重要です。
実務での活用シーンと注意点
うるう年の判定は、単に「TrueかFalseか」を知るだけでは終わりません。
実務でよくある落とし穴として、「うるう年の2月29日に1年を加算しようとした場合」の挙動が挙げられます。
Java 8以降のDate-Time API(LocalDateなど)は、このあたりの調整を自動で行ってくれます。
例えば、2024年2月29日にplusYears(1)を実行すると、結果は自動的に2025年2月28日となります。
手動計算で日付を操作しようとすると、存在しない「2月29日」を作ってしまうバグが発生しやすいため、判定ロジックだけでなく加算処理自体も標準APIに任せるのが安全です。
まとめ
Javaでうるう年を判定する方法にはいくつかの選択肢がありますが、現代の標準的な手法はjava.time.Year.isLeap()を使用することです。
このメソッドは、グレゴリオ暦の複雑なルールを内部で完璧に処理しており、開発者がロジックのミスを心配する必要をなくしてくれます。
もし、プログラミングの基礎知識としてアルゴリズムを学びたいのであれば、剰余演算子を用いた条件式を自分で書いてみるのも良い経験になるでしょう。
どちらの方法をとるにせよ、「400で割り切れる年はうるう年」「100で割り切れるが400で割り切れない年は平年」というルールを常に念頭に置いておくことが大切です。
正確な日付判定をマスターして、信頼性の高いJavaアプリケーションを構築していきましょう。
