简体   繁体   中英

How to calculate the closest date for a recurring period, after a given date (PHP)

I am trying to figure out how to calculate the closest date after a given date for a recurring period.

For example, if the recurring period is every two weeks, starting Jan 1st 2016 and the given date is Jan 17, how do I calculate that the next recurring period date is Jan 28th?

The recurring period could be any number of days, weeks, months or years.

Right now the only solution I can think of is to start at the starting date and loop, adding the recurring period on each iteration until I pass the given date, but I am wondering if there is a more efficient or elegant solution?

You can use DatePeriod to accomplish it:

$begin = new DateTime('2016-01-01');
$end = new DateTime('2016-12-31');
$interval = new DateInterval('P14D');
$datePeriod = new DatePeriod($begin, $interval ,$end);

$givenDate = new DateTime('2016-01-17');

foreach ($datePeriod as $date) {
    if ($date < $givenDate) {
        continue;
    }

    echo 'The next recurring period date is ' . $date->format('Y-m-d');
    break;
}

The output would be:

The next recurring period date is 2016-01-29

If you are open and don't mind to a database option and mini cron script, I have a suggestion. Create a table called as recurring_track and have a key value columns :

For eg :

last_recurring_period as key and value be 05-25-2016

Now run a cron script to just update this every time the recurring duration occur.

Now you just need to query this table to figure out what was the last recurring period and now when will be next recurring period with the given date you can add and determine.

try this,

    $starting_dat = '2016-01-01';
    $recurring_prd = "2 week";
    $given_dat = '2016-02-28';

    while ($given_dat > $starting_dat)
    {


            $next_date=date('Y-m-d', strtotime($recurring_prd, strtotime(date($starting_dat))));
            $starting_dat = $next_date;
    }

    echo date('d-m-Y', strtotime($next_date));
 $now = time(); // or your date as well
 $your_date = strtotime("2010-01-01");

 //Get difference in days  
 $datediff = $now - $your_date; // in days say 60 days 

 //use mod with your reoccurring period 
 $remain = $datediff % $recPeriod // her say 2 weeks = 14 days recurring gets you 4
 //nearest recured date 
 $recdate = strtotime("-".$remain." day", $now); // 4 days ago 

Modify similar way for next date too

Instead of looping you can just do some math and leverage the DateTime class:

$start = new DateTime("2016-01-01");
$interval = 14;

$current = new DateTime("2016-01-17");

// Here we subtract from the interval (14 days) the amount of days remaining
// to the next recurring date
$daysUntilNext = $interval - ($current->diff($start)->days % $interval);

$next = $current->modify("+$daysUntilNext days");

// $next now contains the next recurring date

Another take on this, fairly similar to the one from @Matei Mihai but doesn't require a check within the final loop. Feels like there should be a nicer way to add multiple instances of an DateInterval to a DateTime.

<?php
$start   = new DateTime('2016-01-01');
$cutOff  = new DateTime('2016-01-17');
$period  = new DateInterval('P2W');

// Find out the total number of complete periods between the two dates
$distance = $start->diff($cutOff)->days;
$periodsBetween = (int) ($distance / $period->d);

// Then add on that number of periods + 1 to the original date
for ($a=1; $a<=$periodsBetween + 1; $a++)
{
    $start->add($period);
}

echo $start->format('Y-m-d'); // 2016-01-29

I'm using Carbon below to simplify how things read, but I needed to do this on a project recently and couldn't find much online about it; here's the solution I came up with:

    <?php
    $arr = [
        'type' => 'days', // any unit of measure - months, years, seconds, etc.
        'units' => 20 // length of each period in the above type. E.g. 20 days
    ];


    /** 
     *  -------------------------------------------------------
     * | Begin calculating time remaining in the current cycle.|
     *  -------------------------------------------------------
     */


    /**
     * When the cycle intervals originally started
     * Throwing a random date here but could be whatever you want.
     */
    $startDate = Carbon::now()->subDays(300);

    /**
     * Total number of units since start date
     */
    $diffMethodUnit = ucfirst($arr['type']);
    $diffMethod = "floatDiffIn{$diffMethodUnit}";
    $totalLapsed = $startDate->{$diffMethod}();

    /**
     * Get the start date of the current cycle
     */
    $currentStartDate = $startDate->add(floor($totalLapsed), $arr['type']);

    /**
     * This gives us how many of the unit has lapsed 
     * since the beginning of the current cycle
     */
    $currentLapsed = $totalLapsed - floor($totalLapsed);

    /**
     * Finally, get the end date of the current cycle
     */
    $currentEndDate = Carbon::now()
        ->sub($arr['type'], $currentLapsed)
        ->add($arr['type'], $arr['units']);

    $arr['current_cycle'] = [
        'start' => $currentStartDate;
        'end' => $currentEndDate;
        'remaining' => [
             'seconds' => Carbon::now()->diffInSeconds($currentEndDate);
             'minutes' => Carbon::now()->diffInMinutes($currentEndDate);
             'hours' => Carbon::now()->diffInHours($currentEndDate);
             'days' => Carbon::now()->diffInDays($currentEndDate);
             'weeks' => Carbon::now()->diffInWeeks($currentEndDate);
             'months' => Carbon::now()->diffInMonths($currentEndDate);
             'years' => Carbon::now()->diffInYears($currentEndDate);
        ]
     ];

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM