skip to Main Content

There are two timestamps stored in two variables: $startTimestamp = "2023-08-05 14:45:05" and $endTimestamp = "2023-08-09 12:00:14".

I am only able to get the total difference between the timestamps but not individual intervals. I need to get the time consumed on each day between the two timestamps. How to proceed?

What I have tried?

$startTimestamp = "2023-08-05 14:45:05";
$endTimestamp = "2023-08-09 13:00:14";

$begin = new DateTime($startTimestamp);
$end = new DateTime($endTimestamp);

$startDate = date("Y-m-d", strtotime($startTimestamp));
$endDate = date("Y-m-d", strtotime($endTimestamp));

if($startDate == $endDate)
{
    $return = [
        "$startDate" => round(abs(strtotime($endTimestamp) - strtotime($startTimestamp)) / 60,2)
    ];
}
else
{
    $return = [];
    $interval = DateInterval::createFromDateString('1 day');
    $period = new DatePeriod($begin, $interval, $end);

    foreach ($period as $dt)
    {
        $date_this = $dt->format("Y-m-d");
        if($date_this == $startDate)
        {
            $startEnd = $date_this." 23:59:59";
            $return[] = ["$date_this" => round(abs(strtotime($startEnd) - strtotime($startTimestamp)) / 60,2)];
        }
        elseif ($date_this == $endDate)
        {
            $endStart = $date_this." 00:00:00";
            $return[] = ["$date_this" => round(abs(strtotime($endTimestamp) - strtotime($endStart)) / 60,2)];
        }
        else
        {
            $return[] = ["$date_this" => 1440];
        }
    }
}

Expected output:

2023-08-05: 555 minutes
2023-08-06: 1440 minutes
2023-08-07: 1440 minutes
2023-08-08: 1440 minutes
2023-08-09: 885 minutes

Problem – Actual output:

2023-08-05: 554.9 minutes
2023-08-06: 1440 minutes
2023-08-07: 1440 minutes
2023-08-08: 1440 minutes

The 5th date is missing when end timestamp’s hour is less than start timestamp’s hour.

2

Answers


  1. Do not mix DateTime with the old date & time functions (date(), time(), strtotime(), etc). The old function provide poor support for timezones (or no support at all) and working with them is cumbersome. The new DateTime classes are easier to work with and can express better the business logic, hiding the technical details.

    The algorithm below splits the input interval into calendar days then, for each day, compute the fragment of the day that is included in the input interval. It then expresses each of these fragments in minutes.

    The algorithm does not handle the days when the DST changes (if DST applies to your timezone) but rest assured, the DateTime classes handle them correctly.

    It also does not handle the special case when the start and end timestamps lay on the same calendar day. If this case needs a special handling then it can be done after the foreach loop, on the output array (it has only one item when this special case happens).

    // Input data
    $startTimestamp = '2023-08-05 14:45:05';
    $endTimestamp = '2023-08-09 13:00:14';
    
    // The interval expressed using `DateTime` objects
    $begin = new DateTimeImmutable($startTimestamp);
    $end = new DateTimeImmutable($endTimestamp);
    
    // The beginning of the first day (midnight)
    $start = $begin->setTime(0, 0, 0);
    // What its name says
    $oneDay = new DateInterval('P1D');
    
    $output = [];
    
    // Check each day of the interval [$start, $end)
    $period = new DatePeriod($start, $oneDay, $end);
    foreach ($period as $day) {
        $s = max($day, $begin);               // day start (midnight, except for the first day)
        $e = min($end, $day->add($oneDay));   // day end (midnight, except for the last day)
        $l = $e->diff($s);                    // day length
    
        // Express the day length in minutes
        $output[$day->format('Y-m-d')] = 24 * 60 * $l->d + 60 * $l->h + $l->i + round($l->s / 60);
    }
    
    print_r($output);
    

    Check it online.

    Login or Signup to reply.
  2. axiac already copied my code to write an answer, but I still think my code can be useful in an answer.

    As I said in my comment: Your mixing different ways of representing dates and time, which is confusing. I’ve created a solution that only uses one way:

    <?php
    
    function getDiffInMinutes($startTime, $endTime)
    {
        $diff = $startTime->diff($endTime);
        return round(1440 * $diff->d + 60 * $diff->h + $diff->i + 1/60 * $diff->s);
    }
    
    
    function getMinutesPerDay($startTime, $endTime)
    {
        $output    = [];
        $startDate = $startTime->setTime(0, 0, 0);
        $endDate   = $endTime->setTime(23, 59, 59);
        $oneDay    = new DateInterval('P1D');
        $period    = new DatePeriod($startDate, $oneDay, $endDate);
        foreach ($period as $date) {
            $start = max($startTime, $date);
            $end   = min($endTime, $date->add($oneDay));
            $output[$date->format('Y-m-d')] = getDiffInMinutes($start, $end);
        }
        return $output;
    }
    
    $startTime = new DateTimeImmutable("2023-08-05 14:45:05");
    $endTime   = new DateTimeImmutable("2023-08-09 13:00:14");
    
    print_r(getMinutesPerDay($startTime, $endTime));
    

    which returns:

    Array
    (
        [2023-08-05] => 555
        [2023-08-06] => 1440
        [2023-08-07] => 1440
        [2023-08-08] => 1440
        [2023-08-09] => 780
    )
    

    See: https://3v4l.org/5Af18#v8.2.10

    I extracted the code to compute the difference, in minutes, between two datetimes, because that can be a useful function. I also turned the algorithm itself into a function so there are no global variables that can interfere with other code.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search