1

I'm struggling to to write a PHP function that would calculate time difference between two hours (minus the brake) and the result would be in decimal format. My inputs are strings in 24-hour format (hh:mm):

$start = '07:00'; //started at 7 after midnight
$brake = '01:30'; //1 hour and 30 minutes of brake
$finish = '15:00'; //finished at 3 afternoon
//the desired result is to print out '6.5'

example 2

$start = '19:00'; //started late afternoon
$brake = '00:30'; //30 minutes of brake
$finish = '03:00'; //finished at 3 after midnight
//the desired result is to print out '7.5'

I used to have following formula in MS Excel which worked great:

=IF(D12>=F12,((F12+1)-D12-E12)*24,(F12-D12-E12)*24) '7.5 worked hours

where
D12 - Start time '19:00
F12 - Finish time '03:00
E12 - Brake time '00:30

I tried to play with strtotime() with no luck. My PHP version is 5.4.45. Please help

Pawel
  • 891
  • 1
  • 9
  • 31
  • What problems did you have with strtotime()? It will take your format and output it as a unix timestamp. You can then do your math on that result. – JNevill Feb 22 '18 at 18:01
  • Transform your data to seconds from the Unix era, do your operations and transform back your data. – CFV Feb 22 '18 at 18:06
  • What's the error you got? – CFV Feb 22 '18 at 18:06
  • see this https://stackoverflow.com/questions/365191/how-to-get-time-difference-in-minutes-in-php and then just convert your break amount to minues (if it isn't already) and subtract – Funk Doc Feb 22 '18 at 18:11

3 Answers3

1

To do this, convert you string times into a unix timestamp. This is an integer number of seconds since the unix epoch (00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970, minus the number of leap seconds that have taken place since then). Do your math, then use the Date() function to format it back into your starting format:

<?php

$start = '19:00'; //started late afternoon
$break = '00:30'; //30 minutes of brake
$finish = '03:00'; //finished at 3 after midnight

//get the number of seconds for which we took a $break
//do this by converting break to unix timestamp, then extracting the hour and multiplying by 360
//and do the same extracting minutes and multiplying by 60
$breaktime = date("G",strtotime($break))*60*60 + date("i",strtotime($break))*60;

//get start time
$unixstart=strtotime($start);

//get finish time. Add a day if finish is tomorrow
if (strtotime($finish) < $unixstart) {
    $unixfinish = strtotime('+1 day', strtotime($finish));
} else {
    $unixfinish = strtotime($finish);
}

//figure out time worked
$timeworked = ($unixfinish - $unixstart - $breaktime) / 3600;

echo $timeworked;

?>
JNevill
  • 46,980
  • 4
  • 38
  • 63
  • Hey man, I think the result is off. It's outputting `3:30`and it should be `6:30`. I think you probably meant to `/60/60` instead of using `date()`. – ishegg Feb 22 '18 at 18:37
  • @ishegg Strange as this same code ouputs "7:30" on my machine. I'm pretty certain I don't want to divide the hour (`1` in this case) by 360. I want to get the number of seconds in that `1` hour. – JNevill Feb 22 '18 at 18:39
  • Shit you're right... [7:30](https://3v4l.org/b8Ihj)... it's showing `3:30` on my machine. I'll see what the hell. Cheers. – ishegg Feb 22 '18 at 18:40
  • @ishegg That's an odd one. Could timezone affect that? It isn't 15:00 here yet. – JNevill Feb 22 '18 at 18:43
  • @fyrye That's funny. I wrote that warning at the bottom having not seen the second example. – JNevill Feb 22 '18 at 19:33
  • @JNevill ah I thought it was in reference to > 24 hours. – Will B. Feb 22 '18 at 19:33
  • You could reduce the `strtotime` calls by defining `$unixfinish` before your condition and adjust it if it is less than the `$unixstart`. `$unixfinish = strtotime($finish); if ($unixfinish < $unixstart) { $unixfinish = strtotime('+1 day', $unixfinish); }` – Will B. Feb 22 '18 at 20:31
1

To provide a solution that doesn't require as much mathematics or parsing of the time values.

Assuming the day is not known, we can also account for the offset of the finish time and start time, when the start time is late at night.

Example: https://3v4l.org/FsRbT

$start = '07:00'; //started at 7 after midnight
$break = '01:30'; //1 hour and 30 minutes of brake
$finish = '15:00'; //finished at 3 afternoon

//create the start and end date objects
$startDate = \DateTime::createFromFormat('H:i', $start);
$endDate =  \DateTime::createFromFormat('H:i', $finish);

if ($endDate < $startDate) {
    //end date is in the past, adjust to the next day
    //this is only needed since the day the time was worked is not known
    $endDate->add(new \DateInterval('PT24H'));
}

//determine the number of hours and minutes during the break
$breakPeriod = new \DateInterval(vsprintf('PT%sH%sM', explode(':', $break)));

//increase the start date by the amount of time taken during the break period
$startDate->add($breakPeriod);

//determine how many minutes are between the start and end dates
$minutes = new \DateInterval('PT1M');
$datePeriods = new \DatePeriod($startDate, $minutes, $endDate);

//count the number of minute date periods
$minutesWorked = iterator_count($datePeriods);

//divide the number of minutes worked by 60 to display the fractional hours
var_dump($minutesWorked / 60); //6.5

This will work with any time values within a 24 hour period 00:00 - 23:59. If the day the times were worked are known, the script can be modified to allow for the day to be given and provide more precise timing.

Will B.
  • 17,883
  • 4
  • 67
  • 69
0

Another way, using DateTime. Basically, create 2 DateTime objects with the times of start and finish. To the start time, subtract the brake time, and the subtract from the result the end time.

You need to split the brake time in order to use modify().

<?php
$start = '07:00'; //started at 7 after midnight
$brake = '01:30'; //1 hour and 30 minutes of brake
$brakeBits = explode(":", $brake);
$finish = '15:00'; //finished at 3 afternoon
$startDate = \DateTime::createFromFormat("!H:i", $start);
$startDate->modify($brakeBits[0]." hour ".$brakeBits[1]." minutes");
$endDate  = \DateTime::createFromFormat("!H:i", $finish);
$diff = $startDate->diff($endDate);
echo $diff->format("%r%H:%I"); // 06:30

Demo

ishegg
  • 9,685
  • 3
  • 16
  • 31