PHP - Calculating working hours between two dates - php

I would like to calculate working hours between two dates. I am using working_hours_diff function which is answered here; https://stackoverflow.com/a/8927347/4671728
Actually, it is calculating normally but when I input two same date but different hours, it returns wrong values. For example; 29-12-2015 13:17:43 and 29-12-2015 11:17:39 - It returns 11 hours but it should be returned 2 hours. However when I input different dates from eachother, it calculates correctly.
You can kindly find the codes below;
function work_hours_diff($date1,$date2) {
if ($date1>$date2) { $tmp=$date1; $date1=$date2; $date2=$tmp; unset($tmp); $sign=-1; } else $sign = 1;
if ($date1==$date2) return 0;
$days = 0;
$working_days = array(1,2,3,4,5); // Monday-->Friday
$working_hours = array(8, 17); // from 8:30(am) to 17:30
$current_date = $date1;
$beg_h = floor($working_hours[0]); $beg_m = ($working_hours[0]*60)%60;
$end_h = floor($working_hours[1]); $end_m = ($working_hours[1]*60)%60;
// setup the very next first working timestamp
if (!in_array(date('w',$current_date) , $working_days)) {
// the current day is not a working day
// the current timestamp is set at the begining of the working day
$current_date = mktime( $beg_h, $beg_m, 0, date('n',$current_date), date('j',$current_date), date('Y',$current_date) );
// search for the next working day
while ( !in_array(date('w',$current_date) , $working_days) ) {
$current_date += 24*3600; // next day
}
} else {
// check if the current timestamp is inside working hours
$date0 = mktime( $beg_h, $beg_m, 0, date('n',$current_date), date('j',$current_date), date('Y',$current_date) );
// it's before working hours, let's update it
if ($current_date<$date0) $current_date = $date0;
$date3 = mktime( $end_h, $end_m, 59, date('n',$current_date), date('j',$current_date), date('Y',$current_date) );
if ($date3<$current_date) {
// outch ! it's after working hours, let's find the next working day
$current_date += 24*3600; // the day after
// and set timestamp as the begining of the working day
$current_date = mktime( $beg_h, $beg_m, 0, date('n',$current_date), date('j',$current_date), date('Y',$current_date) );
while ( !in_array(date('w',$current_date) , $working_days) ) {
$current_date += 24*3600; // next day
}
}
}
// so, $current_date is now the first working timestamp available...
// calculate the number of seconds from current timestamp to the end of the working day
$date0 = mktime( $end_h, $end_m, 59, date('n',$current_date), date('j',$current_date), date('Y',$current_date) );
$seconds = $date0-$current_date+1;
//printf("<br>From %s To %s : %d hours<br>",date('d/m/y H:i',$date1),date('d/m/y H:i',$date0),$seconds/3600);
// calculate the number of days from the current day to the end day
$date3 = mktime( $beg_h, $beg_m, 0, date('n',$date2), date('j',$date2), date('Y',$date2) );
while ( $current_date < $date3 ) {
$current_date += 24*3600; // next day
if (in_array(date('w',$current_date) , $working_days) ) $days++; // it's a working day
}
if ($days>0) $days--; //because we've allready count the first day (in $seconds)
//printf("<br>From %s To %s : %d working days<br>",date('d/m/y H:i',$date1),date('d/m/y H:i',$date3),$days);
// check if end's timestamp is inside working hours
$date0 = mktime( $beg_h, 0, 0, date('n',$date2), date('j',$date2), date('Y',$date2) );
if ($date2<$date0) {
// it's before, so nothing more !
} else {
// is it after ?
$date3 = mktime( $end_h, $end_m, 59, date('n',$date2), date('j',$date2), date('Y',$date2) );
if ($date2>$date3) $date2=$date3;
// calculate the number of seconds from current timestamp to the final timestamp
$tmp = $date2-$date0+1;
$seconds += $tmp;
//printf("<br>From %s To %s : %d hours<br>",date('d/m/y H:i',$date2),date('d/m/y H:i',$date3),$tmp/3600);
}
// calculate the working days in seconds
$seconds += 3600*($working_hours[1]-$working_hours[0])*$days;
//printf("<br>From %s To %s : %d hours<br>",date('d/m/y H:i',$date1),date('d/m/y H:i',$date2),$seconds/3600);
//return $sign * $seconds/3600; // to get hours
return round($seconds/3600);
}
date_default_timezone_set("Europe/Istanbul");
$dt2 = strtotime("29-12-2015 11:17:39");
$dt1 = strtotime("29-12-2015 13:17:43");
echo work_hours_diff($dt1 , $dt2 );

