gotagota日記

「面白きことは良きことなり」

『たのしいruby 第4版』 第20章 TimeクラスとDateクラス

第20章 TimeクラスとDateクラス

模範解答はこちら

(1)

問題

「"2013年5月30日午後8時17分50秒"」といったように、「年・月・日・時・分・秒」を使った時刻の文字列を Time オブジェクトに変換して返す jparsedate メソッドを定義しろ、という問題です。

私的解答
require 'time'
require 'date'

def jparsedate(str)
  ary = str.split(/\D+/)
  year = ary[0]
  month = ary[1]
  day = ary[2]
  if str =~ /午後|pm/i
    hour = ary[3].to_i + 12
  else
    hour = ary[3]
  end
  min = ary[4]
  sec = ary[5]
  Time.mktime(year, month, day, hour, min, sec)
end
p jparsedate("2013年5月30日午後8時17分50秒") #=> 2013-05-30 20:17:50 +0900

まず、与えられた文字列を数字以外の部分(=漢字部分)で区切って配列にします。
こうすることで単純にインデックス通りに「年・月・日・時・分・秒」になるのですが、「時」だけはちょっと工夫がいります。
午前午後を正規表現で判断して、午後かpmが含まれていれば12を足して24時間表記に直しています。

(2)

問題

Unix の「ls -t」コマンドのように、指定ディレクトリの下にあるファイルを時刻の順に並べ替える ls_t メソッドを定義しろ、という問題です。

私的解答
require 'time'
require 'date'

def ls_t(path)
  ary = []
  returnStr = ''
  Dir.open(path) do |dir|
    dir.each do |name|
      if FileTest.file?(name)
        ary << name
      end
    end
  end
  ary.sort_by!{|item| File.ctime(item)}.each do |str|
    returnStr << "#{ary.index(str)+1}番目: #{str}\n"
  end
  returnStr
end
puts ls_t("./")

ディレクトリを開いて、ファイルを空の配列に追加していきます。
それを sort_by メソッドを用いて最後に更新された順に並べ替え、空の文字列に追加していきます。

(3)

問題

Date クラスを用いて今月の1日と月末の日付と曜日を求め、以下のような形式でカレンダーを表示させろ、という問題です。

      Sep 2014       
Su Mo Tu We Th Fr St
    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
私的解答

この問題はかなり骨が折れました。。。
なので(?)、ほとんどこちらを参考にさせていただきました。

以下、上記のブログを参考にしつつ自分なりにアレンジしました。

require 'time'
require 'date'

class Calender
  def initialize(year=Date.today.year, month=Date.today.month)
    @firstDay = Date.new(year, month, 1)
    @lastDay = Date.new(year, month, -1).day
    @wday = @firstDay.wday
    @weeks = (@wday + @lastDay + 6) / 7
  end

  def calAry
    calAry = (0...@weeks).map do |i|
      day = i * 7 - @wday + 1
      if day <= 0
        [nil] * @wday + (1..7 - @wday).to_a
      elsif day + 7 > @lastDay
        (day..@lastDay).to_a
      else
        (day..day + 6).to_a
      end
    end
  end

  def putCal
    header = @firstDay.strftime('%b') + "\s" + @firstDay.strftime('%Y')
    puts header.center(21)
    puts "Su Mo Tu We Th Fr St"
    calAry.each do |week|
      puts week.map {|d| d ? sprintf('%2d', d) : '  '}.join(' ')
    end
  end
end
sample = Calender.new
sample.putCal

まず、年月の指定があれば、引数で指定できるようにします。デフォルトなら現時点の年月です。
次に、
@firstDay に1日の日付,
@lastDay に月末の日付,
@wady に1日の曜日,
@weeks にその月が何週間あるか,
をそれぞれインスタンス変数にぶち込みます。これは @weeks 以外はやらなくてもいいのですが、何を扱ってるのかわかりやすくするためにあえて変数を用意しました。

calAry メソッドでは、7日間ごとの配列を作っています。
if day <= 0で最初の週を求めています。
elsif day + 7 > @lastDayで最後の週を求めています。

putCal メソッドは、カレンダー形式に出力する部分を担っています。 変数headerに月と西暦を入れて、それを center メソッドを用いて、中央寄せで出力します。
曜日は普通に出力して、日付は一週間ごとの配列 calAry を each メソッドで1つずつ取り出したものを三項演算子を用いつつ sprintf メソッドでいい感じに整えて出力します。