[Java]年齢を求める

## コードを作った経緯

自分で作っているIDサービス(以下、本サービス)で一部のサービスと連携するにあたって年齢チェックが必要になったのがきっかけなんですが、氏名（本名）、住所、生年月日、性別、電話番号やマイナンバーといった個人情報を管理するシステム(以下、個人情報管理システム)が本サービスのAPIサーバーのさらに裏にありまして、本サービスはこの個人情報管理システムに年齢を計算させてその年齢と比較した結果を受け取るだけというふうになっています。
イメージとしてはこんな感じですね。（「サ」：本サービス　「個」：個人情報管理システム）

サ「ねえねえ、このIDの人って18歳以上？」
個「えっと…この人の誕生日はこの日だから今日を基準として考えると………うん、18歳以上だよ」
サ「はーい」

まあこんなフローはどうでもよくて、問題はなぜJavaでこんなのを書いたかなんですが、実はこの個人情報管理システムの開発言語がJavaなんです。
雑に年齢計算できればいい程度であればそこまで神経質にならずにざっくりとしたコードでいいんですけど、本サービスは

- [風俗営業等の規制及び業務の適正化等に関する法律](https://laws.e-gov.go.jp/law/323AC0000000122)
- [インターネット異性紹介事業を利用して児童を誘引する行為の規制等に関する法律](https://laws.e-gov.go.jp/law/415AC0000000083)

の２つに対応させるため非常に精密な年齢計算が必要でした。
もちろんそのくらいのコードは誰かが書いているだろうと思って調べました。
[pontyo4さん](https://qiita.com/pontyo4)という方が[[Java]誕生日と現在日から年齢を求める](https://qiita.com/pontyo4/items/14f159e802b312cb0b61)というコードを出していました。
ただ、私の今回のシステムは精密な計算が必要なので、この方のコードを拝借して色々なパターンでの年齢計算を試してみました。そしたら問題が発生してしまいました。
それは、**2月29日生まれの人が閏年でない年の2月28日に年齢が1つ増えていない**というものでした。
雑な計算で良いなら些細な問題ということで済ませるところではあるのですが、今回は法令への対応ということでこの問題を見過ごすことはできず、新しくコードを書きました。

## コード

2月29日生まれの人の年齢変化以外に問題は無かったので、pontyo4さんのコードをベースに今回のコードを作ってみました。

```java
private static boolean IsLeapYear(int Year) {
	if (Year % 4 != 0) return false;
	if (Year % 100 == 0) return Year % 400 == 0;
	return true;
}
private static int calcAge(Date birthday, Date now) {
	Calendar cBirthday = Calendar.getInstance();
	Calendar cNow = Calendar.getInstance();
	cBirthday.setTime(birthday);
	cNow.setTime(now);
	if (cBirthday.get(Calendar.MONTH) == 1 && cBirthday.get(Calendar.DAY_OF_MONTH) == 29 && !IsLeapYear(cNow.get(Calendar.YEAR))) cBirthday.add(Calendar.DATE, -1);
	int age = cNow.get(Calendar.YEAR) - cBirthday.get(Calendar.YEAR);
	if (cNow.get(Calendar.MONTH) < cBirthday.get(Calendar.MONTH) || (cNow.get(Calendar.MONTH) == cBirthday.get(Calendar.MONTH) && cNow.get(Calendar.DAY_OF_MONTH) < cBirthday.get(Calendar.DAY_OF_MONTH))) age--;
	return age;
}
```

若干私の好みの書き方に書き換わっている部分もあります（変数名程度ですが...）が、ほとんどいじっていません。
やったのは閏年じゃない年に2月29日生まれの人を2月28日生まれとして年齢計算させるようにした程度です。

## 使用例

これもほとんどpontyo4さんのコードそのままです。
まあ私のやつはテストしやすいように指定日地点での年齢を計算できるようにしてはありますが...

```java:Main.java
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		try {
			Scanner scan = new Scanner(System.in);
	        System.out.println("誕生日を入力してください（yyyy/mm/dd）");
	        String day = scan.next();
	        System.out.println("基準日を入力して下さい。（yyyy/mm/dd）");
	        String borderDate = scan.next();
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
			System.out.println("年齢は" + calcAge(sdf.parse(day), sdf.parse(borderDate)) + "歳です");
			scan.close();
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		
	}
	public static boolean IsLeapYear(int Year) {
		if (Year % 4 != 0) return false;
		if (Year % 100 == 0) return Year % 400 == 0;
		return true;
	}
	public static int calcAge(Date birthday, Date now) {
		Calendar cBirthday = Calendar.getInstance();
		Calendar cNow = Calendar.getInstance();
		cBirthday.setTime(birthday);
		cNow.setTime(now);
		if (cBirthday.get(Calendar.MONTH) == 1 && cBirthday.get(Calendar.DAY_OF_MONTH) == 29 && !IsLeapYear(cNow.get(Calendar.YEAR))) cBirthday.add(Calendar.DATE, -1);
		int age = cNow.get(Calendar.YEAR) - cBirthday.get(Calendar.YEAR);
		if (cNow.get(Calendar.MONTH) < cBirthday.get(Calendar.MONTH)
			|| (cNow.get(Calendar.MONTH) == cBirthday.get(Calendar.MONTH) && cNow.get(Calendar.DAY_OF_MONTH) < cBirthday.get(Calendar.DAY_OF_MONTH))) age--;
		return age;
	}
}
```