PHPでカレンダーを作成してみる③
祝日の判定

こんにちは、ゆんつです。

「PHPでカレンダーを作成してみる」の第3回目です。

第1回目では当月分のカレンダーを作成しました。

PHPでカレンダーを作ってみる① 当月分のカレンダー

第2回目ではカレンダーのリンクから前月のカレンダーや翌月のカレンダーに移動できるように、第1回目に作成したコードに追加をしたり修正したりしました。

PHPでカレンダーを作ってみる② 前月や翌月のカレンダー

そして第3回目の今回はカレンダーの祝日を判定して、祝日に該当する日はデザインを変えて表示したいと思います。

祝日の判定に必要なもの

PHPには祝日に関していろんな処理をすることができる「Yasumi」というライブラリがあります。

自分で祝日を判定するプログラムを書くととても大変ですが、Yasumiを利用することで「その日が祝日かどうか?」の判定が簡単にできるようになります。

Yasumiのインストール

YasumiのインストールにはComposerを利用します。

composer require azuyalabs/yasumi

というコマンドでインストールすることができます。

ゆんつ
簡単!

Yasumiを使ってみる

使い方について簡単に説明しておきます。

まず

require 'vendor/autoload.php';

でオートローダーを読み込んでおきます。

そしてインスタンスを作成します。

インスタンスは

\Yasumi\Yasumi::create('祝日の情報を取得する国名', 対象となる西暦, 祝日を表記する言語);

で作成します

例えば日本の2020年の祝日に関するインスタンスが作成したい場合は

$holidays = \Yasumi\Yasumi::create('Japan', 2020, 'ja_JP');

という感じになります。

このインスタンスに2020年の全祝日の情報が入っているのでforeachで回してみます。

require 'vendor/autoload.php';

$holidays = \Yasumi\Yasumi::create('Japan', 2020, 'ja_JP');

foreach ($holidays as $holiday) {
  echo $holiday . ':' . $holiday->getName() . "\n";
}

※getName()を使うと祝日名を取得できます。

すると以下のような結果となります。

2020年の全祝日が取得されています。

こんな感じでYasumiを使うことで簡単に祝日の日付を取得することができます。

そして肝心の祝日の判定ですが、それにはisHoliday()を使います。

isHolidayの引数に判定の対象となるDateTimeインスタンスを設定すると、その日が祝日かどうかをtrueかfalseで返してくれます。

例えば2020年9月21日は「敬老の日」、2020年9月25日は普通の日です。

この2つの日付を以下のようなコードで祝日判定してみます。

require 'vendor/autoload.php';

$holidays = \Yasumi\Yasumi::create('Japan', 2020, 'ja_JP');

var_dump($holidays->isHoliday(new DateTime('2020-09-21')));
var_dump($holidays->isHoliday(new DateTime('2020-09-25')));

結果は以下のように2020年9月21は祝日なのでtrue、2020年9月25日は普通の日なのでfalseが返ってきます。

このようなisHolidayの仕組みを利用して、祝日の判定を行います。

カレンダーにYasumiを組み込む

実際にカレンダーに「Yasumi」を組み込んでいきます。

以下のようなコードになりました

<?php
/*--------------------------------------
追加部分1
--------------------------------------*/
require 'vendor/autoload.php';
/*--------------------------------------
追加部分1終了
--------------------------------------*/

//現在のDateTimeインスタンスを作成
$today = new DateTime();

//カレンダーの表示月のDatetimeインスタンスを作成
if(isset($_GET['t']) && preg_match('/\A\d{4}-\d{2}\z/', $_GET['t'])) {
  //クエリ情報を基にしてDateTimeインスタンスを作成
  $start_day = new DateTime($_GET['t'] . '-01');
} else {
  //当月初日のDateTimeインスタンスを作成
  $start_day = new DateTime('first day of this month');
}

//カレンダー表示月の前月の年月を取得
$dt = clone($start_day);
$prev_month =  $dt->modify('-1 month')->format('Y-m');

//カレンダー表示月の翌月の年月を取得
$dt = clone($start_day);
$next_month =  $dt->modify('+1 month')->format('Y-m');

