カレンダー UI を実装している時にその日が祝日かどうかまたはその日の祝日名が欲しいことがあると思う。
これが結構大変なのだが、私の場合はコジごみカレンダーというアプリで祝日判定に関して実装していたので、それを移植して祝日名を返すようにしたらできた。
2021 年は山の日、スポーツの日、海の日が特別ルールになっていたりといろいろと面倒だ。
あと振替休日判定が面倒。
ただ単に前の日が日曜日だったら、という判定では足りない。
尚 Laravel での実装前提なので日付を格納するインスタンスのクラスとして Carbon
を使用している。
例えば素 PHP の場合は適当に $year
$month
$day
などの int
型変数に書き換えればいいだろう。
また、今どきはこういう実装は JavaScript 側にすることが多いと思う。
そちらへの移植も難しくないだろう。
/**
* その日が祝日であれば祝日名を返す.
* 祝日でなければ null を返す.
*
* @param Carbon $carbon 対象日付
* @return string|null 祝日名 (祝日でない場合 null)
*/
private function getHolidayName(Carbon $carbon): ?string
{
list($y, $m, $d, $w) = [$carbon->year, $carbon->month, $carbon->day, $carbon->dayOfWeek];
if ($m === 1 && $d === 1) {
return '元旦';
} elseif (($y < 2000 && $m === 1 && $d === 15) || ($y > 1999 && $m === 1 && $d >= 8 && $d <= 14 && $w === Carbon::MONDAY)) {
return '成人の日';
} elseif ($m === 2 && $d === 11) {
return '建国記念の日';
} elseif (($y > 2018 && $m === 2 && $d === 23) || ($y > 1988 && $y < 2019 && $m === 12 && $d === 23)) {
return '天皇誕生日';
} elseif ($this->isShunbun($y, $m, $d)) {
return '春分の日';
} elseif ($m === 4 && $d == 29) {
return '昭和の日';
} elseif ($m === 5 && $d === 3) {
return '憲法記念日';
} elseif ($m === 5 && $d === 4) {
return 'みどりの日';
} elseif ($m === 5 && $d === 5) {
return 'こどもの日';
} elseif (($y > 1995 && $y < 2003 && $m === 7 && $d === 20) || ($y > 2002 && $y !== 2021 && $m === 7 && $d >= 15 && $d <= 21 && $w === Carbon::MONDAY) || ($y === 2021 && $m === 7 && $d === 22)) {
return '海の日';
} elseif (($y > 2015 && $y !== 2021 && $m === 8 && $d === 11) || ($y === 2021 && $m === 8 && $d === 8)) {
return '山の日';
} elseif (($y < 2003 && $m === 9 && $d === 15) || ($y > 2002 && $m == 9 && $d >= 15 && $d <= 21 && $w === Carbon::MONDAY)) {
return '敬老の日';
} elseif ($this->isShubun($y, $m, $d)) {
return '秋分の日';
} elseif (($y < 2000 && $m === 10 && $d === 10) || ($y > 1999 && $y !== 2021 && $m === 10 && $d >= 8 && $d <= 14 && $w === Carbon::MONDAY) || ($y === 2021 && $m === 7 && $d === 23)) {
return $y > 2019 ? 'スポーツの日' : '体育の日';
} elseif ($m === 11 && $d === 3) {
return '文化の日';
} elseif ($m === 11 && $d === 23) {
return '勤労感謝の日';
} else {
return null;
}
}
/**
* 春分の日かどうかを判定して返す.
*
* @param int $y 年
* @param int $m 月
* @param int $d 日
* @return bool 春分の日かどうか
*/
private function isShunbun(int $y, int $m, int $d): bool
{
$arg1 = $y < 1980 ? 20.8357 : 20.8431;
$arg2 = $y < 1980 ? 1983 : 1980;
return $m === 3 && floatval($d) === floor($arg1 + 0.242194 * ($y - 1980) - floor(($y - $arg2) / 4.0));
}
/**
* 秋分の日かどうかを判定して返す.
*
* @param int $y 年
* @param int $m 月
* @param int $d 日
* @return bool 秋分の日かどうか
*/
private function isShubun(int $y, int $m, int $d): bool
{
$arg1 = $y < 1980 ? 23.2588 : 23.2488;
$arg2 = $y < 1980 ? 1983 : 1980;
return $m === 9 && floatval($d) === floor($arg1 + 0.242194 * ($y - 1980) - floor(($y - $arg2) / 4.0));
}
/**
* 対象日が振替休日かを判定して返す.
*
* @param Carbon $target 対象日
* @return bool 振替休日かどうか
*/
private function isExtra(Carbon $target): bool
{
// 前日が日曜かつ祝日であるならば振替休日
$carbon = $target->copy();
$carbon->subDay();
if ($this->getHolidayName($carbon) !== null) {
if ($carbon->dayOfWeek === Carbon::SUNDAY) {
return true;
}
// 2007 年以後では日曜の祝日が発生した場合次の平日が振替休日となる: GW の振替休日判定が増える
if ($carbon->year > 2006) {
// 前日と後日が祝日であったならば挟まれた日も祝日
$carbon->addDays(2); // 対象日の後日に移動
if ($this->getHolidayName($carbon) !== null) {
return true;
}
// 前々日から日曜の祝日を順に探索. 日曜でなく祝日だけ満たしていたらその前の日を探索し続ける. 祝日でなくなったら終了
$carbon->subDays(3); // 後日から前々日へシフト
while ($this->getHolidayName($carbon) !== null) {
if ($carbon->dayOfWeek === Carbon::SUNDAY) {
return true;
}
$carbon->subDay();
}
}
}
return false;
}
これを以下のように使用できる:
$carbon = Carbon::now();
$holidayName = $this->getHolidayName($carbon) ?? ($this->isExtra($carbon) ? '振替休日' : '');
$holidayName
には祝日名もしくは振替休日という文字列、対象でない場合は null
が格納される。