Marc Ermshaus’ avatar

Marc Ermshaus

Linkblog

PHP: A helper function for rendering calendar table layouts

Published on 13 May 2010. Tagged with programming, php, snippet.

/**
 * Returns the table layout of a given month as an array of rows.
 * 
 * <p>Padding will be added as necessary.</p>
 *
 * <p>Example: Return value for May 2010</p>
 *
 * <pre>
 * array(
 *     array(null, null, null, null, null,    1,    2),
 *     array(   3,    4,    5,    6,    7,    8,    9),
 *     array(  10,   11,   12,   13,   14,   15,   16),
 *     array(  17,   18,   19,   20,   21,   22,   23),
 *     array(  24,   25,   26,   27,   28,   29,   30),
 *     array(  31, null, null, null, null, null, null)
 * )
 * </pre>
 *
 * @param   int $year         Year to generate the layout of.
 * @param   int $month        Month to generate the layout of.
 * @param   int $firstWeekday Weekday of first column. Works like date('w').
 *                            Defaults to monday.
 * @throws  IllegalArgumentException
 * @author  Marc Ermshaus <marc@ermshaus.org>
 * @version 2010-05-13
 * @example http://ermshaus.org/2010/05/php-a-helper-function-for-rendering-calendar-table-layouts
 * @return  array
 */
function getMonthLayout($year, $month, $firstWeekday = 6)
{
    $year         = (int) $year;
    $month        = (int) $month;
    $firstWeekday = (int) $firstWeekday;

    if ($month < 1 || $month > 12) {
        throw new InvalidArgumentException(
                '$month has to be between 1 and 12.');
    }

    if ($firstWeekday < 0 || $firstWeekday > 6) {
        throw new InvalidArgumentException(
                '$firstWeekday has to be between 0 and 6.');
    }

    $data = array();
    
    $dt = new DateTime($year . '-' . $month . '-01');

    $t = $dt->format('t');
    $w = $dt->format('w');
    $w = ($w + $firstWeekday) % 7;

    // Add padding values to first row
    if ($w == 0) {
        $row = array();
    } else {
        $row = array_fill(0, $w, null);
    }

    for ($i = 1; $i <= $t; $i++) {
        $row[] = $i;
        if (($i + $w) % 7 == 0) {
            $data[] = $row;
            $row = array();
        }
    }

    // Add padding values to last row
    $k = (7 - (($t + $w) % 7)) % 7;

    if ($k > 0) {
        $row =  array_merge($row, array_fill(0, $k, null));
        $data[] = $row;
    }

    return $data;
}

The following example renders all months from the year 2010.

<?php

$year  = 2010;
$shift = 6; // Set first column to monday

$days = array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA');

// Ring shift weekday names according to $shift parameter
for ($i = 0; $i < $shift; $i++) {
    $popped = array_pop($days);
    array_unshift($days, $popped);
}

$date = new DateTime();

?>

<?php for ($month = 1; $month <= 12; $month++):

    $data = getMonthLayout($year, $month, $shift);
    $date->setDate($year, $month, 1);
    echo '<p>' . $date->format('F Y') . '</p>';
?>

<table border="1">
<tr>
    <th>
        <?php echo implode('</th><th>', $days); ?>
    </th>
</tr>
<?php foreach ($data as $row): ?>
<tr>
    <?php foreach ($row as $day): ?>
        <?php if ($day == null): ?>
            <td>&nbsp;</td>
        <?php else: ?>
            <td><?php echo $day; ?></td>
        <?php endif; ?>
    <?php endforeach; ?>
</tr>
<?php endforeach; ?>
</table>

<?php endfor; ?>