PHP+HTMLで基本的なカレンダーを作る
予約日の日付取得が必要で、HTMLのinput type="date" でカレンダーを作っていたのですが、急遽、土日祝日をクリックできない仕様にしてくださいと言われました。
▼HTMLのinput type="date"で作るカレンダー
HTMLのdateはカレンダー表示が簡単にできて良かったのですが、土日祝日除外設定ができない(多分)ので、自作して作りました。
似たような案件が来た時のために、作り方を書いておこうと思います。
カレンダー制作の解説記事として書いているのでかなり冗長な記事になっていますが、仕組みをわかりやすく解説したいと思いますのでご容赦いただけますと幸いです。
カレンダーの仕組みを考える
最初は「PHP カレンダー」でググってたんですが、解説読むのがめんどくさくなって、自分で考えました。
必要なものは、月末の日付と曜日の取得、それと祝日の月日があれば、なんとかなるだろうと。
月初は必ず1日から始まるので特に問題はないのですが、月末の日にちは月によって異なるので、注意が必要です。
祝日は内閣府に祝日のCSVがあるのでそれを読み込んで使います。
月末の日付取得は date関数の「t」でとれる
echo '今月の末日は'.date("t").'日です';
▼実行結果
今月の末日は31日です
曜日はdate関数の「w」でとれる
echo '今日の曜日は「'.date("w").'」です';
▼実行結果
今日の曜日は「6」です
曜日を返すdate("w")は、0~6の数字が返ってきます。
0が日曜から始まり、「日月火水木金土」と、~6の土曜日で終わります。
例題は日付指定をしてないので、実行した日の曜日が数字で返ってきます。
この数字に日~土までの対応をした配列を作成し、Keyから曜日を表示させるという寸法です。
$week = array('日','月','火','水','木','金','土'); echo '今日の曜日は「'.$week[date("w")].'曜日」です';
今日の曜日は「土曜日」です
基本的な作りはこれだけで作れます。
とりあえず月初~月末までをPHPで表示してみる
1日~月末までの日付を表示させる仕組みを考えます。
for文で1から初めて、月末の日付で終わらせるのが思いつきます。
for($i=1; $i<=date("t"); $i++){ echo $i.','; }
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
これで日付表示はできました。
次にこれを表組にして考えていきます。
HTMLのTableタグを使って表にしていきます。
HTMLのtableを使って表に仕上げていく
必要最低限なカレンダーをひとまず作ってみます。
PHPで作ったカレンダー表示
2024年10月 | ||||||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
PHP + HTML のソース
一旦、プログラム全体を出します。
$week = array('日','月','火','水','木','金','土'); $now_month = date("Y年n月"); //表示する年月 $start_date = date('Y-m-01'); //開始の年月日 $end_date = date("Y-m-t"); //終了の年月日 $start_week = date("w",strtotime($start_date)); //開始の曜日の数字 $end_week = 6 - date("w",strtotime($end_date)); //終了の曜日の数字 echo '<table class="cal">'; //該当月の年月表示 echo '<tr>'; echo '<td colspan="7" class="center">'.$now_month.'</td>'; echo '</tr>'; //曜日の表示 日~土 echo '<tr>'; foreach($week as $key => $youbi){ if($key == 0){ //日曜日 echo '<th class="sun">'.$youbi.'</th>'; }else if($key == 6){ //土曜日 echo '<th class="sat">'.$youbi.'</th>'; }else{ //平日 echo '<th>'.$youbi.'</th>'; } } echo '</tr>'; //日付表示部分ここから echo '<tr>'; //開始曜日まで日付を進める for($i=0; $i<$start_week; $i++){ echo '<td></td>'; } //1日~月末までの日付繰り返し for($i=1; $i<=date("t"); $i++){ $set_date = date("Y-m",strtotime($start_date)).'-'.sprintf("%02d",$i); $week_date = date("w", strtotime($set_date)); //土日で色を変える if($week_date == 0){ //日曜日 echo '<td class="sun">'.$i.'</td>'; }else if($week_date == 6){ //土曜日 echo '<td class="sat">'.$i.'</td>'; }else{ //平日 echo '<td>'.$i.'</td>'; } if($week_date == 6){ echo '</tr>'; echo '<tr>'; } } //末日の余りを空白で埋める for($i=0; $i<$end_week; $i++){ echo '<td></td>'; } echo '</tr>'; echo '</table>';
カレンダー表示のPHP 解説
サンプルプログラムの解説をしていきます。
変数の設定
カレンダーで使う変数を最初に定義しています。
$week = array('日','月','火','水','木','金','土'); $now_month = date("Y年n月"); //表示する年月 $start_date = date('Y-m-01'); //開始の年月日 $end_date = date("Y-m-t"); //終了の年月日 $start_week = date("w",strtotime($start_date)); //開始の曜日の数字 $end_week = 6 - date("w",strtotime($end_date)); //終了の曜日の数字
$week は曜日表示用の配列です。
$now_monthはカレンダーの一番上に表示する年月の表示用。
$start_date は現在の年月に-1、マイナスではなく、ハイフンを付けたものです。
$end_date は月の終わりの日付を取得しています。
$start_weekは月の始まりの曜日を取得。
カレンダーの始まりの曜日より前に、空の<td>を生成するため。
$end_week は月の終わりの曜日を取得し、6からその数を引いています。
これは月終りの空の<td>を生成するためです。
曜日設定をforeachで作る
カレンダーの年月表示はそのままなので割愛して、曜日表示の箇所です。
//曜日の表示 日~土 echo '<tr>'; foreach($week as $key => $youbi){ if($key == 0){ //日曜日 echo '<th class="sun">'.$youbi.'</th>'; }else if($key == 6){ //土曜日 echo '<th class="sat">'.$youbi.'</th>'; }else{ //平日 echo '<th>'.$youbi.'</th>'; } } echo '</tr>';
日~土までの曜日が入った配列 $week を foreachで展開しています。
配列の添え字($key)には0~6までの数字が入っています。
その数字が0の場合は日曜日、6の場合は土曜日の表示用のclassを設定して、条件分岐しています。
分岐の数が多い場合はswitch文の方がおすすめですが、2個くらいならif文でもいいかなと。
どっちでも好きな方でいいと思います。
月頭の曜日まで空のtdで埋める
カレンダーの開始です。
日曜からセルがあるので、開始の曜日まで空白のtdを追加する処理です。
//日付表示部分ここから echo '<tr>'; //開始曜日まで日付を進める for($i=0; $i<$start_week; $i++){ echo '<td></td>'; }
$start_weekには開始曜日の数字が0~6で入ります。
月曜日開始の場合は「1」がはいるので、for文が1回、土曜日の場合は「6」が入り、これにより開始曜日までに空白のセルが生成されます。
1日から月末までを表示させる
//1日~月末までの日付繰り返し for($i=1; $i<=date("t"); $i++){ $set_date = date("Y-m",strtotime($start_date)).'-'.sprintf("%02d",$i); $week_date = date("w", strtotime($set_date)); //土日で色を変える if($week_date == 0){ //日曜日 echo '<td class="sun">'.$i.'</td>'; }else if($week_date == 6){ //土曜日 echo '<td class="sat">'.$i.'</td>'; }else{ //平日 echo '<td>'.$i.'</td>'; } if($week_date == 6){ echo '</tr>'; echo '<tr>'; } }
カレンダーは必ず1日から始まるので、for文の開始は「1」で固定できます。
末日だけ、月によって異なるので、date関数の「t」で取得します。
for($i=1; $i<=date("t"); $i++){
これで日付は末日まで生成されますが、曜日はその日ごとに取得する必要があるので、date関数で曜日を取得しています。
$start_dateにはカレンダーの開始年月日が入っています。
それをdate関数を使って、for文のi日に変換しています。
$start_date = date('Y-m-01'); //開始の年月日 $set_date = date("Y-m",strtotime($start_date)).'-'.sprintf("%02d",$i);
date関数の第二引数に変換したい日時を指定してYYYY-mmという年月を取得して、'-'.$iで「YYYY-mm-dd」という日付フォーマットにしています。
その作った日付を元に曜日を取得すると。
こんなめんどくさいことしなくても取れると思うんですが、可読性重視と言うことで。
$week_date = date("w", strtotime($set_date));
この取得した曜日の数字を$week_date に代入して、その数字に応じてif文でクラスを設定しています。
//土日で色を変える if($week_date == 0){ //日曜日 echo '<td class="sun">'.$i.'</td>'; }else if($week_date == 6){ //土曜日 echo '<td class="sat">'.$i.'</td>'; }else{ //平日 echo '<td>'.$i.'</td>'; }
週末で改行<tr>させる
曜日の数字が「6」の場合、土曜日なので、土曜日になったら<tr>の改行を閉じて、次の改行を開始します。
if($week_date == 6){ echo '</tr>'; echo '<tr>'; }
これで、月初から月末までの表示が完成です。
なんですが、一応土曜日で終わっていない場合は、空の<td>を追加して完了します。
終りの空<td>を追加
末日までの空白を<td>で埋めます。
//末日の余りを空白で埋める for($i=0; $i<$end_week; $i++){ echo '<td></td>'; }
$end_weekには最初に定義した、6から末日の曜日の数字を引いた数値が入っています。
$end_week = 6 - date("w",strtotime($end_date)); //終了の曜日の数字
画像の例だと2021年2月の末日の2/28が日曜日なので「0」、
6 - 0 = 6 が入っています。
なので、月曜日~土曜日までの空白<td>が6回入るという仕組みです。
最後に改行とtableを閉じて完了!
echo '</tr>'; echo '</table>';
祝日を読み込んで対応
ここまでが基本的なカレンダーの作り方です。
最後に祝日表示の機能を追加していきます。
祝日を内閣府から取得する
祝日のめんどくさいところは、規則性がありそうでないところです。
ハッピーマンデーがあるので、固定の月日では設定できないし、突然増えたりもします。
なので、祝日の日付の入ったCSVを内閣府からゲットしてそれを読み込んで使います。
↑のページの中段くらいに祝日CSVがあるのでそれをダウンロードします。
2021年2月現在、2022年までの祝日があります。
2023年になったらまた取得してこのCSVを更新しないといけないわけですね。めんどくさいよね。
祝日のcsvを展開して配列にいれる
取得したCSVの文字コードをHTMLの文字コードとそろえておきましょう。
ダウンロードした状態だとShift-JISになっているので、私はTerapadでUTF-8nに変換しています。
//CSVファイルの読み込み $file = new SplFileObject("syukujitsu.csv"); $file->setFlags(SplFileObject::READ_CSV); $syuku_array = array(); foreach ($file as $line) { //▼祝日の名前がない場合は無視(空行を読み込まない処理) if(isset($line[1])){ //$line[0]に日付がはいっている $date = date("Y-m-d",strtotime($line[0])); //$line[1]に祝日の名前が入っている $name = $line[1]; $syuku_array[$date] = $name; } }
SplFileObjectというPHPに組み込まれているモジュールを使ってCSVを読み込みます。
読み込んだCSVを foreach で1行単位で処理していきます。
最後の1行の空行を読まないように、「isset」を使って、祝日の名前がない場合は処理をしないようにしています。
if(isset($line[1])){
これで、配列$syuku_arrayには、keyに日付(YYYY-mm-dd)が入り、中身に祝日の名前が入りました。
祝日の名前は使わないのでなくてもいいのですが、せっかくなのでいれておきました。
祝日の日付があったら祝日のクラスを適用する
祝日の配列を作ったら、作ってあった土日祝日設定のところにif文を追加しましょう。
for($i=1; $i<=date("t"); $i++){ $set_date = date("Y-m",strtotime($start_date)).'-'.sprintf("%02d",$i); $week_date = date("w", strtotime($set_date)); //土日で色を変える if($week_date == 0){ //日曜日 echo '<td class="sun">'.$i.'</td>'; }else if($week_date == 6){ //土曜日 echo '<td class="sat">'.$i.'</td>'; }else if(array_key_exists($set_date,$syuku_array)){ //祝日 echo '<td class="sun">'.$i.'</td>'; }else{ //平日 echo '<td>'.$i.'</td>'; } if($week_date == 6){ echo '</tr>'; echo '<tr>'; } }
for文でYYYY-mm-dd の日付が回ってきますので、その日付が祝日の配列にあったら祝日のクラスを当てます。
array_key_existsは配列のkeyに指定した文字があるかどうかをチェックしてくれます。
$set_dateは、日付が代入されています。
}else if(array_key_exists($set_date,$syuku_array)){ //祝日 echo '<td class="sun">'.$i.'</td>';
祝日設定をいれたカレンダー完成!
これで祝日表示がされるカレンダーが完成しました。
2024年10月 | ||||||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
最終的なPHPとHTMLのソース。
//祝日の読み込み $file = new SplFileObject("img/syukujitsu.csv"); $file->setFlags(SplFileObject::READ_CSV); $syuku_array = array(); foreach ($file as $line) { if(isset($line[1])){ $date = date("Y-m-d",strtotime($line[0])); $name = $line[1]; $syuku_array[$date] = $name; } } $week = array('日','月','火','水','木','金','土'); $now_month = date("Y年n月"); //表示する年月 $start_date = date('Y-m-01'); //開始の年月日 $end_date = date("Y-m-t"); //終了の年月日 $start_week = date("w",strtotime($start_date)); //開始の曜日の数字 $end_week = 6 - date("w",strtotime($end_date)); //終了の曜日の数字 echo '<table class="cal">'; //該当月の年月表示 echo '<tr>'; echo '<td colspan="7" class="center">'.$now_month.'</td>'; echo '</tr>'; //曜日の表示 日~土 echo '<tr>'; foreach($week as $key => $youbi){ if($key == 0){ //日曜日 echo '<th class="sun">'.$youbi.'</th>'; }else if($key == 6){ //土曜日 echo '<th class="sat">'.$youbi.'</th>'; }else{ //平日 echo '<th>'.$youbi.'</th>'; } } echo '</tr>'; //日付表示部分ここから echo '<tr>'; //開始曜日まで日付を進める for($i=0; $i<$start_week; $i++){ echo '<td></td>'; } //1日~月末までの日付繰り返し for($i=1; $i<=date("t"); $i++){ $set_date = date("Y-m",strtotime($start_date)).'-'.sprintf("%02d",$i); $week_date = date("w", strtotime($set_date)); //土日で色を変える if($week_date == 0){ //日曜日 echo '<td class="sun">'.$i.'</td>'; }else if($week_date == 6){ //土曜日 echo '<td class="sat">'.$i.'</td>'; }else if(array_key_exists($set_date,$syuku_array)){ //祝日 echo '<td class="sun">'.$i.'</td>'; }else{ //平日 echo '<td>'.$i.'</td>'; } if($week_date == 6){ echo '</tr>'; echo '<tr>'; } } //末日の余りを空白で埋める for($i=0; $i<$end_week; $i++){ echo '<td></td>'; } echo '</tr>'; echo '</table>';
今時カレンダーを作ることもあんまりないかと思いますが、PHPで作るといろいろと応用が利くので覚えておくと便利ではあります。
使う人いるかわかりませんが、一応、使っているスタイルシートを表示して終わります!
table.cal { border-spacing: 0; border-collapse: collapse; color: #555; width: 100%; } .cal th { background-color: #f5f5f5; text-align: center; padding: 10px 0 10px 6px; border-top: 1px solid #aaa; } .cal td{ text-align: center; border-top: 1px solid #aaa; padding: 10px 0 10px 6px; } .sun { color: #cc0000; } .sat { color: #0000cc; }
次は作ったカレンダーにjQueryを使って日付取得ができるようにしていこうと思います。
jQueryを使った日付取得はこちら
この記事の続きはこちらです