各月の平日の数を取得する
各月の平日の数を取得する。
つまりはウィークデイ。
(いままで土日を除いた平日を「ウィークデイ」だと思っていたけれど、日曜日だけ抜く使い方もあるらしい。weekdayの意味 - 英和辞書 - goo辞書)
(ここでは土日を除いた平日の数を出す)
とりあえず祝日は考慮しない。
CPANとかありそうだったけど、せっせと再発明で修行する。
ちょっと考えて、一日ずつ localtime と timelocal で曜日取得して判断しようかな、と思った。
こんな感じ。
use Time::Local qw/ timelocal /; my $count = 0; my $year = 2010; my $month = 4; # April for my $day (1 .. 31) { my $wday = eval { (localtime(timelocal(0,0,0,$day,$month-1,$year)))[6] }; if (defined($wday) and (0 != $wday) and (6 != $wday)) { $count++; } } say $count; # => 22
が、timelocal → localtime を12ヶ月なら365ないし366回しないといけない。
5年分なんかになったら約1500回だ。
しかも、ありえない日付対策で、それを毎回 eval させてる。
さすがにちょっと無駄があるなあ、と考えなおしてみる。
カレンダーとにらめっこして、おやと思う。
結局のところ「日」は、
- 28日(2月)
- 29日(2月うるう年)
- 30日(4,6,9,11月)
- 31日(1,3,5,7,8,10,12月)
のパターンしかない。
確実に月の始まりの「ついたち」は「日月火水木金土」のどれかで始まるから(そりゃそうだ)、それにあわせて平日の数が出せるのでは…?
計算するより、手で数えてみた。
my %COUNT_WEEKDAY = ( # firstday_wday Sun => { # lastday => count 28 => 20, 29 => 20, 30 => 21, 31 => 22, }, Mon => { 28 => 20, 29 => 21, 30 => 22, 31 => 23, }, Tue => { 28 => 20, 29 => 21, 30 => 22, 31 => 23, }, Wed => { 28 => 20, 29 => 21, 30 => 22, 31 => 23, }, Thu => { 28 => 20, 29 => 21, 30 => 22, 31 => 22, }, Fri => { 28 => 20, 29 => 21, 30 => 21, 31 => 21, }, Sat => { 28 => 20, 29 => 20, 30 => 20, 31 => 21, }, );
あれ? もうこれ使ったほうが簡単じゃね? ハッシュ最速伝説じゃね?
あとは、月の最初の曜日と、末日を取得するようにすれば完成?
sub wday_of_firstday { my @WDAY = qw/ Sun Mon Tue Wed Thu Fri Sat /; my $year = shift; my $month = shift; my $time = timelocal(0, 0, 0, 1, $month - 1, $year); my $wday_of_firstday = (localtime($time))[6]; return $WDAY[$wday_of_firstday]; } sub lastday { my %LASTDAY = ( 1 => 31, 2 => 28, 3 => 31, 4 => 30, 5 => 31, 6 => 30, 7 => 31, 8 => 31, 9 => 30, 10 => 31, 11 => 30, 12 => 31, ); my $year = shift; my $month = shift; my $lastday = $LASTDAY{$month}; if (2 == $month) { return is_leap($year) ? 1 + $lastday : $lastday; } else { return $lastday; } }
is_leap()はうるう年判定。
あわせて、
my $year = 2010; my $month = 4; my $wday_of_firstday = wday_of_firstday($year, $month); my $lastday = lastday($year, $month); say $COUNT_WEEKDAY{$wday_of_firstday}->{$lastday}; # => 22
できました。
えらい力技になったけど、最初に比べたら計算量減っているので、まあ悪くはないかな。