Search code examples
phpdatetimedateinterval

Periods of 20 days between two dates including the last one


I need to loop through the days between two dates in intervals of 20 days, including the last date. For example, between the dates 2019/01/01 and 2019/01/27 should return the following ranges:

2019-01-01  =>  2019-01-20
2019-01-21  =>  2019-02-09
2019-02-10  =>  2019-02-27

I have tried with this code:

$start = new DateTime('2019-01-01');
$end = new DateTime('2019-02-27');
$interval = new DateInterval('P20D');
$period = new DatePeriod($start, $interval, $end, DatePeriod::EXCLUDE_START_DATE);

$from = $start->format('Y-m-d');
foreach ($period as $day) {
    $to = $day->format('Y-m-d');

    echo $from . '  =>  ' . $to . '<br>';

    $from = $day->modify('+1 day')->format('Y-m-d');
}

Output:

2019-01-01  =>  2019-01-21
2019-01-22  =>  2019-02-10

This code has two problems, does not include the last period (no longer containing 20 days) and is advanced one day. How can I solve these problems?


Solution

  • This is probably more simply done by just incrementing the $start value by the $interval until it is greater than the $end value. Note that the interval needs to be 19 days to make a 20-day (inclusive of start and end) period.

    $start = new DateTime('2019-01-01');
    $end = new DateTime('2019-02-27');
    $interval = new DateInterval('P19D');
    while ($start < $end) {
        echo $start->format('Y-m-d') . ' => ';
        $start->add($interval);
        echo min($start, $end)->format('Y-m-d') . "\n";
        $start->add(new DateInterval('P1D'));
    }
    

    Output:

    2019-01-01 => 2019-01-20 
    2019-01-21 => 2019-02-09
    2019-02-10 => 2019-02-27
    

    Demo on 3v4l.org

    Update

    Here is a version of the code that also skips weekends:

    $start = new DateTime('2019-01-01');
    $end = new DateTime('2019-02-27');
    $interval = new DateInterval('P1D');
    $days = 19;
    while ($start < $end) {
        echo $start->format('Y-m-d') . ' => ';
        for ($i = 0; $i < $days; ) {
            $start->add($interval);
            $day_of_week = $start->format('N');
            if ($day_of_week == 6 || $day_of_week == 7) continue;
            $i++;
        }
        echo min($start, $end)->format('Y-m-d') . "\n";
        $start->add($interval);
    }
    

    Demo on 3v4l.org