Replace this line:
if ($days>0) $days--; //because we've allready count the first day (in $seconds)
with:
$days--; // because we've already counted the first day (in $seconds)
That if was bad, as it removed the distinction between $days values of 0 and 1 (before the if), which just cannot be right.

Try this..
date_default_timezone_set("Europe/Istanbul");
$dt2 = strtotime("29-12-2015 11:17:39");
$dt1 = strtotime("29-12-2015 13:17:43");
echo $diff_hours = ($dt1 - $dt2)/3600;
Output
2.0011111111111 hours

Replace
if ($days>0) $days--; //because we've allready count the first day (in $seconds)
For this:
if (in_array(date('w',$date2) , $working_days)) {
$days--; //because we've allready count the first day (in $seconds)
}

Related

Calculate difference between two datetime excluding weekends

For example
02-11-2018 03:00pm - 05-11-2018 11:00am should be 2hrs.
Because 3rd and 4th are weekends.
$date1 = "2018-03-01 11:12:45";
$date2 = "2018-03-04 15:37:04";
$date1Timestamp = strtotime($date1);
$date2Timestamp = strtotime($date2);
$difference = $date2Timestamp - $date1Timestamp;
echo $difference;
You can use mktime() to create UNIX timestamps for the two date/times you want to compare. These timestamps will represent the number of seconds between the Unix Epoch (January 1 1970 00:00:00 GMT) and the time specified. Since they will both be in seconds, it makes it very easy to calculate the seconds between the two timestamps:
<?php
//set start time and end time - mktime(hour, minute, second, month, day, year)
$startTime = mktime(15, 0, 0, 11, 2, 2018); // 2-11-2018 3:00PM
$endTime = mktime(11, 0, 0, 11, 5, 2018); // 5-11-2018 11:00AM
//calculate total number of seconds between two date/times
$totalSeconds = $endTime - $startTime;
//apply whatever other math you need...
?>
As far as accounting for weekends and business hours, you will need to get creative with determining how many weekend days exist between the two date/times and what hours fall within business hours on business days. The PHP manual for date functions will come in handy. The following code produces the results you are looking for:
<?php
//set business start and end hours
$businessStartHour = 10; //10 AM
$businessEndHour = 16; //4 PM
//set weekend days
$arrWeekendDays = array(6,0); //numeric representations of Saturday (6) and Sunday (0)
//set start and end dates and times
//2-11-2018 3 PM
$startHour = 15;
$startMinute = 0;
$startSecond = 0;
$startMonth = 11;
$startDay = 2;
$startYear = 2018;
//5-11-2018 11 AM
$endHour = 11;
$endMinute = 0;
$endSecond = 0;
$endMonth = 11;
$endDay = 5;
$endYear = 2018;
//create UNIX timestamps
$startTime = mktime($startHour, $startMinute, $startSecond, $startMonth, $startDay, $startYear);
$endTime = mktime($endHour, $endMinute, $endSecond, $endMonth, $endDay, $endYear);
//ensure $endTime is greater than $startTime
if($startTime >= $endTime){
//invalid start and end datetimes
die("Invalid start and end datetimes.");
}
//calculate eligible seconds from partial time on first and last day
$totalSeconds = 0;
$currentTime = mktime(0, 0, 0, $startMonth, $startDay, $startYear); //beginning of $startTime day
$lastFullDay = mktime(0, 0, 0, $endMonth, $endDay, $endYear); //beginning of $endTime day
$startingBusinessTime = mktime($businessStartHour, 0, 0, $startMonth, $startDay, $startYear);
$endingBusinessTime = mktime($businessEndHour, 0, 0, $endMonth, $endDay, $endYear);
if($startTime < $startingBusinessTime){
$startTime = $startingBusinessTime;
}
if($endTime > $endingBusinessTime){
$endTime = $endingBusinessTime;
}
if($currentTime == $lastFullDay){
//$startTime and $endTime occur on the same day
if($endTime > $startTime){
$totalSeconds += ($endTime - $startTime);
}
}else{
//$startTime and $endTime do not occur on the same day
$startingBusinessTime = mktime($businessStartHour, 0, 0, $endMonth, $endDay, $endYear);
$endingBusinessTime = mktime($businessEndHour, 0, 0, $startMonth, $startDay, $startYear);
if($endingBusinessTime > $startTime){
$totalSeconds += ($endingBusinessTime - $startTime);
}
if($endTime > $startingBusinessTime){
$totalSeconds += ($endTime - $startingBusinessTime);
}
}
//calculate eligible seconds from all full days in between start day and end day
$fullDayBusinessSeconds = (($businessEndHour - $businessStartHour) * 3600);
//set $currentTime to beginning of first full day
$nextDay = $currentTime + (26 * 3600); //add 26 hours to $currentTime to get into the next day, compensating for possible daylight savings
$currentTime = mktime(0, 0, 0, date('n', $nextDay), date('j', $nextDay), date('Y', $nextDay));
while($currentTime < $lastFullDay){
//determine if $currentTime is a weekday
if(!in_array(date('w', $currentTime), $arrWeekendDays)){
//it's a business day, add all business seconds to $totalSeconds
$totalSeconds += $fullDayBusinessSeconds;
}
//increment $currentTime to beginning of next day
$nextDay = $currentTime + (26 * 3600); //add 26 hours to $currentTime to get into the next day, compensating for possible daylight savings
$currentTime = mktime(0, 0, 0, date('n', $nextDay), date('j', $nextDay), date('Y', $nextDay));
}
echo "Total eligible time between start time and end time: " . $totalSeconds . " seconds (" . convertSecToTime($totalSeconds) . ")";
function convertSecToTime($sec)
{
$date1 = new DateTime("#0");
$date2 = new DateTime("#$sec");
$interval = date_diff($date1, $date2);
return $interval->format('%y Years, %m months, %d days, %h hours, %i minutes and %s seconds');
// convert into Days, Hours, Minutes
// return $interval->format('%a days, %h hours, %i minutes and %s seconds');
}
?>
Kindly have a look at this precise php function returning days count with weekends excluded.
$start= "2018-03-01 11:12:45";
$end= "2018-04-01 15:37:04";
echo Count_Days_Without_Weekends($start, $end);
function Count_Days_Without_Weekends($start, $end){
$days_diff = floor(((abs(strtotime($end) - strtotime($start))) / (60*60*24)));
$run_days=0;
for($i=0; $i<=$days_diff; $i++){
$newdays = $i-$days_diff;
$futuredate = strtotime("$newdays days");
$mydate = date("F d, Y", $futuredate);
$today = date("D", strtotime($mydate));
if(($today != "Sat") && ($today != "Sun")){
$run_days++;
}
}
return $run_days;
}
Try it out, it really works..

