Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any date/time where this function could break?

I've not seen anything like this before. This is part of a function that returns the expected answer (a series of five dates)... sometimes. For example, it's been run at 6am, and the result is sometimes incorrect: one of the five dates, either at the first or last, can be missing. Other times, it's fine. Same code, run just a few hours later.

I know working with dates can be a lot more complicated than it first appears, but this has stumped me. I can only hope my inexperience with the DateTime objects is to blame.

    $start = new \DateTime(date("Y-m-d", strtotime("-1 day"))); 
    $end = new \DateTime(date("Y-m-d", strtotime("-5 days"))); 
    $diff = $end->diff($start);
    $interval = \DateInterval::createFromDateString('-1 day');
    $period = new \DatePeriod($start, $interval, $diff->days); 

    foreach($period as $date) {
        echo $date->format("Y-m-d"); // Sometimes first or last date will be missing
    }

So for example, if the code is run between 2020-07-05 00:00:00 and 2020-07-05 23:59:59, it should return the last five dates:

2020-07-04
2020-07-03
2020-07-02
2020-07-01
2020-06-30

I've run the code with various date/times manually, and I cannot recreate the bug... and yet it happens once every few days in production.

This is just vanilla PHP, but it is being run as part of a Laravel project, should that factor into things. (The app timezone is set to "Europe/London".)

like image 561
Chuck Le Butt Avatar asked Oct 15 '22 02:10

Chuck Le Butt


1 Answers

I'm not keen on how you're defining $start and $end. If I'm not mistaken, if the server clock happens to tick to the next second between the two variables being defined, then your interval will be 3 days, 23 hours, 59 minutes, 59 seconds - instead of exactly 4 days. This messes up your definition of $diff->days to be 3 instead of 4, leading to a missing date.

I would suggest a different approach here. Specifically, start with the current date, and subtract a day the number of times you want - since this appears to be hard-coded to 5.

$date = new DateTime();
$interval = new DateInterval("P1D");
for( $i=0; $i<5; $i++) {
    $date->sub($interval);
    echo $date->format("Y-m-d")."\n";
}

That $i<5 can, of course, be refactored to $i < DAYS for some appropriate constant definition, to avoid the "magic number" and allow for changing in future development.

like image 56
Niet the Dark Absol Avatar answered Nov 15 '22 03:11

Niet the Dark Absol