Perlで日本の休日をチェックする用事があったので、試しに作ってみたものです。
もともとサブルーチン集 - 休日かどうかをチェックする - futomi's CGI Cafeを見つけて、いまいちぴんとこないところがあったので自分なりに書き換えてみたものですが、逆にすごい変な書き方になっているかもしれません。無保証です。
# jholiday.pl
package jholiday;
use strict;
use vars qw($debug %name);
$debug = 0;
%name = (
new_years => '元日',
coming_of_age => '成人の日',
national_foundation => '建国記念の日',
vernal_equinox => '春分の日',
greenery => 'みどりの日',
constitution_memorial => '憲法記念日',
childrens => 'こどもの日',
marine => '海の日',
respect_for_the_age => '敬老の日',
autumnal_equinox => '秋分の日',
health_and_sports => '体育の日',
national_culture => '文化の日',
labor_thanksgiving => '勤労感謝の日',
emperors_birthday => '天皇誕生日',
holiday_for_a_nation => '国民の休日',
showa => '昭和の日',
compensatory_holiday => '振替休日',
sunday => '日曜日',
saturday => '土曜日',
);
# 祝日のチェック
sub check {
my $date;
if (ref($_[0]) eq 'holiday::date') {
$date = $_[0];
} else {
$date = holiday::date->new(@_);
}
check_normal_holiday($date)
or check_compensatory_holiday($date)
or check_holiday_for_a_nation($date)
or check_weekend($date)
or '';
}
# 土曜・日曜
sub check_weekend {
$debug and warn "check weekend ...\n";
my $date = shift;
my $wday = $date->wday;
return $name{sunday} if ($wday == 0);
return $name{saturday} if ($wday == 6);
return '';
}
# 通常の祝日
sub check_normal_holiday {
my $date = shift;
check_fixed_holiday($date)
or check_equinox_day($date)
or check_happy_monday($date);
}
# 日付固定の祝日
sub check_fixed_holiday {
$debug and warn " fixed holiday ...\n";
my $date = shift;
my ($y, $md) = ($date->year, $date->out_md);
my %fixed_holiday = (
'01-01' => $name{new_years},
'02-11' => $name{national_foundation},
'04-29' => $name{showa},
'05-03' => $name{constitution_memorial},
'05-04' => $name{greenery},
'05-05' => $name{childrens},
'11-03' => $name{national_culture},
'11-23' => $name{labor_thanksgiving},
'12-23' => $name{emperors_birthday},
);
if ($y < 2003) {
$fixed_holiday{'07-20'} = $name{marine};
$fixed_holiday{'09-15'} = $name{respect_for_the_age};
}
if ($y < 2000) {
$fixed_holiday{'01-15'} = $name{coming_of_age};
$fixed_holiday{'10-10'} = $name{health_and_sports};
}
if ($y < 2007) {
delete($fixed_holiday{'05-04'});
$fixed_holiday{'04-29'} = $name{greenery};
}
foreach (keys %fixed_holiday) {
next if ($_ ne $md);
return $fixed_holiday{$_};
}
return '';
}
# 春分の日・秋分の日
sub check_equinox_day {
$debug and warn " equinox day ...\n";
my $date = shift;
my ($y, $m, $d) = ($date->year, $date->month, $date->day);
my $vernal = int(20.8431 + 0.242194 * ($y - 1980) - int(($y - 1980) / 4));
return $name{vernal_equinox} if ($m == 3 && $d == $vernal);
my $autumnal = int(23.2488 + 0.242194 * ($y - 1980) - int(($y - 1980) / 4));
return $name{autumnal_equinox} if ($m == 9 && $d == $autumnal);
return '';
}
# ハッピーマンデー制度による祝日
sub check_happy_monday {
$debug and warn " happy monday ...\n";
my $date = shift;
return '' if ($date->wday != 1);
my ($y, $m) = ($date->year, $date->month);
my $nth = $date->mweek;
return '' if ($y < 2000);
return $name{coming_of_age} if ($m == 1 && $nth == 2);
return $name{health_and_sports} if ($m == 10 && $nth == 2);
return '' if ($y < 2003);
return $name{marine} if ($m == 7 && $nth == 3);
return $name{respect_for_the_age} if ($m == 9 && $nth == 3);
return '';
}
# 振替休日
sub check_compensatory_holiday {
$debug and warn " compensatory holiday ...\n";
my $date = shift;
my $wday = $date->wday;
return '' if (check_normal_holiday($date));
$wday == 1
and check_normal_holiday($date->prev)
and return $name{compensatory_holiday};
$date->year >= 2007
and $date->out_md eq '05-06'
and $wday >= 2
and $wday <= 3
and return $name{compensatory_holiday};
return '';
}
# 国民の休日(祝日と祝日の間の日)
sub check_holiday_for_a_nation {
$debug and warn " national holiday ...\n";
my $date = shift;
my $prev = $date->prev;
my $next = $date->next;
return '' if ($date->wday == 0);
return '' unless (check_normal_holiday($prev));
return '' unless (check_normal_holiday($next));
return $name{holiday_for_a_nation};
}
# 日付オブジェクト #####################################################
package holiday::date;
use Time::Local;
use Carp qw(croak);
# 年月日よりdateオブジェクトを作成
sub new {
my $pkg = shift;
my ($y, $m, $d) = @_;
&ymdlocal($y, $m, $d); # error check
my $date = {
year => $y,
month => $m,
day => $d,
};
bless $date, $pkg;
}
# 年を返す
sub year {
my $self = shift;
return $self->{year};
}
# 月を返す
sub month {
my $self = shift;
return $self->{month};
}
# 日を返す
sub day {
my $self = shift;
return $self->{day};
}
# dateオブジェクトからエポック秒を返す
sub epoch_time {
my $self = shift;
return &ymdlocal($self->year, $self->month, $self->day);
}
# 前日のdateオブジェクトを返す
sub next {
my $self = shift;
my $pkg = ref($self);
my $time = $self->epoch_time + 86400;
return $pkg->new(&localymd($time));
}
# 翌日のdateオブジェクトを返す
sub prev {
my $self = shift;
my $pkg = ref($self);
my $time = $self->epoch_time - 86400;
return $pkg->new(&localymd($time));
}
# yyyy-mm-dd 形式で日付を出力
sub out_ymd {
my $self = shift;
return sprintf('%d-%02d-%02d', $self->year, $self->month, $self->day);
}
# mm-dd 形式で日付を出力
sub out_md {
my $self = shift;
return sprintf('%02d-%02d', $self->month, $self->day);
}
# 曜日 (日 => 0, ... 土 => 6)
sub wday {
my $self = shift;
return (localtime($self->epoch_time))[6];
}
# 月の中の第n週目かを求める
sub mweek {
my $self = shift;
my $d = $self->day;
return int(($d - 1) / 7) + 1;
}
# エポック秒から年月日を返す (localtimeのラッパー)
sub localymd {
my $time = shift;
my ($y, $m, $d) = (localtime($time))[5, 4, 3];
$y += 1900;
$m += 1;
return ($y, $m, $d);
}
# 年月日からエポック秒を返す (timelocalのラッパー)
sub ymdlocal {
my ($y, $m, $d) = @_;
my $time;
eval { $time = timelocal(0, 0, 0, $d, $m - 1, $y - 1900); };
croak('illegal date.') if ($@);
return $time;
}
1;
# a.pl use strict; require 'jholiday.pl'; print jholiday::check(@ARGV), "\n";
$ perl a.pl 2005 5 3 憲法記念日 $ perl a.pl 2005 5 4 国民の休日 $ perl a.pl 2005 5 5 こどもの日 $ perl a.pl 2005 5 6 $