php way to get date 15 of every month

I am trying to output the 15th day of next month, based on input provided by the user.
For example:
04/02/2016 - input by user
05/15/2016 - output after calculation
This is my code, which I'm trying:
// this value is already coverted in strtotime.
$today = $loan_data['loan_applied_date'];
$numOfDays = date('t', $today);
$base = strtotime('+'.$numOfDays.' days', strtotime(date('m/01/Y', $today)));
$day15 = date('m/01/Y', $base);
$time = strtotime($day15);
By running the above code I am getting the 1st day. How can I get the 15th?
Example scenario:
Suppose Subscription registered on 04/04/2016, so their next payment date will be 15th of next month or 05/15/2016.
Just use DateTime:
// Your description says that $loan_date['loan_applied_date'] is
// "already coverted in strtotime", so I assume a UNIX timestamp ...
DateTime::createFromFormat('U', $loan_date['loan_applied_date'])
// When you import a UNIX timestamp into DateTime, it assumes UTC,
// so we need to set your timezone back to whatever it is
->setTimezone(new DateTimeZone(date_default_timezone_get()))
// Add 1 month
->modify('+1 month')
// Hard-code the 15th day
->format('15/m/Y');
The solution is quite simple,using PHP you can take advantage of the flexible mktime function, as follows:
<?php
$today = strtotime("now"); //GRAB THE DATE - YOU WOULD WANT TO REPLACE THIS WITH YOUR VALUE
$year = date("Y", $today); //GET MONTH AND YEAR VALUES
$month = date("m", $today);
$month++; //INCREMENT MONTH
echo date("d/m/Y", mktime(0, 0, 0, $month, 15, $year)); //OUTPUT WITH MKTIME
?>
I did something similar to recurring payments, this code charge payments every 15th:
//ini_set("date.timezone", "America/New_York"); //uncomment for test timezone
function getLastDay($date=false){
return $date?date("t", strtotime($date)):date("t", strtotime(0));
}
function monthDayMatch($data = array('startDay'=>1,'day'=>1,'lastDayMonth','startMonth'=>1,'month'=>1,'frecuency'=>1)){
if(($data['startMonth']%$data['frecuency'] == $data['month']%$data['frecuency']) && ($data['startDay'] == $data['day'] || $data['startDay'] > $data['lastDayMonth'])){
return true;
}
return false;
}
$date = '04/02/2016';
$date = explode('/',$date);
$customer_date=array('month'=>$date[0],'day'=>$date[1],'year'=>$date[2],'all'=>implode('/',$date));
$resp = monthDayMatch(array(
'startMonth'=> $customer_date['month'], //initial customer month
'month' => date('m'), //current month
'frecuency' => 1, //recurring once a month
'startDay' => date('d'), //current day day
'day' => 15, //day to validate
'lastDayMonth'=>getLastDay($customer_date['all'])
));
//every 15th is true
if($resp){
echo 'payment charge';
}else{
echo 'no payment';
}

