Well well well, if it isn't my old SharePoint enemy the All Day Event field. Like any good arch nemesis, problems with All Day Event keep coming back to make my life interesting.
Ok, I'm exaggerating as there are worse SharePoint adversaries (the 2013 Workflow Engine and Access Services come to mind). All Day Event is not that bad, just a little bit of a nuisance. Here's why. The field All Day Event stores the start time for your event as 12:00:00 AM and ends at 12:59:00 PM. While this might make sense to you and me, per this article, “these date values are offset currently by the GMT offset, but the value are stored in SQL for the all-day events without being offset. Normal events (non-all day events) are stored using the GMT offset.” Thus, this can make all day events show up as starting the previous day. There's a few posts on StackOverflow about this and some of the solutions there and other places are unnecessarily complex. A lot of these solutions were good at the time, but because how the platform has changed over the years, there are now better ways to resolve this.
Credit goes to Dan Adams for helping figure this one out.