💭

閏年問題とDate#leap?メソッド

2022/03/23に公開

はじめに

今日の練習問題は閏年問題。
模範回答と私の回答を比較し、さらにRubyに準備されているDate#leap?メソッドなるものを使用して書き換えをしてみます。

練習問題

問題.1
西暦の年数および月を入力し、その月の日数を求めるプログラムを書きます。
その場合、閏年について考慮する必要があります。

閏年は以下の判断基準で決まります。

①その西暦が4で割り切れたら閏年である
②ただし、例外として100で割り切れる西暦の場合は閏年ではない
③ただし、その例外として400で割り切れる場合は閏年である

つまり、西暦2000年は閏年であり、西暦2100年は閏年ではありません。
これらに対応できるように、出力例と雛形をもとに実装しましょう。


出力例
1990年2月 =>"1990年2月は28日間あります"
2000年2月 =>"2000年2月は29日間あります"
2100年2月 =>"2100年2月は28日間あります"
2000年3月=>"2000年3月は31日間あります"


雛形

def get_days(year, month)
  month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  # ここに処理を書き加えてください
end

puts "年を入力してください:"
year = gets.to_i
puts "月を入力してください:"
month = gets.to_i

days = get_days(year, month)
puts "#{year}#{month}月は#{days}日間あります"

私の回答

def get_days(year, month)
  month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  # 月と配列が一つずつずれる
  days = month_days[month - 1]

  # 2月のときの場合分け
  if month == 2
    # 4で割り切れたら閏年
    if year % 4 == 0
      # 100で割り切れたら閏年(ただし400で割り切れる年は除く)
      if year % 100 == 0 && year % 400 != 0
        days
      # それ以外は閏年
      else
        days + 1
      end
    # 4で割り切れなかったら閏年でない
    else
      days
    end
  # 2月以外の場合はdaysそのまま
  else
    days
  end
end

puts "年を入力してください:"
year = gets.to_i
puts "月を入力してください:"
month = gets.to_i

days = get_days(year, month)
puts "#{year}#{month}月は#{days}日間あります"

模範回答

def get_days(year, month)
  month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  if month == 2
    if year % 4 == 0
      if year % 100 == 0 && year % 400 != 0
        days = 28
      else
        days = 29
      end
    else
      days = 28
    end
  else
    days = month_days[month - 1]
  end

  return days
end

puts "年を入力してください:"
year = gets.to_i
puts "月を入力してください:"
month = gets.to_i

days = get_days(year, month)
puts "#{year}#{month}月は#{days}日間あります"

解説

模範解答では、「2月場合分け」のdaysを28あるいは29と決め、
2月以外のdaysをdays = month_days[month - 1]として最後にreturnしている形になっているようです。

勉強中なので、より良い方というのがまだ分からないのですが
個人的にはメソッドの中ですべての月で具体的数字を使わない記述のほうが好きだなあと思ったりしました。

Date#leap?メソッドを使って

この練習問題の後に、閏年かどうかを判定する練習問題を前に見かけたような・・・?と
あやふやな記憶からググってみるとすぐに出てきました。
閏年かどうかを判定するメソッドがRubyに用意されているようです。
https://docs.ruby-lang.org/ja/latest/method/Date/i/leap=3f.html
例えば以下のように使用するようです。

Date.new(2000).leap?      #=> true
Date.new(2001).leap?      #=> false

これを使って練習問題を解いてみました。
ベースは先ほどの私の回答です。

def get_days(year, month)
  month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  days = month_days[month - 1]
  require "date"
  if month == 2
    if Date.new(year).leap?
      days + 1
    else
      days
    end
  else
    days
  end
end

puts "年を入力してください:"
year = gets.to_i
puts "月を入力してください:"
month = gets.to_i

days = get_days(year, month)
puts "#{year}#{month}月は#{days}日間あります"

まずは、4行目で日付を扱うためのDateライブラリーを使うためにrequire "date"として読み込みます。
6行目の2月の場合分けで、閏年かどうかを確認するのにDate.new(year).leap?としました。

もっとリファクタリングする余地はあるかもしれませんが、こんなにシンプルになりました!
デメリット?はこのメソッドを知ってなきゃできないというところですね。

参考

https://techacademy.jp/magazine/20560

Discussion