First day = 12 hours when adding days on to date and time in PHP

I have put together some code that adds an amount of days on to a date/time. For some reason the first day is calculated as 12 hours. For example, if I set one day it will add 12 hours. If I set 2 days it will add 36 hours.
$postdate = "03/08/2014 14:55:18";
echo $postdate;
//SPLIT UP DATE
$month = substr("$postdate", 0, -17);
$day = substr("$postdate", 3, -14);
$year = substr("$postdate", 6, -9);
$hour = substr("$postdate", 11, -6);
$min = substr("$postdate", 14, -3);
$sec = substr("$postdate", 17);
//DAYS LEFT
$days_left = 1; // Set Days Left
$add_day = "P$days_left D";
$tot_day = preg_replace('/\s+/', '', $add_day); // Trims white space
// Add days
$date = new DateTime();
$date->setDate($year, $month, $day);
$date->setTime($hour, $min, $sec);
$date->add(new DateInterval("$tot_day"));
$end = $date->format("m/d/y h:m:i");
// If time is up
if (time(new DateTimeZone('Europe/London')) > strtotime("$end"))
{
// Times Up
}
else
{
// Time Left
}
?>
Here I have the told the code to add 1 day to the date and time. But my result adds 12 hours instead.
Any help is appreciated. Thanks.

PHP - Calculating working hours between two dates but exclude the time when request is on hold

