Search code examples
phplaraveldatephp-carbon

Calculate the next week cycle based on two dates


I have an event system set up and it works in cycles. For example, users can create events in 2, 4, 6 and 8 weekly cycles. Eventually this will be user definable.

I need to be able to calculate the current cycle for any future events that are not actually generated.

So e.g. if a user has selected a 6-week event cycle, and 9th August is cycle week 5, 16th August will be week 6, the 23rd will then be week 1, and so forth, continuously looping back through the cycle for each week. If we get all the way down to 15th November for example, that will be cycle week 1 again.

"2021-08-09"
5
"2021-08-16"
6
"2021-08-23"
1
"2021-08-30"
2
"2021-09-06"
3

What would be the best way for me to calculate the current week cycle if I know the start of the cycle and the current date? Hopefully that makes sense?

I have the following at the moment, but a loop is going to be quite expensive I'd have thought if I'm calculating this in 5-10 years time.

$start = Carbon\Carbon::parse('2021-08-09');
$end = Carbon\Carbon::parse('2021-11-15');

$cycle_weeks = 6;
$current_cycle = 5;

dump("Week - {$start->toDateString()} - Cycle {$current_cycle}");

// Get current cycle week
while ($start->lt($end)) {
    
    $start = $start->addWeeks(1);
    $current_cycle = $current_cycle + 1;
    
    if ($current_cycle > $cycle_weeks) {
        $current_cycle = 1;
    }
    
    dump("Week - {$start->toDateString()} - Cycle {$current_cycle}");
}

This gives me the date but at the expense of cycling through every week until the end date. I can't think of a mathematical way - if any - to perhaps work out the cycle based on the difference in weeks?

"Week - 2021-08-09 - Cycle 5"
"Week - 2021-08-16 - Cycle 6"
"Week - 2021-08-23 - Cycle 1"
"Week - 2021-08-30 - Cycle 2"
"Week - 2021-09-06 - Cycle 3"
"Week - 2021-09-13 - Cycle 4"
"Week - 2021-09-20 - Cycle 5"
"Week - 2021-09-27 - Cycle 6"
"Week - 2021-10-04 - Cycle 1"
"Week - 2021-10-11 - Cycle 2"
"Week - 2021-10-18 - Cycle 3"
"Week - 2021-10-25 - Cycle 4"
"Week - 2021-11-01 - Cycle 5"
"Week - 2021-11-08 - Cycle 6"
"Week - 2021-11-15 - Cycle 1"

Solution

  • I think this could work:

    function currentCycle(Carbon $startDate, Carbon $currentDate, int $cycles, int $startCycle) {
        if ($startCycle > $cycles) $startCycle = 1;
        return (($currentDate->diffInWeeks($startDate)+$startCycle-1)%$cycles)+1;
    }
    

    The idea is:

    • You get the number of weeks different between the current and start one. Normally the remainder of this number when divided by $cycles would give you a 0-based cycle number if the cycles started at $startDate. The division remainder (mod) in PHP is given by the % operator
    • Since we are given a different cycle to start from, and given this will not be 0-based, we reduce it by one e.g. $startCycle-1 and add this number to the diff result. We then covert this 0-based result back to 1-based by adding +1 at the end

    This should allow you to get the cycle based on any start date, any current date, any start cycle on that start date and any number of cycles.