Skip to main content

Business hours calculation using PHP



Most of ticketing alert projects needed SLA calculation to exclude holidays and weekends or Off business hours.
First of all, let us take a look at types of SLA we need to calculate.
For instance, few companies will work 24/7 shifts and 8x5, 8x7 etc.

So we need to consider business hours. In this example, I'm considering business hours which starts at 09:00 AM and ends at 06:00 PM. Also, using this code we can calculate 24/7 too.

Let's consider business hours started at 9:00 AM to 18:00 PM without weekends.

Now, take the actual work start period and end period.


                                                     Demo

 <?php 
 $incident_work_started = '2018-09-12 09:28:07';
 $incident_work_ended = '2018-09-19 17:45:00';
 $totalHours = round(get_working_hours($incident_work_started,$incident_work_ended),2)."\n";
 echo "Total hours: ".$totalHours;
 function get_working_hours($work_started,$work_ended)
 {
 // business  hours start at 9:00 AM to 18:00. Consider that in an array [hrs,mins]
 $buss_start = array(9,00); // hours, minutes
 $buss_end = array(18,00); // hours, minutes
 
 // Convert $work_started and $work_ended date into date object.
 // Reason to converting, To get difference of time "2018-09-12 09:28:00" to "2018-09-12 09:00:00". here will get 28 mins 
 $work_started_date_obj = date_create($work_started); // Date will converted to date object
 $work_ended_date_obj = date_create($work_ended); // Date will converted to date object
 
 //Now, set $work_started to $buss_start to get difference
 $ini_buss_start_time = date_time_set(date_create($work_started),$buss_start[0],$buss_start[1]); // "2018-09-12 09:28:00" to "2018-09-12 09:00:00"
 $ini_buss_end_time = date_time_set(date_create($work_ended),$buss_end[0],$buss_end[1]); // "2018-09-19 17:45:00" to "2018-09-19 18:00:00"
 
 // To get actual working days and skip weekends(Sat & Sun) and holidays.
 $actual_worked_days = get_workdays($work_started_date_obj,$work_ended_date_obj); 
 $actual_worked_days_count = count($actual_worked_days); 
 
 // convert business hours to seconds for calculating time difference
  $workday_seconds = (($buss_end[0] * 60 + $buss_end[1]) - ($buss_start[0] * 60 + $buss_start[1])) * 60;
 
 //echo $current->format('U')."";
 //echo date('Y/m/d H:i:s', $current->format('U'))."";
 
 //get time difference
  $ini_seconds = 0;
 $end_seconds = 0;
 
   if(in_array($work_started_date_obj->format('Y-m-d'),$actual_worked_days))
 {
  $ini_seconds = $work_started_date_obj->format('U') - $ini_buss_start_time->format('U');
  /* we will get 1687 seconds, beacuase we given date "2018-09-12 09:28:07" our business 
  hours start at 9:00:00 (h:m:s) so need take seconds from 9:00:00. Finally we get seconds
  of 9:00:00 to 09:28:07 is  1687. */
 } 
 
    if(in_array($work_ended_date_obj->format('Y-m-d'),$actual_worked_days))
 {
  $end_seconds = $ini_buss_end_time->format('U') - $work_ended_date_obj->format('U');
 } 
 
 
  $seconds_dif = $ini_seconds > 0 ? $ini_seconds : 0;
  
  if($end_seconds > 0)
  {
   $seconds_dif += $end_seconds;
  }
 
 /* Final Calculation, Hence we got total number of working days $actual_worked_days_count and $workday_seconds.
  business  start at 9:00:00 AM but here work started at 9:28:07 
  so we should subtraction the 28:07 (m:s) total 1687 seconds and business  ends at 18:00:00
  but here work closed at 17:45:00 then subtraction will get (15:00) total 900 seconds. so total 2587 seconds. */
 
 $working_seconds = ($actual_worked_days_count * $workday_seconds) - $seconds_dif; 
 /* here we get total number of working day seconds ($actual_worked_days_count * $workday_seconds)
  and subtraction none used seconds $seconds_dif will get actual working seconds. */

 echo 'Working Hours:'.($working_seconds / 3600)."\n";
 
    return $working_seconds / 3600; //return hrs 
 
 }
 
 
function get_workdays($work_started,$work_ended)
{
 //To skip saturday and sunday weekends
 $weekdays = [0,6]; // sunday:0 monday:1...saturday:6;
 $holidays = []; //eg: ['2018-12-25']; for public holidays
 
 $current = clone $work_started; // Clone is used to load date objects.
 
 // Get date format from $work_started and end date
 $start_date = $work_started->format('Y-m-d');
 $end_date = $work_ended->format('Y-m-d');
 
 // To store valid working days after avoid weekends and holidays
  $days_arr = [];
  
 //So we have $start_date, $end_date, $weekdays and $holidays. We need to take work dates between all these days.
 
 while($start_date <= $end_date){
        if(!in_array($current->format('w'),$weekdays) && !in_array($start_date,$holidays)){
            $days_arr[] = $start_date;
        }
        $current->add(new DateInterval('P1D')); //adds one day
        $start_date = $current->format('Y-m-d');
    }
    return $days_arr; // return the valid worked days 
}
?>

Comments

Post a Comment

Thank you :)

Popular posts from this blog

Laravel Commands

Laravale commands #Check route list php artisan route:list #Check upload files links php artisan storage:link #Check database connected or not php artisan db #Make Request file php artisan make:request YourNameRequest #Make Controller #(In this statement you used -r -> resources and -m -> model. It will create CustomersController and Customers Model files) php artisan make:controller CustomersController -r -m Customers #Make Resource file php artisan make:resource CustomersResource #To check migration files status that those files are running or not with below commands php artisan migrate:status #To check if there is any pending migrate files to run #(also this command shows us the mysql query before running migration file) php artisan migrate --pretend #To make a database table (in this example Products name as taken) php artisan make:migration create_products_table #To create a Request file php artisan make:request StoreProductRequest php artisan make:request Up...

Mysql columns creation in laravel

List of columns  $table->id(); // increment value $table->string('title')->comment('this is blog title'); $table->string('slug')->unique(); $table->text('short_desc'); $table->longText('description'); $table->boolean('is_published')->default(false); $table->integer('min_of_read')->nullable(true); $table->enum('status', ['Active', 'Inactive']); $table->float('discount'); $table->smallInteger('type_id'); $table->date('start_date')->nullable(); $table->timestamps(); $table->foreign('created_by')->references('id')->on('users'); // introducing foreign key $table->unsignedBigInteger('user_id'); //? $table->decimal('latitude', 9, 6)->nullable(true); // Let's say you want starting value from 1000 $table->id()->from(1000); // increment value start from 1000 ->nullabl...

Laravel form validations

 Laravel Validations: List of types "first_name" => 'required|alpha:ascii|min:3|max:100',// alpha:ascii (only accepts a-z) "middle_name" => 'string', "last_name" => 'required|string', "email" => 'required|email|unique:users,email', "password" => 'required|string|confirmed', "sex" => 'required|string', "phone_no" => 'required|string', "account_type" => 'required|string', "dob" => 'required|date_format:d-m-Y', // date with format "nationality" => 'required|string', "company" => 'required|string', "company_sector" => 'required|string', "company_address" => 'required|string' "bank_account_no" => 'required|min_digits:3|max_digits:5', "role" => 'required|in:admin,editor,viewer', ...