After correcting the error in my calendar class (see PHP Calendar Class revisited), I’d been wondering whether the code could be further simplified. Some of the calculations were a bit on the arcane side, even if they now worked OK.
The solution lay in PHP’s relative date formats, which can be used when instantiating or modifying a DateTime object.
Given that we always display full weeks (Monday to Sunday), the two key dates needed for the calendar are the first Monday to display, and the last Sunday to display. It’s more than likely that these are in the previous / next months, rather than the month in focus. For a July 2021 calendar, for example, the first Monday is June 28; and the last Sunday is August 1.
It turns out that PHP is eyebrow-raisingly helpful at deriving these two dates.
For the first Monday, what we need is the Monday that precedes the second day in the month. The first day of the month might itself be a Monday, so we allow for that by starting at the second day. Here’s an example, which shows how straightforward it is, and also that we can chain the function calls on a single line of code:
$year = 2021;
$month = 7;
$start = new DateTime();
$start->setDate($year, $month, 2)->modify('previous Monday');
$start is now set to June 28; and exactly as you’d hope, when we try it for November, $start is set to November 1. I’ve no idea what machinations are going on behind the scenes but the point is that my code is radically more simple and understandable. If there is a performance hit, it’s negligible, given that I’m only incurring it once per page.
Here’s how we get the last Sunday to be displayed. We need the first Sunday after the last-but-one day in the month. Note that setDate() conveniently allows you to have negative days, so we first add 1 to the month, and then use -1 as the day number, which steps back two days from day 1 of that month.
$end = new DateTime();
$end->setDate($year, $month + 1, -1)->modify('first Sunday');
Note that PHP uses “first” here to get the first Sunday after the date, which is perfect for our needs.
I’ll be updating my calendar class to use this new approach.