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

React Advanced JSX

 class vs className This lesson will cover more advanced JSX. You’ll learn some powerful tricks and some common errors to avoid. Grammar in JSX is mostly the same as in HTML, but there are subtle differences to watch out for. The most frequent of these involves the word class. In HTML, it’s common to use class as an attribute name: <h1 class = "big" > Title </h1> In JSX, you can’t use the word  class ! You have to use  className  instead: <h1 className = "big" > Title </h1> This is because JSX gets translated into JavaScript, and  class  is a reserved word in JavaScript. When JSX is  rendered , JSX  className  attributes are automatically rendered as  class  attributes. Self-Closing Tags Another common JSX error involves  self-closing tags . What’s a self-closing tag? Most HTML elements use two tags: an  opening tag  ( <div> ), and a  closing tag  ( </div> ). However, some HTML elements such as  <img>  and  <input>  u