LoginSignup
3
2

More than 5 years have passed since last update.

何番煎じかわからないけどPHPでカレンダー

Last updated at Posted at 2013-09-17

 2番煎じどころか20番煎じくらいはいくと思うのですが、適当なライブラリを探すより作った方が早いと思い、作っちゃいました。すみません。
 クラス数が多いですが、データ部分と描画部分(レンダラ)を分けたかったので、こうしました。祝日対応は敢えてしていません。calendar_day_rendererあたりに祝日のリストを持たせてrenderメソッド内で判定すればいいかなと。うるう年については多分strtotime神関数がなんとかしてくれるはず!←2038年問題により32bit環境でアウトでしたw

calendar.php

<?php

class calendar_day
{
    private $date;
    private $day;

    public function __construct( $date )
    {
        $this->date = date( 'Y-m-d', $date );
        $this->day = date( 'j', $date );
    }
    public function getDate()
    {
        return $this->date;
    }
    public function getDay()
    {
        return $this->day;
    }
}
interface calendar_day_renderer
{
    public function render( calendar_day $day );
}
class calendar_day_renderer_html implements calendar_day_renderer
{
    public function render( calendar_day $day )
    {
        echo '<td align="right">' . $day->getDay() . '</td>' . PHP_EOL;
    }
}
class calendar_day_renderer_shell implements calendar_day_renderer
{
    public function render( calendar_day $day )
    {
        echo str_pad($day->getDay(), 2, " ", STR_PAD_LEFT) . '|';
    }
}

class calendar_week
{
    private $no;
    private $week_start_date;
    private $days;

    public function __construct( $no, $week_start_date )
    {
        $this->no = $no;
        $this->week_start_date = date( 'Y-m-d', $week_start_date );

        for($i=0; $i<7; $i++){
            $date = ($i === 0) ? $week_start_date : strtotime( "+{$i} days", $week_start_date );
            $this->days[$i] =new calendar_day( $date );
        }
    }
    public function getNo()
    {
        return $this->no;
    }
    public function getDay( $weekday )
    {
        return isset($this->days[$weekday]) ? $this->days[$weekday] : NULL;
    }
}
interface calendar_week_renderer
{
    public function render( calendar_week $week, calendar_day_renderer $day_renderer );
}
class calendar_week_renderer_html implements calendar_week_renderer
{
    public function render( calendar_week $week, calendar_day_renderer $day_renderer )
    {
        echo '<tr>' . PHP_EOL;
        echo '<td align="center">' . $week->getNo() . ':</td>' . PHP_EOL;
        for($i=0; $i<7; $i++ ){
            $day_renderer->render( $week->getDay($i) );
        }
        echo '</tr>' . PHP_EOL;
    }
}
class calendar_week_renderer_shell implements calendar_week_renderer
{
    public function render( calendar_week $week, calendar_day_renderer $day_renderer )
    {
        echo '|' . $week->getNo() . ':|';
        for($i=0; $i<7; $i++ ){
            $day_renderer->render( $week->getDay($i) );
        }
        echo PHP_EOL;
    }
}

class calendar
{
    private $weeks;
    private $title;

    public function __construct( $year, $month, $title = NULL )
    {
        $this->title = $title ? $title : date('Y/m', strtotime("$year/$month/01"));

        $month_first_day = strtotime("{$year}-{$month}-01");
        $total_days = date( 't', $month_first_day );
        $start_weekday = date( 'w', $month_first_day );

        // 最初の週
        $week_start_date = strtotime( "-{$start_weekday} days", $month_first_day );

        // 月末までの週を作成
        $week_no = 1;
        $next_month_first_day = strtotime("first day of next month", $month_first_day);
        while( $week_start_date < $next_month_first_day ){
            $this->weeks[] = new calendar_week( $week_no, $week_start_date );
            $week_no ++;
            $week_start_date = strtotime( "+7 days", $week_start_date );
        }
    }
    public function getWeek( $index )
    {
        return isset($this->weeks[$index]) ? $this->weeks[$index] : NULL;
    }
    public function countWeeks()
    {
        return count($this->weeks);
    }
    public function getTitle()
    {
        return $this->title;
    }
}
interface calendar_renderer
{
    public function render( calendar $cal );
}
abstract class abstract_calendar_renderer implements calendar_renderer
{
    private $day_renderer;
    private $week_renderer;