Can some one help me write a function that calculates the number of working hours between two dates but want to exclude the time when the request had a status of "On Hold".
So lets say the request came in at 3PM friday and was closed at 3PM Wednesday, and working hours are from 8AM to 5PM pacific (Mon thru Friday)...Total working hours will be 27 hours...but if the request remained on hold from Monday 3PM till Tuesday 3PM...Actual work time on the request really becomes 18 hours instead of 27 hours.
I have recently started working on PHP and have been assigned this task which is very confusing to me. Please help
All you have to do is get the total time elapsed, then substract the non-working hours.
You can use dateTime and datePeriod php objects for that (requires php 5.3)
Here a small script to do what you want (but you will have probably to adapt for your needs)
<?php
ini_set('display_errors', 'on');
define('DAY_WORK', 32400); // 9 * 60 * 60
define('HOUR_START_DAY', '08:00:00');
define('HOUR_END_DAY', '17:00:00');
// get begin and end dates of the full period
$date_begin = '2013-11-29 15:00:00';
$date_end = '2013-12-03 15:00:00';
// keep the initial dates for later use
$d1 = new DateTime($date_begin);
$d2 = new DateTime($date_end);
// and get the datePeriod from the 1st to the last day
$period_start = new DateTime($d1->format('Y-m-d 00:00:00'));
$period_end = new DateTime($d2->format('Y-m-d 23:59:59'));
$interval = new DateInterval('P1D');
//$interval = new DateInterval('weekdays'); // 1 day interval to get all days between the period
$period = new DatePeriod($period_start, $interval, $period_end);
$worked_time = 0;
$nb = 0;
// for every worked day, add the hours you want
foreach($period as $date){
$week_day = $date->format('w'); // 0 (for Sunday) through 6 (for Saturday)
if (!in_array($week_day,array(0, 6)))
{
// if this is the first day or the last dy, you have to count only the worked hours
if ($date->format('Y-m-d') == $d1->format('Y-m-d'))
{
$end_of_day_format = $date->format('Y-m-d '.HOUR_END_DAY);
$d1_format = $d1->format('Y-m-d H:i:s');
$end_of_day = new DateTime($end_of_day_format);
$diff = $end_of_day->diff($d1)->format("%H:%I:%S");
$diff = split(':', $diff);
$diff = $diff[0]*3600 + $diff[1]*60 + $diff[0];
$worked_time += $diff;
}
else if ($date->format('Y-m-d') == $d2->format('Y-m-d'))
{
$start_of_day = new DateTime($date->format('Y-m-d '.HOUR_START_DAY));
$d2_format = $d2->format('Y-m-d H:i:s');
$end_of_day = new DateTime($end_of_day_format);
$diff = $start_of_day->diff($d2)->format('%H:%I:%S');
$diff = split(':', $diff);
$diff = $diff[0]*3600 + $diff[1]*60 + $diff[0];
$worked_time += $diff;
}
else
{
// otherwise, just count the full day of work
$worked_time += DAY_WORK;
}
}
if ($nb> 10)
die("die ".$nb);
}
echo sprintf('Works from %s to %s, You worked %d hour(s)', $date_begin, $date_end, $worked_time/60/60);
Calculate work time with an accuracy of 1 minute.
WARNING: This function can take many seconds to load as it does a loop for every minute between the time span.
<?php
$request = array(
'start' => '3PM Nov 29 2013',
'end' => '3PM Dec 4 2013'
);
echo calculate_work($request);
/**
* Calculate work time by looping through every minute
* #param array $request start to end time
* #return int work time in minutes
*/
function calculate_work($request)
{
$start = strtotime($request['start']);
$end = strtotime($request['end']);
$work_time = 0;
/* Add 1 minute to the start so that we don't count 0 as a minute */
for ($time = $start + 60; $time <= $end; $time += 60)
{
// Weekends
if (date('D', $time) == 'Sat' OR date('D', $time) == 'Sun')
continue;
// Non Working Hours
if (date('Hi', $time) <= '0800' OR date('Hi', $time) > '1700')
continue;
// On Hold
if ($time > strtotime('3PM Dec 2 2013') AND $time <= strtotime('3PM Dec 3 2013'))
continue;
$work_time++;
}
// Divide by 60 to turn minutes into hours
return $work_time / 60;
}
/**
* Get the total working hours in seconds between 2 dates..
* #param DateTime $start Start Date and Time
* #param DateTime $end Finish Date and Time
* #param array $working_hours office hours for each weekday (0 Monday, 6 Sunday), Each day must be an array containing a start/finish time in seconds since midnight.
* #return integer
* #link https://github.com/RCrowt/working-hours-calculator
*/
function getWorkingHoursInSeconds(DateTime $start, DateTime $end, array $working_hours)
{
$seconds = 0; // Total working seconds
// Calculate the Start Date (Midnight) and Time (Seconds into day) as Integers.
$start_date = clone $start;
$start_date = $start_date->setTime(0, 0, 0)->getTimestamp();
$start_time = $start->getTimestamp() - $start_date;
// Calculate the Finish Date (Midnight) and Time (Seconds into day) as Integers.
$end_date = clone $end;
$end_date = $end_date->setTime(0, 0, 0)->getTimestamp();
$end_time = $end->getTimestamp() - $end_date;
// For each Day
for ($today = $start_date; $today <= $end_date; $today += 86400) {
// Get the current Weekday.
$today_weekday = date('w', $today);
// Skip to next day if no hours set for weekday.
if (!isset($working_hours[$today_weekday][0]) || !isset($working_hours[$today_weekday][1])) continue;
// Set the office hours start/finish.
$today_start = $working_hours[$today_weekday][0];
$today_end = $working_hours[$today_weekday][1];
// Adjust Start/Finish times on Start/Finish Day.
if ($today === $start_date) $today_start = min($today_end, max($today_start, $start_time));
if ($today === $end_date) $today_end = max($today_start, min($today_end, $end_time));
// Add to total seconds.
$seconds += $today_end - $today_start;
}
return gmdate("H:i:s", $seconds);
}

