n日後の営業日を取得する


祝日・振替休日を取得するには `PEAR::Date_Holidays_Japan` にお任せする。

インストールは以下のコマンドで。

```sh
# pear install Date_Holidays_Japan-alpha
```

Calendar クラスの実装は以下のように。

```php:Calendar.class.php
<?php

/**
 * カレンダークラス
 * Calendar.class.php
 */
final class Calendar
{

    /**
     * 日曜日
     */
    const T_SUNDAY = 0;

    /**
     * 月曜日
     */
    const T_MONDAY = 1;

    /**
     * 火曜日
     */
    const T_TUESDAY = 2;

    /**
     * 水曜日
     */
    const T_WEDNESDAY = 3;

    /**
     * 木曜日
     */
    const T_THURSDAY = 4;

    /**
     * 金曜日
     */
    const T_FRIDAY = 5;

    /**
     * 土曜日
     */
    const T_SATURDAY = 6;

    /**
     * キャッシュに使用する祝日配列
     * @var array(YYYY => array('YYYY-mm-dd' => '祝日名'))
     */
    static private $_arrHolidays = array();

    /**
     * 祝日を取得する
     * @param integer $year
     * @return array
     */
    private static function getHolidays($year)
    {
        // 取得済みならキャッシュを使う
        if (isset(static::$_arrHolidays[$year])) {
            return static::$_arrHolidays[$year];
        }

        // 未取得のとき
        $errorLevel = error_reporting(); // エラー出力レベルを取得
        error_reporting(E_NOTICE); // PHP Deprecated: Non-static method... を抑止

        require_once 'Date/Holidays.php';
        $dh = Date_Holidays::factory('Japan', $year, 'ja_JP');
        $holidays = array();
        foreach ($dh->getHolidays() as $h) {
            $holidays[$h->getDate()->format('%Y-%m-%d')] = $h->getTitle();
        }
        error_reporting($errorLevel); // エラー出力レベルを戻す

        ksort($holidays);

        static::$_arrHolidays[$year] = $holidays;
        return static::$_arrHolidays[$year];
    }

    /**
     * 祝日かどうかを判定する
     * @param \DateTime $objDate 判定する日付
     * @return bool 祝日ならtrue
     */
    public static function isHoliday(\DateTime $objDate)
    {
        $year = $objDate->format('Y');
        $holidays = self::getHolidays($year);
        return array_key_exists($objDate->format('Y-m-d'), $holidays);
    }

    /**
     * 定休日かどうかを判定する
     * @param \DateTime $objDate 判定する日付
     * @return bool 定休日ならtrue
     */
    public static function isRegularHoliday(\DateTime $objDate)
    {
        //定休日リスト
        $arrRegularHoliday = array(
            self::T_SUNDAY
            , self::T_SATURDAY
        );
        return in_array($objDate->format('w'), $arrRegularHoliday);
    }

    /**
     * ○日後の営業日を取得する
     * @param integer $dateAfter 指定しない時は翌営業日を返す
     * @param \Datetime $objDate 基準日
     * @return \Datetime
     */
    public static function getWeekdayAfter($dateAfter = 1, \DateTime $objDate = null)
    {
        $date = (is_null($objDate)) ? new \DateTime() : $objDate;
        $interval = new \DateInterval("P1D");
        for ($i = 0; $i < $dateAfter; $i++) {
            $date->add($interval);

            //    定休日・祝日のときはカウントしないので、$dateAfterをインクリメント
            if (self::isRegularHoliday($date) || self::isHoliday($date)) {
                $dateAfter++;
            }
        }
        return $date;
    }

}

```

利用する時はこんな感じ。

```php
<?php

require 'Calendar.class.php';

// usage
// 2015-10-10(Friday) -> 2015-10-12(Monday) 体育の日
$res = Calendar::getWeekdayAfter(1, new \DateTime('2015-10-10'));
var_dump($res); // 2015-10-13

// 2015-05-01(金)
// 2015-05-02(土)
// 2015-05-03(日・憲法記念日)
// 2015-05-04(みどりの日)
// 2015-05-05(こどもの日)
// 2015-05-06(振替休日)
$res = Calendar::getWeekdayAfter(1, new \DateTime('2015-05-01'));
var_dump($res); // 2015-05-07
```