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