💬

うるう年を考慮したお誕生日の判定方法

2024/12/13に公開

これは株式会社TimeTree Advent Calendar 2024の13日目の記事です。

こんにちは。TimeTreeのAndroidエンジニアの笠松です。
弊社はビジネスネーム制を採用しており、社内ではRingと呼ばれています。

この記事ではコードレビュで教えていただいたうるう年を考慮したお誕生日を判定する方法について書きたいと思います。

うるう年とは

うるう年は、地球が太陽の周りを1周するのにかかる時間(約365.2422日)を補正するために、通常の年よりも1日多い366日になる年のことです。
うるう年の判定は以下のとおりで、この1日が追加されるのは、2月29日です。

  1. 西暦年が4で割り切れる年は(原則として)閏年
  2. ただし、西暦年が100で割り切れる年は(原則として)平年
  3. ただし、西暦年が400で割り切れる年は必ず閏年

2月29日がお誕生日の人も毎年、お誕生日を迎えられるよう考慮する必要があります。
TimeTreeでは日本の法律に合わせて、うるう年ではない2月29日のお誕生日は2月28日と判定しています。

2月29日の判定についてはこちらのラジオもお聞きください。

threetenbpとは

threetenbp はJava 8の java.time パッケージをそれ以前のバージョンに移植したものです。

Java 8から導入された java.time APIは、従来の日時APIの問題点を解決するために設計されました。

threetenbpはタイムゾーンの読み込み等、Androidで効率的に動作するため、TimeTreeではこのライブラリを使用しています。

お誕生日の判定方法

本題のうるう年を考慮したお誕生日の判定方法です。

素朴に実装すると以下のようになります。

fun isBirthday(birthday: LocalDate, now: LocalDate): Boolean {
    return when {
            birthday.year > now.year -> false
            birthday.month == now.month && birthday.dayOfMonth == now.dayOfMonth -> true
            birthday.month == Month.FEBRUARY && birthday.dayOfMonth == 29 ->
                now.month == Month.FEBRUARY && now.dayOfMonth == 28 && !now.isLeapYear
            else -> false
    }
}

それぞれ、以下の判定を行っています。

  • birthday.year > now.yearで生まれる前ならば、お誕生日と判定しない
  • birthday.month == now.month && birthday.dayOfMonth == now.dayOfMonthで通常の日付一致
  • birthday.month == Month.FEBRUARY && birthday.dayOfMonth == 29 -> now.month == Month.FEBRUARY && now.dayOfMonth == 28 && !now.isLeapYearで2月29日のお誕生日の場合の例外ロジック

ライブラリをうまく使うと以下のように簡潔に実装できます。

fun isBirthday(birthday: LocalDate, now: LocalDate): Boolean {
    return birthday.isBefore(now) && birthday.withYear(now.year).isEqual(now)
}

ポイントはbirthday.withYear(now.year)です。
以下のようにライブラリ内でうるう年ではない2月29日を2月28日に変換するため、現在日付と一致するか確認するだけでよくなります。

private static LocalDate resolvePreviousValid(int year, int month, int day) {
    switch (month) {
        case 2:
            day = Math.min(day, IsoChronology.INSTANCE.isLeapYear(year) ? 29 : 28);
            break;
        ...
    }
    return LocalDate.of(year, month, day);
}

まとめ

うるう年を考慮したお誕生日の判定ロジックとthreetenbpを使用した実装方法をご紹介しました。
レビューはとてもありがたいですね。

TimeTree Tech Blog

Discussion