今日が「毎月第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で行えば目的が果たせそうです。

タグ:

TrackBack