    public function __construct( calendar_day_renderer $day_renderer, calendar_week_renderer $week_renderer )
    {
        $this->day_renderer = $day_renderer;
        $this->week_renderer = $week_renderer;
    }
    public function render( calendar $cal )
    {
        $this->renderHeader( $cal );
        $weeks = $cal->countWeeks();
        for( $i=0; $i<$weeks; $i++ ){
            $this->week_renderer->render( $cal->getWeek($i), $this->day_renderer );
        }
        $this->renderFooter( $cal );
    }
    abstract public function renderHeader( calendar $cal );
    abstract public function renderFooter( calendar $cal );
}
class calendar_renderer_html extends abstract_calendar_renderer
{
    public function renderHeader( calendar $cal )
    {
        echo '<table border="1">' . PHP_EOL;
        echo '<caption>' . $cal->getTitle() . '</caption>' . PHP_EOL;
        echo '<tr><th>No</th><th>日</th><th>月</th><th>火</th><th>水</th><th>木</th><th>金</th><th>土</th></tr>' . PHP_EOL;
    }
    public function renderFooter( calendar $cal )
    {
        echo '</table>' . PHP_EOL;
    }
}
class calendar_renderer_shell extends abstract_calendar_renderer
{
    public function renderHeader( calendar $cal )
    {
        echo '--' . $cal->getTitle() . '--' . PHP_EOL;
        echo '|No|日|月|火|水|木|金|土|' . PHP_EOL;
        echo '+--+--+--+--+--+--+--+--+' . PHP_EOL;
    }
    public function renderFooter( calendar $cal )
    {
        echo '+--+--+--+--+--+--+--+--+' . PHP_EOL;
    }
}

// 以下、使用方法

// 2013年9月~11月のカレンダー
$cal_9 = new calendar( 2013, 9 );
$cal_10 = new calendar( 2013, 10 );
$cal_11 = new calendar( 2013, 11 );

// カレンダーをHTMLで描画
$cal_renderer_html = new calendar_renderer_html( new calendar_day_renderer_html, new calendar_week_renderer_html );
$cal_renderer_html->render( $cal_9 );
$cal_renderer_html->render( $cal_10 );
$cal_renderer_html->render( $cal_11 );

// カレンダーをCLIで描画
echo '<pre>' . PHP_EOL;
$cal_renderer_shell = new calendar_renderer_shell( new calendar_day_renderer_shell, new calendar_week_renderer_shell );
$cal_renderer_shell->render( $cal_9 );
$cal_renderer_shell->render( $cal_10 );
$cal_renderer_shell->render( $cal_11 );
echo '</pre>' . PHP_EOL;

// 閏年テスト
$cal_leap_2013 = new calendar( 2013, 2 );   // 今年は平年
$cal_leap_2012 = new calendar( 2012, 2 );   // 去年は閏年
$cal_leap_2100 = new calendar( 2100, 2 );   // 100年で割り切れる年は平年
$cal_leap_2000 = new calendar( 2000, 2 );   // 400で割り切れる年は閏年

echo '<pre>' . PHP_EOL;
$cal_renderer_shell->render( $cal_leap_2013 );
$cal_renderer_shell->render( $cal_leap_2012 );
$cal_renderer_shell->render( $cal_leap_2100 );
$cal_renderer_shell->render( $cal_leap_2000 );
echo '</pre>' . PHP_EOL;

実行結果(HTMLは省略)

