2012-01-18 4 views
4

J'ai besoin d'une méthode PHP pour calculer les heures de travail entre deux dates basées sur une journée de travail de 8 heures et excluant les week-ends et jours fériés.Calcul des heures de travail entre deux dates

Par exemple, la différence entre 2012-01-01T08:30:00 et 2012-01-05T10:30:00 du temps de travail est en fait 26 heures de travail parce que les deux premiers jours sont des vacances de week-end/bancaire qui laisse seulement 3 jours ouvrables et le differnce temps de 2 heures à savoir 3*8+2=26.

J'ai utilisé @flamingLogos excellent answer to a previous question mais je ne peux pas l'obtenir pour prendre en compte l'heure et la date.

+0

À quelle heure commence et se termine la journée de travail de 8 heures? –

+0

La journée de travail est 8h30-5h30. – user1156756

Répondre

6

Peut-être que vous pouvez utiliser cette fonction:

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.5, 17.5); // 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("\nFrom %s To %s : %d hours\n",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("\nFrom %s To %s : %d working days\n",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("\nFrom %s To %s : %d hours\n",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("\nFrom %s To %s : %d hours\n",date('d/m/y H:i',$date1),date('d/m/y H:i',$date2),$seconds/3600); 

    return $sign * $seconds/3600; // to get hours 
} 

Je mets printf() pour montrer ce qu'il est fait (vous pouvez les supprimer)

Vous appelez ça comme ça:

date_default_timezone_set("America/Los_Angeles"); 
$dt2 = strtotime("2012-01-01 05:25:00"); 
$dt1 = strtotime("2012-01-19 12:40:00"); 
echo work_hours_diff($dt1 , $dt2); 
7

La fonction ci-dessous calcule les heures de travail entre deux dates, fournies dans un format texte tel que «2013-11-27 13:40», en prenant les heures de travail de 9 à 17 (peut être modifié).

function get_working_hours($from,$to) 
{ 
    // timestamps 
    $from_timestamp = strtotime($from); 
    $to_timestamp = strtotime($to); 

    // work day seconds 
    $workday_start_hour = 9; 
    $workday_end_hour = 17; 
    $workday_seconds = ($workday_end_hour - $workday_start_hour)*3600; 

    // work days beetwen dates, minus 1 day 
    $from_date = date('Y-m-d',$from_timestamp); 
    $to_date = date('Y-m-d',$to_timestamp); 
    $workdays_number = count(get_workdays($from_date,$to_date))-1; 
    $workdays_number = $workdays_number<0 ? 0 : $workdays_number; 

    // start and end time 
    $start_time_in_seconds = date("H",$from_timestamp)*3600+date("i",$from_timestamp)*60; 
    $end_time_in_seconds = date("H",$to_timestamp)*3600+date("i",$to_timestamp)*60; 

    // final calculations 
    $working_hours = ($workdays_number * $workday_seconds + $end_time_in_seconds - $start_time_in_seconds)/86400 * 24; 

    return $working_hours; 
} 

Il existe deux fonctions supplémentaires. On revient jours ouvrables tableau ...

function get_workdays($from,$to) 
{ 
    // arrays 
    $days_array = array(); 
    $skipdays = array("Saturday", "Sunday"); 
    $skipdates = get_holidays(); 

    // other variables 
    $i = 0; 
    $current = $from; 

    if($current == $to) // same dates 
    { 
     $timestamp = strtotime($from); 
     if (!in_array(date("l", $timestamp), $skipdays)&&!in_array(date("Y-m-d", $timestamp), $skipdates)) { 
      $days_array[] = date("Y-m-d",$timestamp); 
     } 
    } 
    elseif($current < $to) // different dates 
    { 
     while ($current < $to) { 
      $timestamp = strtotime($from." +".$i." day"); 
      if (!in_array(date("l", $timestamp), $skipdays)&&!in_array(date("Y-m-d", $timestamp), $skipdates)) { 
       $days_array[] = date("Y-m-d",$timestamp); 
      } 
      $current = date("Y-m-d",$timestamp); 
      $i++; 
     } 
    } 

    return $days_array; 
} 

et seconde - retourne un tableau de vacances

function get_holidays() 
{ 
    // arrays 
    $days_array = array(); 

    // You have to put there your source of holidays and make them as array... 
    // For example, database in Codeigniter: 
    // $days_array = $this->my_model->get_holidays_array(); 

    return $days_array; 
} 
+0

Excellent excellent code, hajlabajla! On se demande cependant s'il y a une façon élégante d'utiliser la demi-journée (0900-1300) pour les samedis – Roy

0

Les deux autres propositions ne fonctionnent pas si vous choisissez début ou de fin d'une journée non-travail ou le temps . Ce sont les résultats que mon code obtient en utilisant une journée de travail de 9h00 à 20h00 et les jours de repos samedi et dimanche.

get_working_hours('2016-10-08 08:00:00', '2016-10-08 21:00:00'); //Saturday: 0 hrs 
get_working_hours('2016-10-10 08:00:00', '2016-10-10 21:00:00'); //Monday: 11 hrs 
get_working_hours('2016-10-10 10:00:00', '2016-10-10 19:00:00'); //Monday: 9 hrs 
get_working_hours('2016-10-07 19:00:00', '2016-10-10 10:00:00'); //fri-mon: 2 hrs 
get_working_hours('2016-10-08 19:00:00', '2016-10-10 10:00:00'); //sat-mon: 1 hrs 
get_working_hours('2016-10-07 19:00:00', '2016-10-09 10:00:00'); //fri-sun: 1 hrs 

function get_working_hours($ini_str,$end_str){ 
    //config 
    $ini_time = [9,0]; //hr, min 
    $end_time = [20,0]; //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 = get_workdays($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; 
    echo $ini_str.' - '.$end_str.'; Working Hours:'.($working_seconds/3600).b(); 
    return $working_seconds/3600; //return hrs 
} 

function get_workdays($ini,$end){ 
    //config 
    $skipdays = [6,0]; //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; 
} 
Questions connexes