続・満年齢の計算 - わさっきの続きです.
馬齢(ばれい)とは馬の年齢のことである。
馬齢 - Wikipedia
日本では生まれた時が0歳で、以後1月1日が来ると同じ年に生まれた馬は一斉に1歳加齢する。
国による違いや,日本でもこの算出方法にしたのは2001年からだとかありますが,そういうのは省略させてもらって,上の意味での馬齢を求める処理を,以前のコードに追加しましょう.
出来上がりはこんなの.ちなみに前回のファイル名はage2.rb,今回はage3.rbです.
#!/usr/bin/env ruby require 'date' class Age METHOD_ORDINAL = 1 # 誕生した日を1歳とし,1月1日に年齢を1加える METHOD_CONVENTION = 2 # 誕生日に年齢を1加える METHOD_LAW = 3 # 年齢計算ニ関スル法律に基づき,誕生日の前日に年齢を1加える METHOD_HORSE = 4 # 誕生した日を0歳とし,1月1日に年齢を1加える(馬齢) def initialize @birth = nil # 生年月日.Dateインスタンスまたはnil @today = Date.today # 基準日 @method = METHOD_CONVENTION # 算出方法.定数METHOD_*のいずれか @readiness = 0 # 入力データの状況 end attr_accessor :birth, :today, :method attr_reader :readiness def parse(argv) @readiness = 0 # コマンドライン引数解析ができなければすぐ終了 return if !(Array === argv) or argv.empty? @readiness = 1 while !argv.empty? # -l, -o, -c オプションの判定 if argv[0][0, 1] == '-' param = argv.shift case param when '-o' @method = METHOD_ORDINAL when '-c' @method = METHOD_CONVENTION when '-l' @method = METHOD_LAW when '-h' @method = METHOD_HORSE end else d, argv = create_date(argv) return unless d case @readiness when 1 # 生年月日 @birth = d @readiness = 2 when 2 # 基準日 @today = d @readiness = 3 end end end end def create_date(argv) d = nil begin if argv.size >= 3 and /^\d+ \d+ \d+$/ =~ argv[0, 3].join(' ') # 3つの数値が引数にあるなら,それをDateオブジェクトに変換する d = Date.parse(argv.slice!(0, 3).join('/')) else # そうでなければ,配列の最初の要素をDateオブジェクトに変換する d = Date.parse(argv.shift) end rescue # 失敗したら,nilと,日付取得のための要素を取り除いた配列を返す # (無限ループ回避のため) end return d, argv end def method_to_s case @method when METHOD_ORDINAL 'ordinal' when METHOD_LAW 'by law' when METHOD_HORSE 'house age' else 'by convention' end end def calc_age # 未来の人の年齢を求めることはできない return -1 if @today < @birth # 年差を仮の年齢とおく age = @today.year - @birth.year case @method when METHOD_ORDINAL # 数え年: 年差+1 age += 1 when METHOD_HORSE # 馬齢:今のageの値 else # [基準年, 生まれ月, 生まれ日]をもとにDateインスタンスを作る. # ただし,2月29日生まれで,基準年が閏年でない場合は, # 基準年の3月1日を用いる. if !@today.leap? and @birth.mon == 2 and @birth.mday == 29 d = Date.new(@today.year, 3, 1) else d = Date.new(@today.year, @birth.mon, @birth.mday) end # 法律の満年齢: 年差 (ただし,誕生日の前日より前なら -1) # 慣習の満年齢: 年差 (ただし,誕生日より前なら -1) d -= 1 if @method == METHOD_LAW age -= 1 if @today < d end # 戻り値 age end end if __FILE__ == $0 # 引数チェック ac = Age.new ac.parse(ARGV) case ac.readiness when 0 puts "Usage: ruby #{$0} [-h][-l][-c][-o] birthdate [today]" exit when 1 puts "Wrong parameter" exit end # 年齢計算 age = ac.calc_age # 出力 puts "Birth date: #{ac.birth}" puts "Today: #{ac.today}" if age < 0 puts "Preborn?" exit end puts "Age: #{age}" puts "Method: #{ac.method_to_s}" end
数え年から1を引けばいいや,という以前に,単純に年差でよかったのでした.
さて,実行結果.
$ ruby age3.rb 2000 2 6 Birth date: 2000-02-06 Today: 2008-02-24 Age: 8 Method: by convention $ ruby age3.rb -l 2000 2 6 Birth date: 2000-02-06 Today: 2008-02-24 Age: 8 Method: by law $ ruby age3.rb -o 2000 2 6 Birth date: 2000-02-06 Today: 2008-02-24 Age: 9 Method: ordinal $ ruby age3.rb -h 2000 2 6 Birth date: 2000-02-06 Today: 2008-02-24 Age: 8 Method: house age $ ruby age3.rb 2000/2/29 2003/3/1 Birth date: 2000-02-29 Today: 2003-03-01 Age: 3 Method: by convention $ ruby age3.rb -o 2000/2/29 2003/3/1 Birth date: 2000-02-29 Today: 2003-03-01 Age: 4 Method: ordinal $ ruby age3.rb -h 2000/2/29 2003/3/1 Birth date: 2000-02-29 Today: 2003-03-01 Age: 3 Method: house age
ついでに,前のと差分を出しておきましょう.
$ diff age2.rb age3.rb 8a9 > METHOD_HORSE = 4 # 誕生した日を0歳とし,1月1日に年齢を1加える(馬齢) 37a39,40 > when '-h' > @method = METHOD_HORSE 80a84,85 > when METHOD_HORSE > 'house age' 93c98,99 < if @method == METHOD_ORDINAL --- > case @method > when METHOD_ORDINAL 95a102,103 > when METHOD_HORSE > # 馬齢:今のageの値 116c124 < if __FILE__ == $0 and false --- > if __FILE__ == $0 122c130 < puts "Usage: ruby #{$0} [-l][-c][-o] birthdate [today]" --- > puts "Usage: ruby #{$0} [-h][-l][-c][-o] birthdate [today]"
特に注意することはないのですが,まあ,メソッドcalc_ageの中で@methodで場合分けする際に,case〜whenに置き換えています.この値がMETHOD_HORSEのときは何も処理しませんが,「when METHOD_HORSE」を書いておかないとelseの処理をしてしまいます.あと,Cのswitch〜caseと違ってbreakは不要です.いや,書くとエラーです.