<?php
/*
 * ZendX_Date_Holiday
 *
 * 2007年以降の日本の祝日を判定します
 * 
 * 2006年より過去に遡った調査をする場合は要調整（5/4が国民の休日になる）
 * wikipedia(http://ja.wikipedia.org/wiki/国民の祝日)とかで確認して$ddやkokumin()、spring()、autumn()辺りを調整してください
 *
 * @cateogry ZendX
 * @package  ZendX_Date
 * @author misney <mizuno@ninja.co.jp> 
 */
class ZendX_Date_Holiday {

	/**
	 * 平日かな？
	 *
	 *	@return boolean
	 */
	public static function isWeekday($y, $m, $d) {
		if (self::isHoliday($y, $m, $d)) {
			return false;
		} else {
			return true;
		}
	}

	/**
	 * 祝日かな？
	 *
	 *	@return boolean
	 */
	public static function isHoliday($y, $m, $d) {
		$t = mktime(0,0,0,$m,$d,$y);
		$w = date("w", $t);
		$wc = intval($d / 7) + 1;

		//祝日定義
		$dd = array(
			1=>array(1=>true, 'function'=>array('monday2')),
			2=>array(11=>true),
			3=>array('function'=>array('spring')),
			4=>array(29=>true),
			5=>array(3=>true, 4=>true, 5=>true),
			7=>array('function'=>array('monday3')),
			9=>array('function'=>array('autumn', 'monday3', 'kokumin')),
			10=>array('function'=>array('monday2')),
			11=>array(3=>true, 23=>true),
			12=>array(23=>true),
		);

		//山の日は2016年から
		if ($y >= 2016) {
			$dd[8] = array(11=>true);
		}

		if (!empty($dd[$m][$d])) {//決められた祝日
			return true;
		}

		//ハッピーマンデイや春秋分、国民の休日がある月は関数処理
		if(!empty($dd[$m]['function'])) {
			foreach ($dd[$m]['function'] as $func) {
				if (self::$func($y, $m, $d, $w, $wc, $t)) {
					return true;
				}
			}
		}

		//水曜日までは振替休日をチェック。
		if ($w < 4) {
			$_w = $w;
			$_y = $y;
			$_m = $m;
			$_d = $d;
			while($_w > 0) {
				//１日の場合は前月末日を作成する
				if ($d == 1) {
					$t = $t - 86400;
					$_y = date("Y", $t);
					$_m = date("m", $t);
					$_d = date("d", $t);
				} else {
					$_y = $_y;
					$_m = $_m;
					$_d = $_d - 1;
				}

				if (self::isHoliday($_y, $_m, $_d)) {
					$_w--;
					if ($_w == 0) {
						return true;
					}
				} else {
					break;
				}
			}
		}
		return false;
	}

	/**
	 * ハッピーマンデーかな？（第2月曜日）
	 *
	 *	@return boolean
	 */
	private static function monday2($y, $m, $d, $w, $wc, $t) {
		if ($w == 1 && $wc == 2) {
			return true;
		}
		return false;
	}

	/**
	 * ハッピーマンデーかな？（第3月曜日）
	 *
	 *	@return boolean
	 */
	private static function monday3($y, $m, $d, $w, $wc, $t) {
		if ($w == 1 && $wc == 3) {
			return true;
		}
		return false;
	}

	/**
	 * 春分の日かな？
	 *
	 * 前年２月の最初の平日に制定されるらしい。(官報に掲載される暦要項）
	 * 地球の運行状態が変化した場合などに変更される可能性はあるらしい（過去には無い）
	 *
	 *	@return boolean
	 */
	private static function spring($y, $m, $d, $w, $wc, $t) {
		$p = $y % 4;
		switch($p) {
			case '1':
					$_d = '20';
				break;
			case '2':
				if ($y < 2023) {
					$_d = '21';
				} else {
					$_d = '20';
				}
				break;
			case '3':
				if ($y < 2056) {
					$_d = '21';
				} else {
					$_d = '20';
				}
				break;
			default:
				if ($y < 2089) {
					$_d = '20';
				} else {
					$_d = '19';
				}
				break;
		}
		if ($d == $_d) {
			return true;
		}
		return false;
	}


	/**
	 * 秋分の日かな？
	 *
	 * 前年２月の最初の平日に制定されるらしい。(官報に掲載される暦要項）
	 * 地球の運行状態が変化した場合などに変更される可能性はあるらしい（過去には無い）
	 *
	 *	@return boolean
	 */
	private static function autumn($y, $m, $d, $w, $wc, $t) {
		$p = $y % 4;

		switch($p) {
			case '1':
				if ($y < 2042) {
					$_d = '23';
				} else {
					$_d = '22';
				}
				break;
			case '2':
				if ($y < 2075) {
					$_d = '23';
				} else {
					$_d = '22';
				}
				break;
			case '3':
				$_d = '23';
				break;
			default:
				if ($y < 2009) {
					$_d = '23';
				} else {
					$_d = '22';
				}
				break;
		}

		if ($d == $_d) {
			return true;
		}
		return false;
	}

	/**
	 * 国民の休日かな？
	 *
	 * 祝日と祝日に挟まれた日は祝日というオセロのような祝日。
	 * 2013年現在は9月21日or22日の火曜日にしか発生しない。
	 *
	 *	@return boolean
	 */
	private static function kokumin($y, $m, $d, $w, $wc, $t) {
		if (($d == 21 || $d == 22) && $w == 2) {
			if (self::isHoliday($y, $m, $d-1, $w-1, $wc, $t) && self::isHoliday($y, $m, $d+1, $w+1, $wc, $t)) {
				return true;
			}
		}
		return false;
	}

}