skip to Main Content

I use a "DateInterval" with an interval of 3 hours to get all the dates between a start and end time. The result looks like that:

15:00 | 18:00 | 21:00 | 00:00 | 03:00 (and so on...)

Now I’m searching a solution that only the next "DateInterval" value gets shown (depending on the actual time), and not all of them.

Example: if the actual time is 19:29 the shown result should be 21:00.

My code so far:

$start = new DateTime('2022-12-18 15:00:00');
$interval = DateInterval::createFromDateString('3 hours');
$end = new DateTime('2022-12-31 15:00:00');

$occurrences = new DatePeriod($start, $interval, $end);

foreach ($occurrences as $occurrence) {

    echo $occurrence->format('H:i') . PHP_EOL;
}

2

Answers


  1. No need to iterate through the period. Just manipulate current hour number.
    This probably might be reworked with plain unix timestamps for even simplier code:

    <?php
    
    // This is just for test, in the code below
    // should be replaced with plain `(new DateTime)`
    $now = new DateTime('2022-12-18 19:29:00');
    
    // Set the next fraction-3 hour
    $result = ( new DateTime )
        ->setTime(
            // Divide current hour by 3, take integer part,
            // add one, multiply by 3, take 24 modulo 
            // (to get '0' instead of '24' and '3' instead of '27')
            (((int)($now->format('H') / 3) + 1 ) * 3) % 24,
            0
        );
    
    // The result
    // Note, the date is current here
    print $result->format('H:i');
    

    UPDATE: for dynamic start time, when the hour is not x3

    <?php
    
    # The alternative start time
    // $start = new DateTime('2022-12-18 13:00:00');
    $start = new DateTime('2022-12-18 15:00:00');
    
    // Moving magic number out of the calculations
    $period_hours = 3;
    
    // This is just for test, in the below code
    // should be replaced with plain `(new DateTime)`
    $now = new DateTime('2022-12-18 19:29:00');
    
    // Set the next fraction-period hour
    $result = ( new DateTime )
        ->setTime(
            // Divide current hour by the period, take integer part,
            // add one, multiply by period, add start DT shift,
            // and at last take 24 modulo 
            // (to get '0' instead of '24', '3' instead of '27' etc)
            (
                (((int)($now->format('H') / $period_hours) + 1 ) * $period_hours) 
                + $start->format('H') % $period_hours
            )
            % 24,
            0
        );
    
    // The result
    // Note, the day is current here
    print $result->format('H:i');
    
    
    Login or Signup to reply.
  2. The following will properly take into account the start time and return an object with the same timezone and other properties as its input.

    function intervalToSeconds(DateInterval $interval) {
        $d1 = new DateTimeImmutable('', new DateTimezone('UTC'));
        $d2 = $d1->add($interval);
        return $d2->getTimestamp() - $d1->getTimestamp();
    }
    
    function getNextDateTime(DateTime $target, DateTime $start, DateInterval $interval) {
        $t_start    = $start->getTimestamp();
        $t_target   = $target->getTimestamp();
        $t_since    = $t_target - $t_start;
        $t_interval = intervalToSeconds($interval);
        
        $t_next = ( intdiv($t_since, $t_interval) + 1 ) * $t_interval + $t_start;
        
        return (clone $target)->setTimestamp($t_next);
    }
    
    $start = new DateTime('2022-12-18 16:00:00', new DateTimezone('UTC'));
    $interval = DateInterval::createFromDateString('3 hours');
    $now = new DateTime('2022-12-20 23:45:01', new DateTimezone('America/Vancouver'));
    
    var_dump(
        $start, $interval,
        getNextDateTime($now, $start, $interval)
    );
    

    Output:

    object(DateTime)#1 (3) {
      ["date"]=>
      string(26) "2022-12-18 16:00:00.000000"
      ["timezone_type"]=>
      int(3)
      ["timezone"]=>
      string(3) "UTC"
    }
    object(DateInterval)#2 (2) {
      ["from_string"]=>
      bool(true)
      ["date_string"]=>
      string(7) "3 hours"
    }
    object(DateTime)#5 (3) {
      ["date"]=>
      string(26) "2022-12-21 02:00:00.000000"
      ["timezone_type"]=>
      int(3)
      ["timezone"]=>
      string(17) "America/Vancouver"
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search