<?php
class Holiday {
private $holidayDefinitions;
private $year;
private $month;
private $result;
private $useIndefiniteHoliday;
private $resultType;
private $dateTime;
public function __construct($year = 0) {
// 祝日定義
// ('国民の祝日に関する法律'が公布・施行された1948年7月20日以降のもののみ)
$this->holidayDefinitions = [
1 => [
'1949:1' => '元日',
'1949:15, 2000:2_1'
=> '成人の日',
],
2 => [
'1967:11' => '建国記念の日',
'2020:23' => '天皇誕生日',
'1989:24, 1990:0'
=> '大喪の礼',
],
3 => [
'1949:s' => '春分の日',
],
4 => [
'1959:10, 1960:0'
=> '結婚の儀',
'1949:29' => '天皇誕生日, 1989:みどりの日, 2007:昭和の日',
],
5 => [
'2019:1, 2020:0'
=> '皇太子殿下即位・改元',
'1949:3' => '憲法記念日',
'2007:4' => 'みどりの日',
'1949:5' => 'こどもの日',
],
6 => [
'1993:9, 1994:0'
=> '結婚の儀',
],
7 => [
'1996:20, 2003:3_1, 2020:23, 2021:22, 2022:3_1'
=> '海の日',
'2020:24, 2021:23, 2022:0'
=> 'スポーツの日',
],
8 => [
'2016:11, 2020:10, 2021:8, 2022:11'
=> '山の日',
],
9 => [
'1966:15, 2003:3_1'
=> '敬老の日',
'1948:a' => '秋分の日',
],
10 => [
'1966:10, 2000:2_1, 2020:0, 2022:2_1'
=> '体育の日, 2020:スポーツの日',
'2019:22, 2020:0'
=> '即位礼正殿の儀',
],
11 => [
'1948:3' => '文化の日',
'1990:12, 1991:0'
=> '即位礼正殿の儀',
'1948:23' => '勤労感謝の日',
],
12 => [
'1989:23, 2019:0'
=> '天皇誕生日',
],
];
$this->dateTime = new DateTime();
$this->dateTime->setTime(0, 0, 0);
if($year < 1) $year = $this->dateTime->format('Y');
$this->year = (int) $year;
$this->month = (int) $this->dateTime->format('m');
$this->result = [];
$this->useIndefiniteHoliday = 1;
$this->resultType = 0;
}
/**
* 1年分のリストを返す
*/
public function getHolidayOfYear($year = 0) {
if($year < 1) $year = $this->year;
$year = (int) $year;
if(!isset($this->result[$year])) {
// 該当年の祝日配列に変換
$holiday = [];
$equinox = .242194 * ($year - 1980) - floor(($year - 1980) / 4);
foreach($this->holidayDefinitions as $month => $currentMonthData) {
$holiday[$month] = [];
foreach($currentMonthData as $days => $names) {
// 対象年・日取得
$days = explode(',', $days);
$arrTmp = [];
foreach($days as $tmp) {
$tmp = explode(':', $tmp);
$arrTmp[(int) $tmp[0]] = trim($tmp[1]);
}
$yearTmp = 0;
foreach(array_keys($arrTmp) as $tmp)
if($year >= $tmp && $tmp >= $yearTmp) $yearTmp = $tmp;
if($yearTmp == 0) continue;
// 日を記述形式ごとに取得
if($arrTmp[$yearTmp] === 's')
$day = floor(20.8431 + $equinox);
elseif($arrTmp[$yearTmp] === 'a')
$day = floor(23.2488 + $equinox);
elseif(strpos($arrTmp[$yearTmp], '_') !== false) {
[$num, $w] = explode('_', $arrTmp[$yearTmp]);
$day = $this->getDayOfNumWeek($year, $month, $num, $w);
}
else
$day = $arrTmp[$yearTmp];
if($day < 1) continue;
// 名称取得
$names = explode(',', $names);
$arrTmp = [];
foreach($names as $tmp) {
$tmp = explode(':', $tmp);
if(count($tmp) == 1)
$arrTmp[0] = trim($tmp[0]);
else
$arrTmp[(int) $tmp[0]] = trim($tmp[1]);
}
$yearTmp = 0;
foreach(array_keys($arrTmp) as $tmp)
if($year >= $tmp && $tmp >= $yearTmp) $yearTmp = $tmp;
$holiday[$month][$day] = !isset($holiday[$month][$day]) ?
$arrTmp[$yearTmp] : $holiday[$month][$day]. ', '. $arrTmp[$yearTmp];
}
}
// 国民の休日・振替休日
if($this->useIndefiniteHoliday)
$holiday = $this->indefiniteHoliday($holiday, $year);
foreach($holiday as $k => $v) ksort($holiday[$k]);
$this->result[$year] = $holiday;
}
return $this->resultType ?
$this->convertLinear($year, $this->result[$year]) :
$this->result[$year];
}
/**
* 国民の休日・振替休日
*/
private function indefiniteHoliday($holiday, $year) {
for($month = 1; $month <= 12; $month++) {
// 月末日
$lastDay = (int) $this->dateTime->setDate($year, $month, 1)->format('t');
for($day = 1; $day <= $lastDay; $day++) {
// 前日の月日
$this->dateTime->setDate($year, $month, $day)->modify('-1 day');
$prevMonth = (int) $this->dateTime->format('m');
$prevDay = (int) $this->dateTime->format('d');
// 祝日に挟まれた平日を国民の休日に変更(1986年以降)
if( $year >= 1986 &&
isset($holiday[$prevMonth][$prevDay])
) {
// 翌日の月日
$this->dateTime->setDate($year, $month, $day)->modify('+1 day');
$nextMonth = (int) $this->dateTime->format('m');
$nextDay = (int) $this->dateTime->format('d');
$this->dateTime->setDate($year, $month, $day);
if( isset($holiday[$nextMonth][$nextDay]) &&
!isset($holiday[$month][$day]) &&
(int) $this->dateTime->format('w') !== 0
) {
$holiday[$month][$day] = '国民の休日';
}
}
$this->dateTime->setDate($year, $month, $day);
// 振替休日(1973年4月以降)
if(($year == 1973 && $month >= 4 || $year > 1973) &&
// 祝日かつ日曜
isset($holiday[$month][$day]) &&
(int) $this->dateTime->format('w') === 0
) {
// その日以降の直近の平日を振替休日に
for($i = 1; $i < 7; $i++) {
$this->dateTime->setDate($year, $month, $day)->modify("+$i day");
$m = (int) $this->dateTime->format('m');
$d = (int) $this->dateTime->format('d');
if(!isset($holiday[$m][$d])) {
$holiday[$m][$d] = '振替休日';
break;
}
}
}
}
}
return $holiday;
}
/**
* $year年 $month月 第$num $w曜日に該当する日を返す
*/
private function getDayOfNumWeek($year, $month, $num, $w) {
$firstDayWeek = (int) $this->dateTime->setDate($year, $month, 1)->format('w');
return 1 + ($num - 1) * 7 + (7 + $w - $firstDayWeek) % 7;
}
/**
* YYYY-MM-DDをキーとした連想配列に変換
*/
private function convertLinear($year, $array) {
$arrTmp = [];
foreach($array as $month => $currentMonthData) {
foreach($currentMonthData as $days => $names) {
$arrTmp[sprintf('%04d-%02d-%02d', $year, $month, $days)] = $names;
}
}
return $arrTmp;
}
/**
* 1か月分のリストを返す
*/
public function getHolidayOfMonth($month = 0) {
if($month < 1 || $month > 12) $month = $this->month;
$year = $this->year;
// 該当年の結果が未取得であれば取得
if(!isset($this->result[$year]))
$this->getHolidayOfYear();
if($this->resultType) {
$arrTmp = [];
foreach($this->result[$year][(int) $month] as $days => $names) {
$arrTmp[sprintf('%04d-%02d-%02d', $year, $month, $days)] = $names;
}
return $arrTmp;
} else {
return $this->result[$year][(int) $month];
}
}
/**
* 国民の休日・振替休日 使用フラグ変更
*/
public function setUseIndefiniteHoliday($flg = 1) {
$this->useIndefiniteHoliday = $flg == 1 ? 1 : 0;
// 取得済みの結果をリセット
$this->result = [];
}
/**
* 戻り値形式変更
*/
public function setResultType($flg = 0) {
$this->resultType = $flg == 0 ? 0 : 1;
}
/**
* 年変更
*/
public function setYear($year = 0) {
if($year < 1) $year = (new DateTime())->format('Y');
$this->year = (int) $year;
}
}