challenge 西暦 to 和暦

西暦を和暦に変換するプログラムを書いてください。元号の切り替わる日など、複数の表記が可能な場合には両方表示し、西暦が無効な日付の場合には「範囲外」と表示するようにしてください。対応すべき日付は明治元年以降とします。

>a.py 1868/12/2
明治1年12月2日

>a.py 1926/12/24
大正15年12月24日

>a.py 2007/12/01
平成19年12月1日

>a.py 1926/12/25
大正15年12月25日 昭和1年12月25日

>a.py 1868/1/2
範囲外

>a.py 1868/100/2
範囲外

Posted feedbacks - Ruby

お題のサンプル出力では元年は前年号の最終年と併記していますが, 1年を元年としたことが同義だと考えてその記述は省略しました.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
require 'date'
require 'kconv'
WAREKI = [['明治', '1868/9/8'], ['大正', '1912/7/30'], 
  ['昭和', '1926/12/25'], ['平成', '1989/1/8']]
DATES = %w('1868/12/2' '1926/12/24' '2007/12/01' '1926/12/25'
           '1868/1/2' '1868/100/2')
wareki = WAREKI.sort{|a, b| 
  -1*(Date.parse(a[1]).year <=> Date.parse(b[1]).year)
} # => 新しい年代からマッチさせるためにソート 
DATES.each{|date_str|
  begin
    date = Date.parse(date_str)
    y0, m0, d0 = date.year, date.month, date.day
    era = wareki.each{|nengo, date_str|
      date = Date.parse(date_str)
      y1, m1, d1 = date.year, date.month, date.day
      evaluation = y0 >= y1 && (m0 > m1 or m0 == m1 && d0 >= d1)
      break [nengo, y0 - y1] if evaluation
    }
    puts "#{era[0]}#{era[1] == 0 ? '元' : era[1] + 1}#{m0}#{d0}日".tosjis
  rescue
    puts '範囲外'.tosjis
    next
  end
}

文字列の比較と,Rangeを使って書きました. RubyのRangeは終点を無限大にできないので,自前でそれっぽいクラスを定義しました.

「明治」はFri Dec 13 20:45:52 UTC 1901以前を含むので,32ビットのtime_tでは表現できず,そのためTimeクラスが使えないってのが痛いなぁ.

実行結果:

./jdate.rb 1868/12/2
範囲外
./jdate.rb 1926/12/24
大正15年12月24日
./jdate.rb 2007/12/01
平成19年12月1日
./jdate.rb 1926/12/25
大正15年12月25日 昭和1年12月25日
./jdate.rb 1868/1/2
範囲外
./jdate.rb 1868/100/2
範囲外
./jdate.rb 1873/01/01
明治6年1月1日
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/usr/local/bin/ruby -Ke
require 'time'

class DateRange < Range
  attr_reader :infinit
  def initialize(first, last, exclude_end = false)
    super
    @infinit = true if first > last
  end

  def ===(other)
    return super unless @infinit
    return true if self.first <= other
    return false
  end
end

def acceptable?(date, y, m, d)
  unless (0 < m and m < 13) and (0 < d and d < 32) and
         (date >= '1873/01/01')
    return false
  end
  return true
end

def usage
  puts "usage: #{File.basename($0)} <date>"
end

class DateOutOfRange < Exception; end

era = [['明治', ['1868/01/25', '1912/07/30']],
       ['大正', ['1912/07/30', '1926/12/25']],
       ['昭和', ['1926/12/25', '1989/01/07']],
       ['平成', ['1989/01/08', '1970/01/01']]].
      map {|name, r| [name, DateRange.new(*r)] }
begin
  date = ARGV[0]
  y, m, d = %r!(\d+)[/-](\d+)[/-](\d+)!.match(date).to_a[1..-1].map {|x| x.to_i}
  date = "%04d/%02d/%02d" % [y, m, d]
  raise DateOutOfRange unless acceptable?(date, y, m, d)
  result = era.select {|_, r| r === date}.
               map {|name, r| [name, y - r.first.split('/')[0].to_i + 1] }.
               map {|name, y| "%s%d年%d月%d日" % [name, y, m, d] }
  raise DateOutOfRange if result.empty?
  puts result.join(' ')
rescue DateOutOfRange
  puts '範囲外'
rescue
  usage
  exit(1)
end

caseを使って素直に実装してみました。太陰暦は考慮に入れていません。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/env ruby -Ku

require 'date'

def seireki2wareki(date = Date.today)
  case
  when date > Date.new(1989, 1, 7)
    "平成#{date.year - 1988}#{date.month}#{date.day}日"
  when date == Date.new(1989, 1, 7)
    '昭和64年1月7日 平成1年1月7日'
  when date > Date.new(1926, 12, 25)
    "昭和#{date.year - 1925}#{date.month}#{date.day}日"
  when date == Date.new(1926, 12, 25)
    '大正15年12月25日 昭和1年12月25日'
  when date > Date.new(1912, 7, 30)
    "大正#{date.year - 1911}#{date.month}#{date.day}日"
  when date == Date.new(1912, 7, 30)
    '明治45年7月30日 大正1年7月30日'
  when date > Date.new(1868, 9, 8)
    "明治#{date.year - 1867}#{date.month}#{date.day}日"
  else
    '範囲外'
  end
end

if __FILE__ == $0
  begin
    if ARGV.empty?
      puts seireki2wareki
    else
      puts seireki2wareki(Date.parse(ARGV.shift))
    end
  rescue
    puts '範囲外'
  end
end

だいぶ遅くなりましたが、投稿させていただきます。調べる日付を閾値の配列に放り込んでsort することで元号を調べています。

#明治6年未満は「範囲外」としました。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
require 'date'

DAYS = [Date.parse('1873/1/1'), Date.parse('1912/7/30'), Date.parse('1926/12/25'), Date.parse('1989/1/8')]
GENGO = ['明治', '大正', '昭和', '平成']

def format_date(d, i)
  raise if d < DAYS[0]
   d = d + 365 * 6 + 1 if i <= 1 #明治用のオフセット
  return "#{GENGO[i != 0 ? i - 1 : i]}#{d.year - DAYS[i - 1].year + 1}#{d.month}#{d.day}日"
end

begin
  d = Date.parse(ARGV[0])
  days = DAYS.push(d).sort
  i = days.index(d)
  puts (days.uniq.size == 4 && i != 0 ? format_date(d, i) << ' ' << format_date(d, i + 1) : format_date(d, i))
rescue
  puts '範囲外'
end

Index

Feed

Other

Link

Pathtraq

loading...