PHP: get next 13 dates from date?

I am trying to get an array of a date plus the next 13 dates to get a 14 day schedule starting from a given date.
here is my function:
$time = strtotime($s_row['schedule_start_date']); // 20091030
$day = 60*60*24;
for($i = 0; $i<14; $i++)
{
$the_time = $time+($day*$i);
$date = date('Y-m-d',$the_time);
array_push($dates,$date);
}
But it seems to be repeating a date when the month switches over..
this is what I get:
2009-10-30|2009-10-31|2009-11-01|2009-11-01|2009-11-02|2009-11-03|2009-11-04|2009-11-05|2009-11-06|2009-11-07|2009-11-08|2009-11-09|2009-11-10|2009-11-11
Notice that 2009-11-01 is repeated. I cannot figure out why?
What am I doing wrong?
Thanks!!
I would use strtotime
$start = strtotime($s_row['schedule_start_date']);
$dates=array();
for($i = 1; $i<=14; $i++)
{
array_push($dates,date('Y-m-d', strtotime("+$i day", $start)));
}
print_r($dates);
You have the same date because of daylight saving time switch. It's not safe to add 24*60*60 seconds to find next day, because 2 days in the year have more/less seconds in them. When you switch from summer to winter time you are adding 1 hour to a day. So it'll be 25*60*60 seconds in that day, that's why it's not switched in your code.
You can do your calculation by mktime(). For example:
## calculate seconds from epoch start for tomorrow
$tomorrow_epoch = mktime(0, 0, 0, date("m"), date("d")+1, date("Y"));
## format result in the way you need
$tomorrow_date = date("M-d-Y", $tomorrow_epoch);
Or the full version for your code:
$dates = array();
$now_year = date("Y");
$now_month = date("m");
$now_day = date("d");
for($i = 0; $i < 14; $i++) {
$next_day_epoch = mktime(0, 0, 0, $now_month, $now_day + $i, $now_year);
array_push(
$dates,
date("Y-m-d", $next_day_epoch)
);
}
I recommend something like:
for($i=1;$i<=14;$i++){
echo("$i day(s) away: ".date("m/d/Y",strtotime("+$i days")));
}

Categories