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;
}
CodePudding user response:
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"
}
CodePudding user response:
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');