//カレンダー表示月の年と月を取得
$year_month = $start_day->format('Y-m');

/*--------------------------------------
追加部分2
--------------------------------------*/
//祝日取得のためにカレンダーの表示月の年を取得
$year = $start_day->format('Y');
/*--------------------------------------
追加部分2終了
--------------------------------------*/

//表示月初日の曜日を数値で取得
$w = $start_day->format('w');

//表示月初日をカレンダーの開始日に変更する
$start_day->modify('-' . $w . ' day');

//表示月末日のDateTimeオブジェクトを作成
$end_day = new DateTime('last day of ' . $year_month);

//カレンダーの終了日を取得するため月末の曜日を数値で取得
$w = $end_day->format('w');

//土曜日を数値にすると6。そこから月末の曜日に対応する数を引いてやれば、カレンダー末尾に追加すべき日数が判明する。
//+1しているのはDatePeriodの特性を考慮するため
$w = 6 - $w + 1;

//月末をカレンダーの終了日の翌日に変更する
$end_day->modify('+' . $w . ' day');

//カレンダーに表示する期間のオブジェクトを作成する
$period = new DatePeriod(
  $start_day,
  new DateInterval('P1D'),
  $end_day
);

/*------------------------------------------------------
追加部分3
------------------------------------------------------*/
//Yasumiを使ってカレンダー表示年の祝日のインスタンスを作成
$holidays = \Yasumi\Yasumi::create('Japan', $year, 'ja_JP');
/*------------------------------------------------------
追加部分3終了
------------------------------------------------------*/

//htmlに描写するための変数
$body = '';

foreach ($period as $day) {
  //当月以外の日付はgreyクラスを付与してCSSで色をグレーにする
  $grey_class = $day->format('Y-m') === $year_month ? '' : 'grey';
  
  //本日にはtodayクラスを付与してCSSで数字の見た目を変える
  $today_class = $day->format('Y-m-d') === $today->format('Y-m-d') ? 'today' : '';
  
  /*------------------------------------------------------
  追加部分4
  ------------------------------------------------------*/
  //祝日ならクラス名にholidayを追加する
  $holiday_class = $holidays->isHoliday($day) ? 'holiday' : '';
  /*------------------------------------------------------
  追加部分4終了
  ------------------------------------------------------*/
  
  //その曜日が日曜日なら<tr>タグを挿入する
  if ($day->format('w') == 0) {
    $body .= '<tr>';
  }
  
  //sprintfを使って整形しながらhtml部分を作成する
  $body .= sprintf(
    '<td class="youbi_%d %s %s %s">%d</td>',
    $day->format('w'),
    $today_class,
    $grey_class,
    /*------------------
    追加部分5
    ------------------*/
    //祝日の場合はクラス名を付与してCSSで文字色を緑にする
    $holiday_class,
    /*------------------
    追加部分5終了
    ------------------*/
    $day->format('d')
  );
  
  //その曜日が土曜日なら</tr>タグを挿入する
  if ($day->format('w') == 6) {
    $body .= '</tr>';
  }
}

?>
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Calendar</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <table class="calendar">
    <thead class="calendar-head">
      <tr class="calendar-row">
        <!-- リンクにクエリ情報を設定する -->
        <th><a href="?t=<?php echo $prev_month ?>">&laquo;</a></th>
        <th colspan="5"><a href=""><?php echo $year_month ?></a></th>
        <th><a href="?t=<?php echo $next_month ?>">&raquo;</a></th>
      </tr>
    </thead>
    <tbody>
      <tr class="calendar-row">
        <td>日</td>
        <td>月</td>
        <td>火</td>
        <td>水</td>
        <td>木</td>
        <td>金</td>
        <td>土</td>
      </tr>
      <?php echo $body ?>
    </tbody>
    <tfoot>
      <tr class="calendar-row">
        <th colspan="7"><a href="./calendar02.php">today</a></th>
      </tr>
    </tfoot>
  </table>
</body>
</html>

「PHPでカレンダーを作ってみた②」のコードに、祝日判定のコードを追加した形となっています。

