Calendar for Custom Year
PHP source code and discussion
The calendar generator has been written in PHP 5 and the source code can be viewed here. It may be used and altered in any way. You can create tables for a year with it or also display a custom number of days from any starting date (e.g. to display the next 100 days as of today).
Technical Description
The core of the calendar generator are the methods createMatrix($date, $numberOfDays)
and createYearMatrix($year)
. The former generates a two-dimensional array representing
a layout table, i.e. each entry in the first dimension ($matrix[1]
) is a row and every
entry in there is a specific field ($matrix[1][0]
), containing the date's day.
For example, createMatrix($date, 21)
with $date
being a timestamp for
2015-05-04 (a Monday), will return:
|[0]|[1]|[2]|[3]|[4]|[5]|[6] [0]| 4| 5| 6| 7| 8| 9| 10 [1]| 11| 12| 13| 14| 15| 16| 17 [2]| 18| 19| 20| 21| 22| 23| 24
The method createYearMatrix($year)
generates such a matrix per month for you, i.e.
it will return an array of twelve elements where each field contains the matrix of the month.
If you just want to display a year's calendar, simply call generateYearHtml($year)
and it will take care of the details for you. The output HTML contains CSS classes so you can refine
the output to your wishes. You can view the calendar CSS rules that this
demo page uses as an example.
Similarly, generateHtmlTable($matrix)
will generate a HTML table from a given matrix.
For more control, you can use the method generatePlainTable
or generateYearPlainOutput
which lets you define all the texts before/after a row, between the cells, etc.
(Hint: generateHtmlTable
is just a custom call to generatePlainTable
.)
For more specific use cases, you can always extend or modify the class to your wishes or just take the matrix and use your own displaying function, as shown here below.
Example: Work Week Only
If you want to generate a work week calendar, you could remove the last two entries from each
row and call it a day. The following shows a possible solution (in particular, notice how
$matrix
is assigned).
function removeWeekend($row) {
return array_slice($row, 0, 5);
}
$fullMatrix = $generator->createYearMatrix(2015);
$matrix = array_map(function ($month) {
return array_map('removeWeekend', $month);
}, $fullMatrix);
// Display workday matrix
$i = 1;
foreach ($matrix as $month) {
echo "\n<h2>" . $generator->getMonthNames()[$i] . '</h2>';
echo '<table><tr><th>' . implode('</th><th>', ['Mon','Tue','Wed','Thu','Fri']) . '</th></tr>';
foreach ($month as $row) {
echo "\n<tr><td>" . implode('</td><td>', $row) . '</td></tr>';
}
echo '</table>';
++$i;
}
Example: Show Month Next to Table
The year output function shows the month, but this is not the case for createMatrix()
.
To programmatically know the current month in a large table, you could check when the current row has
a smaller date than the current one, e.g. if the above row displays the date "25" and the next one has
date "1", you know that the month has changed. The following is a possible way to do this. In particular,
note how we use $currentMonth
and when it is updated.
$totalRows = count($matrix);
$currentMonth = 4; // -1 of the current month to compensate ++
$months = $generator->getMonthNames();
echo '<table><tr><th>Month</th><th>'
. implode('</th><th>', ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'])
. '</th></tr>';
for ($i = 0; $i < $totalRows; ++$i) {
echo "\n<tr><td>";
if ($i === 0 || $matrix[$i][0] < $matrix[$i-1][0]) {
$currentMonth = (++$currentMonth > 12) ? 1 : $currentMonth;
echo $months[$currentMonth];
}
echo '</td><td>' . implode('</td><td>', $matrix[$i]) . '</td></tr>';
}
echo '</table>';
For large number of days, the code could be expanded to also display the year for every January.
Limits and Caveats
The calendar generator heavily relies on PHP's built-in date functions and therefore, one must not forget about the limits of Unix time. For most systems, this means you should only use it for years 1901 - 2037.
As commented in the utility function makeTime()
, daylight savings time plays a dirty
trick on you if you pass a timestamp with a time early in the morning (before 2 AM). If you
iteratively add the number of seconds in a day to a timestamp in order to advance one day, you might
get a day twice because we go one hour back as we exit DST. Pass
timestamps with a later time, or use the utility function (sets the time to 12 PM).
We see in both examples that as soon as we want more control over the output, we have to write our own display function. The examples are hopefully a sufficient reference for such situations. I think for many cases it might make sense to use the matrix and to write your own display logic. A sort of template system with tags may have been a better approach in the calendar generator.
Lastly, as always, if you allow week names or month names to come from user input, it must be treated with caution. The plain output functions have an option whether the names of the months and week days should be HTML-escaped or not. Other contexts might require different types of escaping or sanitization.