Date logic is hard, and how you can make it easier

There are excellent tools available for essentially every programming language allowing you to work with dates as objects, because date logic is hard. But it doesn't have to be, and you can make it easier.

A few weeks ago, I was working on a submission system that is only supposed to allow a single submission once per week, between Monday at "midnight" and Sunday at "midnight". The code posted below is what was written to handle the conditions as described. Looking at this code, do you see the bug?

 
// Subtract 1 from today because the week starts on Monday, Central Time
$day = date('w') - 1;
$originalTimeZone = date_default_timezone_get();
date_default_timezone_set('America/Chicago');
$weekStart = strtotime('midnight', strtotime('-'.$day.' days'));
$weekEnd = strtotime("tomorrow", strtotime('midnight', strtotime('+'.(6-$day).' days')))-1;
date_default_timezone_set($originalTimeZone);
return [$weekStart, $weekEnd];
 

In case you didn't catch it, this code, assuming that "today" is Sunday, thinks that the $weekStart value is the following Monday, not the previous Monday as was the intent. This allows the users to submit multiple times on Sunday, gaming the system. The new version uses the amazing Carbon package to handle the generation of the date range, and as such, is replaced by the following code:

return [ Carbon::now()->startOfWeek()->timestamp, Carbon::now()->endOfWeek()->timestamp ];

This code more accurately reflects the original intent and as a side effect, the code is more readable to future developers. When possible, use battle-hardened and tested code (Carbon, for example, has tests covering 98% of their codebase). Often, it will allow you to abstract away the parts of your code that aren't your business logic, and allow you to solve the actual problem at hand.