I'm using the following code to calculate the average time between a start time and an end time for a number of events (from a database):
function getATBData($siteID, $fromDate, $toDate)
{
global $pdo;
$ATBarray = array();
$maxATB;
$minATB;
$avgATB;
$totalATB=new DateTime("#0");
$totalEvents=0;
$timetable;
$query = "SELECT id, siteID, start_time, end_time FROM atb_log WHERE siteID=:siteID AND (start_time BETWEEN :fromDate AND :toDate) AND (end_time BETWEEN :fromDate AND :toDate)";
$stmt = $pdo->prepare($query);
$stmt->bindParam(":siteID", $siteID);
$stmt->bindParam(":fromDate", $fromDate);
$stmt->bindParam(":toDate", $toDate);
$stmt->execute();
foreach ($stmt as $row)
{
$timeDiff = date_diff(new DateTime($row['start_time']),new DateTime($row['end_time']), true); //force absolute
if(!isset($maxATB) OR dateIntervalInSeconds($timeDiff) > dateIntervalInSeconds($maxATB))
$maxATB = $timeDiff;
if(!isset($minATB) OR dateIntervalInSeconds($timeDiff) < dateIntervalInSeconds($minATB))
$minATB = $timeDiff;
$totalATB->add($timeDiff);
$totalEvents++;
}
if($totalEvents!=0)
{
//$avgATB=round($totalATB->getTimestamp() / $totalEvents);
$avgATB = average_time($totalATB->format("H:i:s"),$totalEvents,0);
}
else
{
$avgATB=0;
$maxATB=new DateInterval('PT0S');
$minATB=new DateInterval('PT0S');
}
$avgSeconds = new DateInterval("PT" . $avgATB . "S");
$ATBarray['max'] = $maxATB->format("%H:%I:%S");
$ATBarray['min'] = $minATB->format("%H:%I:%S");
$ATBarray['avg'] = gmdate("H:i:s",$avgATB); //$avgSeconds->format("%H:%i:%s");
$ATBarray['total'] = $totalATB->format("H:i:s");
$ATBarray['events'] = $totalEvents;
return $ATBarray;
}
Unfortunately, I'm getting extremely high averages. As an example, I'm finding that the max time is 3 seconds, the min time is 0 seconds, but the average time is 1 hour and 1 second. This is obviously impossible, so is there something wrong with how I'm calculating total and/or average? The total is rather high too, but I haven't been able to manually add up this data so I'm not sure if that is also incorrect.
This is most probably a DST problem. Is your code running in a U.S. timezone? Rerember that when you're using new DateTime("#0"), this represents 1970-01-01 00:00:00 (non-DST); in the U.S., DST has started now, so the absolute value of your DateTime objects created from the database are one hour off.
EDIT:
I've changed the way I'm calculating averages. I've added the new function all above, and here is the averaging function (found here):
function average_time($total, $count, $rounding = 0) //from SO
{
$total = explode(":", strval($total));
if (count($total) !== 3) return false;
$sum = $total[0]*60*60 + $total[1]*60 + $total[2];
$average = $sum/(float)$count;
$hours = floor($average/3600);
$minutes = floor(fmod($average,3600)/60);
$seconds = number_format(fmod(fmod($average,3600),60),(int)$rounding);
if($hours<10) $hours = "0"+$hours; //add leading 0
if($minutes<10) $minutes = "0"+$minutes;
if($seconds<10) $seconds = "0"+$seconds;
return $hours.":".$minutes.":".$seconds;
}
So now my average numbers have changed and I'm trying to check the totals to see if they're correct. But I've posted that as another question, located here.
Related
I am working on laravel project using carbon on date format. I getting stuck to find only difference of working hours. Let suppose my working hourse is (10am to 7pm) and we received order on 6pm and process on next day on 1pm so difference of hourse will be (4 hours). Similarly if we've weekend during order process and order received then weekend days also should be exclude. Please give me the solution.
I guess do something like:
Calculate the remaining working hours for the day the order was placed on
Calculate the amount of full work days in between the placement and the processing and multiply by the amount of working hours in a day
Calculate the working hours for the day the order was processed on
Calculate the sum of the above
Thank you for the help, but I got the answer.
function getWorkingHours($ini_str,$end_str){
//config
$ini_time = [8,0]; //start working (hr, min)
$end_time = [17,0]; //end working (hr, min)
//date objects
$ini = date_create($ini_str);
$ini_wk = date_time_set(date_create($ini_str),$ini_time[0],$ini_time[1]);
$end = date_create($end_str);
$end_wk = date_time_set(date_create($end_str),$end_time[0],$end_time[1]);
//days
$workdays_arr = getWorkDays($ini,$end);
$workdays_count = count($workdays_arr);
$workday_seconds = (($end_time[0] * 60 + $end_time[1]) - ($ini_time[0] * 60 + $ini_time[1])) * 60;
//get time difference
$ini_seconds = 0;
$end_seconds = 0;
if(in_array($ini->format('Y-m-d'),$workdays_arr)) $ini_seconds = $ini->format('U') - $ini_wk->format('U');
if(in_array($end->format('Y-m-d'),$workdays_arr)) $end_seconds = $end_wk->format('U') - $end->format('U');
$seconds_dif = $ini_seconds > 0 ? $ini_seconds : 0;
if($end_seconds > 0) $seconds_dif += $end_seconds;
//final calculations
$working_seconds = ($workdays_count * $workday_seconds) - $seconds_dif;
$working_seconds_format = gmdate("H:i:s", $working_seconds);
return $working_seconds_format; //return hrs
}
function getWorkDays($ini,$end){
//config
$skipdays = [5,6]; //friday:5; saturday:6; sunday:0
$skipdates = []; //eg: ['2016-10-10'];
//vars
$current = clone $ini;
$current_disp = $current->format('Y-m-d');
$end_disp = $end->format('Y-m-d');
$days_arr = [];
//days range
while($current_disp <= $end_disp){
if(!in_array($current->format('w'),$skipdays) && !in_array($current_disp,$skipdates)){
$days_arr[] = $current_disp;
}
$current->add(new DateInterval('P1D')); //adds one day
$current_disp = $current->format('Y-m-d');
}
return $days_arr;
}
echo getWorkingHours('2019-10-10 20:00:00', '2019-10-13 19:59:30'); //thir-sun:
Ref: Calculating working hours between two dates
Currently i working with the attendance management system.i calculate how many hours work done in employees.i already calculate the how much hours working in day and it store in the mysql database.
$totaltime = (strtotime($time_out) - strtotime($time_in));
$hours = sprintf('%02d', intval($totaltime / 3600));
$seconds_remain = ($totaltime - ($hours * 3600));
$minutes = sprintf('%02d', intval($seconds_remain / 60));
$seconds = sprintf('%02d' ,($seconds_remain - ($minutes * 60)));
$final = '';
if ($time_in == '' || $time_out == '')
{
$final = '';
}
else
{
$final .= $hours.':'.$minutes.':'.$seconds;
}
for example
$time_in = 08:09:57
$time_out = 16:04:50
$final = 07:54:53 (total working hours)
now i want to get the current month total working time for each employee.how do get sum of the $final using php?
sample data of the month_data
Emp_no Date Time_in Time_out Total_hours TranID
23 2019-08-01 07:54:40 16:01:40 08:07:00 1
23 2019-08-02 07:42:35 16:02:53 08:20:18 2
i want get the sum of the Total_hours for related one employee
If you ask me this can be easily done using plain MySQL, no meed for PHP to calculate this.
You could take a look at a query somewhat like this
SELECT SEC_TO_TIME(SUM(`Total_hours`) ) FROM `month_data` GROUP BY `Emp_no`;
there is a simple SUM function which can do this for you, it returns the total time in seconds though.
In order to turn that into readable time you can use the MySQL function SEC_TO_TIME.
edit
If the said column is not a TIME column you can CAST it to be handled as this type of column using CAST() the needed SQL would look something like
SELECT SEC_TO_TIME(SUM(CAST(`Total_hours` AS TIME)) ) FROM `month_data` GROUP BY `Emp_no`;
My suggestion would be to change the column type to TIME though.
edit 2
I was under the assumption that SUM() would be smart enough to convert the time to seconds and come up with the correct sum of the given times.
Not sure why yet but this is not the case, therefore you need to convert the given times to seconds first.
SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(`Total_hours`)) ) FROM `month_data` GROUP BY `Emp_no`;
Now I have not tested this but TIME_TO_SEC() seems to accept VARCHAR just fine so need to CAST() the column anymore.
take a look at this:
echo OverallTime($allTimes);
$allTimes = array();
function OverallTime($allTimes) {
$minutes = 0;
foreach ($allTimes as $time) {
list($hour, $minute) = explode(':', $time);
$minutes += $hour * 60;
$minutes += $minute;
}
$hours = floor($minutes / 60);
$minutes -= $hours * 60;
return sprintf('%02d:%02d', $hours, $minutes);
<?php
$total = [
'00:02:55',
'00:07:56',
'01:03:32',
'01:13:34',
'02:13:44',
'03:08:53',
'03:13:54'
];
$sum = strtotime('00:00:00');
$sum2=0;
foreach ($total as $v){
$sum1=strtotime($v)-$sum;
$sum2 = $sum2+$sum1;
}
$sum3=$sum+$sum2;
echo date("H:i:s",$sum3);
?>
In case this is useful to someone looking for this, this is what I use on my music website. This code gets the duration in seconds of all the songs in an album, adds them up, and returns the total album length in hh mm ss.
$record_id = $this->record->id; <!--variable for record-->
.$query = 'SELECT SUM(duration) FROM #__songs WHERE `record_id` = '. $db->quote( (int) $record_id ); <!--selects the duration of all the songs in the album-->
$db->setQuery($query);
$results = $db->loadResult();
echo gmdate("H:i:s", $results); <!--echo total time in hh mm ss.-->
Not an expert here. If you see something, say something XD
I'm trying to make something like this:
if (datetime - system date > 15 minutes) (false)
if (datetime - system date <= 15 minutes) (true)
But I'm totally lost. I don't know how to make this operation in PHP.
I'd like to see how I can pick that DateTime from my database and check if it's between the last 15 minutes of my server's time.
The type of database is MySQL.
Finally, I managed to do it thanks to Sammitch and the other ones, here i leave the snippet:
$now = time();
$target = strtotime($row[1]);
$diff = $now - $target;
// 15 minutes = 15*60 seconds = 900
if ($diff <= 900) {
$seconds = $diff;
} else {
$seconds = $diff;
}
If your dates are already in MySQL you will want to do the comparison in the query because:
MySQL has proper DATE types.
MySQL has indexes for comparison.
MySQL performs comparisons much faster than PHP.
If you filter your data in the query then less, or no time is spent transferring superfluous data back to the application.
Below is the most efficient form. If there is an index on the date column it will be used.
SELECT *
FROM table
WHERE date > DATE_SUB(NOW(), INTERVAL 15 MINUTE)
Docs: DATE_SUB()
If you need to do it in PHP:
$now = time();
$target = strtotime($date_from_db);
$diff = $now - $target;
if ( $diff > 900 ) {
// something
}
or, more succinctly:
if( time() - strtotime($date_from_db) > 900 ) {
// something
}
You have a solution for MYSQL in other answers, a good solution for PHP is:-
$now = new \DateTime();
$target = new \DateTime(getTimeStringFromDB());
$minutes = ($target->getTimestamp() - $now->getTimestamp())/60;
if($minutes < 15){
// Do some stuff
} else {
//Do some other stuff
}
the most efficient PHP/MySQL combined solution is:
$date = date('Y-m-d H:i:s', strtotime('-15 minutes'));
$data = $pdo->query("SELECT date_field > '$date' as expired from table")->fetchAll(PDO::FETCH_COLUMN);
foreach($data as $expired) {
if (!$expired) {
// Still Valid
}
}
Try this
$interval = date_create($date_from_db)->diff(new \DateTime());
if ($interval->format('%r%l')>15) {
$result = false;
} else {
$result = true;
}
This comes from my previous question on getting the average time interval over a specified data set, [located here][1]. I'll post the entire function again:
function getATBData($siteID, $fromDate, $toDate)
{
global $pdo;
$ATBarray = array();
$maxATB;
$minATB;
$avgATB;
$totalATB=new DateTime("#0");
$totalEvents=0;
$timetable;
$query = "SELECT id, siteID, start_time, end_time FROM atb_log WHERE siteID=:siteID AND (start_time BETWEEN :fromDate AND :toDate) AND (end_time BETWEEN :fromDate AND :toDate)";
$stmt = $pdo->prepare($query);
$stmt->bindParam(":siteID", $siteID);
$stmt->bindParam(":fromDate", $fromDate);
$stmt->bindParam(":toDate", $toDate);
$stmt->execute();
foreach ($stmt as $row)
{
$timeDiff = date_diff(new DateTime($row['start_time']),new DateTime($row['end_time']), true); //force absolute
if(!isset($maxATB) OR dateIntervalInSeconds($timeDiff) > dateIntervalInSeconds($maxATB))
$maxATB = $timeDiff;
if(!isset($minATB) OR dateIntervalInSeconds($timeDiff) < dateIntervalInSeconds($minATB))
$minATB = $timeDiff;
$totalATB->add($timeDiff);
echo "added " . $timeDiff->format("%H:%I:%S") . " total is now: " . $totalATB->format("H:i:s") . "<br />";
$totalEvents++;
}
if($totalEvents!=0)
{
$avgATB = average_time($totalATB->format("H:i:s"),$totalEvents,0);
}
else
{
$avgATB=0;
$maxATB=new DateInterval('PT0S');
$minATB=new DateInterval('PT0S');
}
//$avgSeconds = new DateInterval("PT" . $avgATB . "S");
$ATBarray['max'] = $maxATB->format("%H:%I:%S");
$ATBarray['min'] = $minATB->format("%H:%I:%S");
$ATBarray['avg'] = $avgATB;
$ATBarray['total'] = $totalATB->format("H:i:s");
$ATBarray['events'] = $totalEvents;
return $ATBarray;
}
Given this function, I have added an output statement to try to debug why I was getting such a large time interval for my total time (when most of the values are a small number of seconds) and this is what it's outputting:
added 00:00:02 total is now: 01:00:02
added 00:00:00 total is now: 02:00:02
added 00:00:01 total is now: 03:00:03
added 00:00:01 total is now: 04:00:04
added 00:00:00 total is now: 05:00:04
added 00:00:02 total is now: 06:00:06
added 00:00:00 total is now: 07:00:06
and so on. So it seems like, despite the time to be added only being a couple seconds, it adds an hour every time. The call to add() on $timeDiff above is how I'm adding.
So the question is - is there a different way to call the add() function such that it will only add the seconds? Am I calling it incorrectly?
Hm, average difference in seconds, why that PHP swath of code if your database can give it to you:
SELECT
SEC_TO_TIME(MAX(TIME_TO_SEC(TIMEDIFF(end_time,start_time)))) AS max_timediff,
SEC_TO_TIME(MIN(TIME_TO_SEC(TIMEDIFF(end_time,start_time)))) AS min_timediff,
SEC_TO_TIME(AVG(TIME_TO_SEC(TIMEDIFF(end_time,start_time)))) AS avg_timediff,
SEC_TO_TIME(SUM(TIME_TO_SEC(TIMEDIFF(end_time,start_time)))) AS sum_timediff,
COUNT(id) as total_events
FROM atb_log
WHERE
siteID=:siteID
AND start_time > :fromDate
AND end_time < :toDate
Format those min/max/avg/sum of seconds as you like.
As I wrote in the answer to your other question, this is a DST (Daylight Savings Time) problem. In the U.S., DST has already begun; in Europe not.
Try this code:
timecheck("Europe/Amsterdam");
timecheck("America/Los_Angeles");
function timecheck($timezone) {
date_default_timezone_set($timezone);
$totalATB=new DateTime("#0");
$t1 = "2014-01-01 17:30:00";
$t2 = "2014-01-01 17:35:00";
$dt1 = new DateTime($t1);
$dt2 = new DateTime($t2);
$timeDiff = date_diff($dt1, $dt2, true);
printf("[%s] Starting with with: %s\n", $timezone, $totalATB->format("H:i:s"));
$totalATB->add($timeDiff);
printf("[%s] added %s, total is now: %s\n", $timezone, $timeDiff->format("%H:%I:%S"), $totalATB->format("H:i:s"));
}
The output:
[Europe/Amsterdam] Starting with with: 00:00:00
[Europe/Amsterdam] added 00:05:00, total is now: 00:05:00
[America/Los_Angeles] Starting with with: 00:00:00
[America/Los_Angeles] added 00:05:00, total is now: 01:05:00
I can't wrap my brain around this one so I hope someone can help. I have a song track that has the song length in milliseconds. I also have the date the song played in DATETIME format. What I am trying to do is find out how many milliseconds is left in the song play time.
Example
$tracktime = 219238;
$dateplayed = '2011-01-17 11:01:44';
$starttime = strtotime($dateplayed);
I am using the following to determine time left but it does not seem correct.
$curtime = time();
$timeleft = $starttime+round($tracktime/1000)-$curtime;
Any help would be greatly appreciated.
For my needs I used the following approach:
$curTime = microtime(true);
// something time consuming here
...
// get time difference in milliseconds
$timeConsumed = round(microtime(true) - $curTime,3)*1000;
So, the point is that we use float representation of time here (see http://php.net/manual/en/function.microtime.php)
Hope you will adopt it for your needs.
i use the following set of functions for handling mysql dates, maybe they can help you:
function sqlArray($date, $trim=true) {
$result = array();
$result['day'] = ($trim==true) ? ltrim(substr($date,8,2),'0') : substr($date,8,2);
$result['month'] = ($trim==true) ? ltrim(substr($date,5,2),'0') : substr($date,5,2);
$result['year'] = substr($date,0,4);
$result['hour'] = substr($date,11,2);
$result['minutes'] = substr($date,14,2);
return $result;
}
function sqlInt($date) {
$date = sqlArray($date);
return mktime($date['hour'], $date['minutes'], 0, $date['month'], $date['day'], $date['year']);
}
function difference($dateStart, $dateEnd) {
$start = sqlInt($dateStart);
$end = sqlInt($dateEnd);
$difference = $end - $start;
$result = array();
$result['ms'] = $difference;
$result['hours'] = $difference/3600;
$result['minutes'] = $difference/60;
$result['days'] = $difference/86400;
return $result;
}
in your case it should be something like:
$dateplayed = '2011-01-17 11:01:44';
print_r(difference($dateplayed, date('Y:m:d')));
hope it works :D
I have written this function to calculate duration between given two timestamps (with milliseconds).
function calculateTransactionDuration($startDate, $endDate)
{
$startDateFormat = new DateTime($startDate);
$EndDateFormat = new DateTime($endDate);
// the difference through one million to get micro seconds
$uDiff = ($startDateFormat->format('u') - $EndDateFormat->format('u')) / (1000 * 1000);
$diff = $startDateFormat->diff($EndDateFormat);
$s = (int) $diff->format('%s') - $uDiff;
$i = (int) ($diff->format('%i')) * 60; // convert minutes into seconds
$h = (int) ($diff->format('%h')) * 60 * 60; // convert hours into seconds
return sprintf('%.6f', abs($h + $i + $s)); // return total duration in seconds
}
$startDate = '02-Mar-16 07.22.13.000548';
$endDate = '02-Mar-16 07.22.14.000072';
$difference = calculateTransactionDuration($startDate, $endDate);
//Outputs 0.999524 seconds
You could convert the datetime string/input into unixtimestamp and then get the difference. If you do have milliseconds, unixtimestamp would have digits after the decimal. Once you have the difference, you can convert that value back into your date time pattern using function date in php. Below is the link.
Good luck!
http://php.net/manual/en/function.date.php
I used this function for my self:
public function calculateStringTimeToMiliseconds($timeInString)
{
$startTime = new \DateTime("now");
$endDate = new \DateTime($timeInString);
$interval = $startTime->diff($endDate);
$totalMiliseconds = 0;
$totalMiliseconds += $interval->m * 2630000000;
$totalMiliseconds += $interval->d * 86400000;
$totalMiliseconds += $interval->h * 3600000;
$totalMiliseconds += $interval->i * 60000;
$totalMiliseconds += $interval->s * 1000;
return $totalMiliseconds;
}