<pre>
--2013/09--
|No|日|月|火|水|木|金|土|
+--+--+--+--+--+--+--+--+
|1:| 1| 2| 3| 4| 5| 6| 7|
|2:| 8| 9|10|11|12|13|14|
|3:|15|16|17|18|19|20|21|
|4:|22|23|24|25|26|27|28|
|5:|29|30| 1| 2| 3| 4| 5|
+--+--+--+--+--+--+--+--+
--2013/10--
|No|日|月|火|水|木|金|土|
+--+--+--+--+--+--+--+--+
|1:|29|30| 1| 2| 3| 4| 5|
|2:| 6| 7| 8| 9|10|11|12|
|3:|13|14|15|16|17|18|19|
|4:|20|21|22|23|24|25|26|
|5:|27|28|29|30|31| 1| 2|
+--+--+--+--+--+--+--+--+
--2013/11--
|No|日|月|火|水|木|金|土|
+--+--+--+--+--+--+--+--+
|1:|27|28|29|30|31| 1| 2|
|2:| 3| 4| 5| 6| 7| 8| 9|
|3:|10|11|12|13|14|15|16|
|4:|17|18|19|20|21|22|23|
|5:|24|25|26|27|28|29|30|
+--+--+--+--+--+--+--+--+
</pre>
<pre>
--2013/02--
|No|日|月|火|水|木|金|土|
+--+--+--+--+--+--+--+--+
|1:|27|28|29|30|31| 1| 2|
|2:| 3| 4| 5| 6| 7| 8| 9|
|3:|10|11|12|13|14|15|16|
|4:|17|18|19|20|21|22|23|
|5:|24|25|26|27|28| 1| 2|
+--+--+--+--+--+--+--+--+
--2012/02--
|No|日|月|火|水|木|金|土|
+--+--+--+--+--+--+--+--+
|1:|29|30|31| 1| 2| 3| 4|
|2:| 5| 6| 7| 8| 9|10|11|
|3:|12|13|14|15|16|17|18|
|4:|19|20|21|22|23|24|25|
|5:|26|27|28|29| 1| 2| 3|
+--+--+--+--+--+--+--+--+
--1970/01--
|No|日|月|火|水|木|金|土|
+--+--+--+--+--+--+--+--+
|1:|28|29|30|31| 1| 2| 3|
|2:| 4| 5| 6| 7| 8| 9|10|
|3:|11|12|13|14|15|16|17|
|4:|18|19|20|21|22|23|24|
|5:|25|26|27|28|29|30|31|
+--+--+--+--+--+--+--+--+
--2000/02--
|No|日|月|火|水|木|金|土|
+--+--+--+--+--+--+--+--+
|1:|30|31| 1| 2| 3| 4| 5|
|2:| 6| 7| 8| 9|10|11|12|
|3:|13|14|15|16|17|18|19|
|4:|20|21|22|23|24|25|26|
|5:|27|28|29| 1| 2| 3| 4|
+--+--+--+--+--+--+--+--+
</pre>

使い方はcalendarオブジェクトとrendererオブジェクトを作って、renderすればOKです。xxx_renderer_htmlはrenderするとHTMLを出力し、xxx_renderer_shellはrenderするとコンソールに文字として出力します。

実行結果ですが、32ビット版PHPでは閏年に対応してません!(笑
これは、strtotimeの制限によるもので、マニュアルの注意事項にひっそりと書かれています。

PHPマニュアルのstrtotimeの項:
http://www.php.net/manual/ja/function.strtotime.php

2038年以降も32bitマシンを現役で動かしちゃうよ!ってエコな方は、PHP5.2から使用できるDateTimeを使ってカスタマイズすればいけると思います。

参考:
http://blog.asial.co.jp/704

補足:
strtotimeといえば冒頭で神関数とか言っちゃってますが、いろいろ問題もあるようです。

http://itpro.nikkeibp.co.jp/article/COLUMN/20070131/260224/
http://www3441ui.sakura.ne.jp/wordpress/?p=177
http://www.ahsd.net/phpstrtotime%E3%81%A7%E3%81%AE%E5%89%8D%E6%9C%88%E3%81%AE%E6%97%A5%E4%BB%98%E5%8F%96%E5%BE%97%E3%81%AF%E4%B8%8A%E6%89%8B%E3%81%8F%E3%81%84%E3%81%8B%E3%81%AA%E3%81%84/

strtotimeの使用は、使用上の注意をよく読み、用法・用量を守って正しくお使いください!

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2