SOFTELメモ

Softel Inc.

【php】カレンダーを表示したかったら こうしたらいいんじゃないか の例

問題

phpでカレンダーの表示をしたいのですが、日付判定、週末判定、for文、while文の嵐で壮大なプログラムになってしまいます。

なにげに難しいです。。。 (p_q)

解答例

phpの場合、便利なライブラリがあれば… なんて考えない方が早いと思う。php自体が関数盛りだくさんのライブラリみたいなものなので、標準の関数だけでほとんどのことができてしまう。

では、phpで書くときはどうするか。

ゴールから考えてみる。

目標設定

カレンダーをtableで表示する場合、週の繰り返しと、週の中の日の繰り返しがある。

それなら、このようなプログラムにできれば、きれいかな。

//外側のtableタグ
echo '<table>';
//見出し
echo '<tr><th>日</th><th>月</th><th>火</th><th>水</th><th>木</th><th>金</th><th>土</th></tr>';
//週のループ
foreach ($data as $week) {
    echo '<tr>';
    //週の中の日のループ
    foreach ($week as $date) {
        echo '<td>' . $date . '</td>';
    }
    echo '</tr>';
}
echo '</table>';

↑ そうするには?

こんなデータがあればいいだろう。

//例)2012年5月の場合(空文字''はカレンダー表示したときの余白を表す)
$data = array(
    array('', '', 1, 2, 3, 4, 5),
    array(6, 7, 8, 9, 10, 11, 12),
    array(13, 14, 15, 16, 17, 18, 19),
    array(20, 21, 22, 23, 24, 25, 26),
    array(27, 28, 29, 30, 31, '', ''),
);

↑ そうするには?

こうすると作れる。日付の一覧と、その前後に適切な余白の分をつけた配列を、7で割る。

//例)2012年5月の場合
//日付の一覧を
$data = array('', '', 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, '', '');
//1週間の日数の7で割る
$data = array_chunk($data, 7);

↑ そうするには?

日付の一覧があれば、前と後ろに余白をつけるのは難しくない。

phpでは配列操作の関数が70個以上あるという充実っぷり。やりたいことができる関数が探せば見つかる。

関数が見つからなくても、date(‘w’)で曜日が数字で取得できるのだから、あとは力技でやったっていい。

//例)2012年5月の場合
//日付の一覧をがあるとして
$data = array(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);
//2012-05-01の曜日を見て前に余白を追加
if (date('w', strtotime('2012/05/01')) > 0) {
    $data = array_merge(array_fill(0, date('w', strtotime('2012/05/01')), ''), $data);
}
//2012/05/31の曜日を見て後ろに余白を追加
if (date('w', strtotime('2012/05/31')) < 6) {
    $data = array_merge($data, array_fill(0, 6 - date('w', strtotime('2012/05/31')), ''));
}

↑ そうするには?

日付の一覧を作ろう。

ここまでやってみて、日付の一覧は array(1,2,3…) ではなくて、array(その月の1日のタイムスタンプ, その月の2日のタイムスタンプ, その月の3日のタイムスタンプ…) がよさそうだと考え直す。

ということで、指定した年月の日付のタイムスタンプの一覧を作るプログラムを作ってみる。

60*60*24 とは1日の秒数のことで、これを足せば1日ずつ進んでいく。

//例)2012年5月の場合
$ymd = '2012-05-01';
//開始日のタイムスタンプ
$s = strtotime($ymd);
//日付リストを作る
//月が同じ日の日付のタイムスタンプを配列にしまう
$data = array();
for ($t = $s; date('m', $t) == date('m', $s); $t += 60*60*24) {
    $data[] = $t;
}

↓ということは?

以上を組み合わせるとこうなる。

<?php
//例)2012年5月の場合
$ymd = '2012-05-01';
//開始日のタイムスタンプ
$s = strtotime($ymd);
//日付リストを作る
$data = array();
for ($t = $s; date('m', $t) == date('m', $s); $t += 60*60*24) {
    $data[] = $t;
}
//先頭の日の曜日を見て前に余白を追加
if (date('w', $data[0]) > 0) {
    $data = array_merge(array_fill(0, date('w', $data[0]), ''), $data);
}
//末尾の日の曜日を見て後ろに余白を追加
if (date('w', end($data)) < 6) {
    $data = array_merge($data, array_fill(0, 6 - date('w', end($data)), ''));
}
//7で割る
$data = array_chunk($data, 7);

//以上でデータは出来上がった

//カレンダーを表示する
echo '<table>';
//見出し
echo '<tr><th>日</th><th>月</th><th>火</th><th>水</th><th>木</th><th>金</th><th>土</th></tr>';
//週のループ
foreach ($data as $week) {
    echo '<tr>';
    //週の中の日のループ
    foreach ($week as $date) {
        if ($date) {
            echo '<td>' . date('j', $date) . '</td>';
        } else {
    	    echo '<td>&#160;</td>';
        }
    }
    echo '</tr>';
}
echo '</table>';

実行するとこうなる。

カレンダー表示ができた♪

補足

今回はタイムスタンプを使用した。

人の誕生日や遠い未来の日付を扱うカレンダーの場合は、タイムスタンプだと問題が発生するので、日付の計算のところをカレンダー関数などでおこなうのがよい。

あくまでもひとつの例なので、場合に応じてプログラムを書くとよい。

日曜日が先頭じゃなくて、土日は右にしたいと言われたら?など考えてみるとよい。

関連するメモ

コメント