Dateクラスの閏年

Ruby閏年計算をするコードを作った際、Dateクラスを使ったらうまくいかなかった話。

 

 Rubyでは日にちを計算するのに便利な機能をまとめたDateクラスが備わっています。コードのはじめに require 'date' と呼び出すと、クラス内であらかじめ定義されているメソッドを使うことができます。

 さて本題。閏年を換算して曜日を算出するプログラム(西暦1年1月1日が月曜)を書きました。そこでDateクラスの中にある閏年を判別するメソッド「.leap?」を使ってみました。しかし、きちんと閏年計算が行われなかったようで、曜日にズレが生じてしまいました。
 実際のプログラムは最後に貼り付けてありますので、長いですがご確認いただければと思います。

 閏年というのは4年に一度366日/年になる(2/29が発生する)というものですが、例外があって、100の倍数の年は400で割り切れる年でないと閏年になりません。(西暦100年とか200年は365日で、西暦400年とか800年は366日)
 しかし、 .leap? メソッドではこの特例が適用されず、西暦100年とかも閏年として計算されていました。そのためズレが生じてしまいました。

 これはそういう仕様だそうです。この点をきちんと算出してくれるメソッドがありました。
.gregorian_leap? メソッドです。
 
 グレゴリウス暦で算出するこのメソッドなら400の倍数の年しか閏年にならない特例もきちんと算出してくれます。<参考サイト>
https://ruby-doc.org/stdlib-2.6.1/libdoc/date/rdoc/Date.html



 以下のプログラムには.leap?メソッドの挙動も確かめられる記述も載せているのでぜひご確認ください。
 

 

require "date"

def get_week(year, month, day)
 total_days = 0
 i = 1
 while i < year do
   if Date.new(i).leap?
     total_days +=366
   else
     total_days +=365
   end
   i +=1
 end

 month_days = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
 num = 1
 while num < month do
   if Date.leap?(year) && num == 2    <- ここをDate.gregorian_leap?(year)にする
     total_days += 29
   else
     total_days += month_days[num]
   end
   num +=1
 end

 total_days +=day

 weeks = ["", "", "", "", "", "", ""]
 remainder = total_days % 7
 weeks[remainder]
end

puts "年を入力してください:"
year = gets.to_i
puts "月を入力してください:"
month = gets.to_i
puts "日を入力してください:"
day = gets.to_i
puts " #{ get_week(year, month, day) }曜日"



# DATEクラスの確認用

if Date.gregorian_leap?(year)
  puts "\n#{year} is true"
else
  puts "\n#{year} is false"
end

if Date.new(100).leap?
  puts "100 is true"
else
  puts "100 is false"
end

if Date.new(400).leap?
  puts "400 is true"
else
  puts "400 is false"
end

if Date.new(2000).leap?
  puts "2000 is true"
else
  puts "2000 is false"
end

if Date.new(2001).leap?
  puts "2001 is true"
else
  puts "2001 is false"
end