このHTMLと後述するCSSを組み合わせると、例えば2020年9月のカレンダーは以下のような表示になります。

2020年9月カレンダー

これから、追加部分のコードに絞って解説していきます。

追加部分1について

オートローダーを読み込みます。

/*--------------------------------------
追加部分1
--------------------------------------*/
require 'vendor/autoload.php';

追加部分2について

/*--------------------------------------
追加部分2
--------------------------------------*/
//祝日取得のためにカレンダーの表示月の年を取得
$year = $start_day->format('Y');

ここではYasumiのインスタンスを作成するためにカレンダー表示月の西暦を取得しています。

追加部分3について

/*------------------------------------------------------
追加部分3
------------------------------------------------------*/
//Yasumiを使ってカレンダー表示年の祝日のインスタンスを作成
$holidays = \Yasumi\Yasumi::create('Japan', $year, 'ja_JP');

追加部分2で作成した西暦を使ってYasumiのインスタンスを作成しています。

これによりカレンダー表示年に対応する祝日の情報が入手できました。

追加部分4と5について

foreach ($period as $day) {
  //当月以外の日付はgreyクラスを付与してCSSで色をグレーにする
  $grey_class = $day->format('Y-m') === $year_month ? '' : 'grey';
  
  //本日にはtodayクラスを付与してCSSで数字の見た目を変える
  $today_class = $day->format('Y-m-d') === $today->format('Y-m-d') ? 'today' : '';
  
  /*------------------------------------------------------
  追加部分4
  ------------------------------------------------------*/
  //祝日ならクラス名にholidayを追加する
  $holiday_class = $holidays->isHoliday($day) ? 'holiday' : '';
  /*------------------------------------------------------
  追加部分4終了
  ------------------------------------------------------*/
  
  //その曜日が日曜日なら<tr>タグを挿入する
  if ($day->format('w') == 0) {
    $body .= '<tr>';
  }
  
  //sprintfを使って整形しながらhtml部分を作成する
  $body .= sprintf(
    '<td class="youbi_%d %s %s %s">%d</td>',
    $day->format('w'),
    $today_class,
    $grey_class,
    /*------------------
    追加部分5
    ------------------*/
    //祝日の場合はクラス名を付与してCSSで文字色を緑にする
    $holiday_class,
    /*------------------
    追加部分5終了
    ------------------*/
    $day->format('d')
  );
  
  //その曜日が土曜日なら</tr>タグを挿入する
  if ($day->format('w') == 6) {
    $body .= '</tr>';
  }
}

foreachのループの中で、順番に送られてくるDatetimeインスタンスが祝日かどうかの判定を行い、祝日だった場合は日付の文字を緑色にするためのクラス名を変数に入れてます。

そして変数に代入したクラス名がある場合には、sprintfでタグにそのクラスが設定されるようにしています。

CSS

body {
  font-family: Arial, sans-serif;
  font-size: 14px;
}

a {
  text-decoration: none;
}

table {
  margin: 15px auto;
  border: 1px solid #ddd;
  border-collapse: collapse;
}

th {
  background: #eee;
}

th, td {
  text-align: center;
  padding: 7px;
}

.youbi_0 {
  color: red;
}

.youbi_6 {
  color: blue;
}

.holiday {
  color: green;
}

.today {
  font-weight: bold;
}

.grey {
  color: #dedede;
}

デモページ

以下がデモページとなります。

カレンダー(祝日判定あり)

祝日は緑文字で表示されます。

以上です

というわけでPHPのライブラリを「Yasumi」利用してカレンダーの祝日の判定を行って祝日の日付をほかの日付の色と変更してみました。

これで本当のカレンダーのデザインにぐっと近づいたんじゃないかと思います。

Yasumiは祝日を自分で追加したりすることもできるので、興味がありましたら調べてみるとよいと思います。

祝日の判定もできたので、これで第1回~第3回にわたるカレンダーづくりは一旦おしまいです。

なにか新しい気付きなどがあれば、その都度追記していきたいと思います。

それでは、またー。