今日が「毎月第2、第4木曜日の6日前」かどうかを判定する処理
2011 年 8 月 19 日 by 山平とある集まり(毎月第2、第4木曜日に開催)の出欠を確認する必要があり、念のため週末をはさんで連絡する役になっているのですが、正直、自信がありません。
遠くない将来、連絡を忘れてしまうことは火を見るよりも明らかなので、私よりも几帳面なCRONに監視してもらうことにします。
せっかく面倒臭そうな判定を作るので、今後も使えそうなものにしたいと思います。
1.考え方
タイトルでは、『今日が「毎月第2、第4木曜日の6日前」かどうか』という言い回しになっていますが、条件が少し変わると月を跨ぐので、ここを汎用的になるように注意します。
例)15日前、第1木曜日、など
そこで、意味を変えずに『今日の6日後が「第2、第4木曜日」かどうか』と読みかえて考えます。
当然、木曜日以外でも使えるようにするので、「指定月のX番目のY曜日かどうか」が分かるような仕組みを作る必要がありそうです。
下記のような比較ができるようにDateクラスを拡張してみます。
今日+6日 == 指定年月→指定曜日→2番目
2.実装
指定年月の指定曜日のN番目を取得する、というと複雑な気がしますが、配列で実現できました。
#2011年8月の木曜日 ym201108thu = ["2011-08-04", "2011-08-11", "2011-08-18", "2011-08-25"] #2011年8月の第2木曜日(配列は0から数えるため、-1する) ym201108thu[1] #=>"2011-08-11"
ということは、Dateインスタンス(指定年月)の指定曜日が配列で取得できれば解決できそうなので、以下のようにDateクラスにメソッドを追加します。
#!/usr/bin/ruby require 'date' class Date %w[mondays tuesdays wednesdays thursdays fridays saturdays sundays].inject(1){|wday, name| eval "def #{name} ; weekdays(#{wday});end" wday += 1 } def weekdays(w)# return [date, ..] days, year, month = [] , self.year, self.month day = Date.new(year, month, 1) # first of this month matsu = Date.new(year, month, -1) # last of this month until day > matsu do days << day.dup if day.wday == w day += (days.length > 0 ? 7 : 1)#day end days end end p (Date.today + 6).wdays(4)[2,4].map{|d| d.to_s} if $0 == __FILE__
3.使い方
Dateクラスに、下記の8つのメソッドが追加されます。
weekdays(w) mondays tuesdays wednesdays thursdays fridays saturdays sundays
weekdays(w)は、引数に曜日(月曜日を1とする)を取り、自身の年月の中から対象の曜日を配列で返します。
それ以外は曜日名のメソッドで対応する曜日の配列を返します。
現在、2011年8月7日(日曜日)のため、『今日が「毎月第2、第4土曜日の6日前」かどうかを判定したい』と言う条件に変更してテストを実施します。
d = Date.today+6 #=> # d.to_s #(確認)=> "2011-08-13" d.saturdays[1,3].map{|w|w.to_s} #(確認)=> ["2011-08-13", "2011-08-27"] d.saturdays[1,3].include?(d) #=> true
うまくできました。
これで日付の判定→通知を行う処理を1日1回cronで行えば目的が果たせそうです。
タグ: Ruby