lunes, 20 de diciembre de 2010

Calcular días habiles con php

Como calcular dias habiles incluyendo fines de semana y feriados locales, debo confesar que al al principio se me hizo complicado el asunto pero luego  piace of cake.

1.- Pasar la fecha inicial y final a maketime y obtener un arreglo con todas los días intermedios.

function DiasHabiles($fecha_inicial,$fecha_final)
{
list($dia,$mes,$year) = explode("-",$fecha_inicial);
$ini = mktime(0, 0, 0, $mes , $dia, $year);
list($diaf,$mesf,$yearf) = explode("-",$fecha_final);
$fin = mktime(0, 0, 0, $mesf , $diaf, $yearf);

$r = 1;
while($ini != $fin)
{
$ini = mktime(0, 0, 0, $mes , $dia+$r, $year);
$newArray[] .=$ini; 
$r++;
}
return $newArray;
}

2.- Una función que evalué el arreglo de fechas obtenido, que contenga los feriados nacionales que correspondan (restando) y que reste los sábados y domingos.

function Evalua($arreglo)
{
$feriados        = array(
'1-1',  //  Año Nuevo (irrenunciable)
'10-4',  //  Viernes Santo (feriado religioso)
'11-4',  //  Sábado Santo (feriado religioso)
'1-5',  //  Día Nacional del Trabajo (irrenunciable)
'21-5',  //  Día de las Glorias Navales
'29-6',  //  San Pedro y San Pablo (feriado religioso)
'16-7',  //  Virgen del Carmen (feriado religioso)
'15-8',  //  Asunción de la Virgen (feriado religioso)
'18-9',  //  Día de la Independencia (irrenunciable)
'19-9',  //  Día de las Glorias del Ejército
'12-10',  //  Aniversario del Descubrimiento de América
'31-10',  //  Día Nacional de las Iglesias Evangélicas y Protestantes (feriado religioso)
'1-11',  //  Día de Todos los Santos (feriado religioso)
'8-12',  //  Inmaculada Concepción de la Virgen (feriado religioso)
'13-12',  //  elecciones presidencial y parlamentarias (puede que se traslade al domingo 13)
'25-12',  //  Natividad del Señor (feriado religioso) (irrenunciable)
);

$j= count($arreglo);

for($i=0;$i<=$j;$i++)
{
$dia = $arreglo[$i];

        $fecha = getdate($dia);
            $feriado = $fecha['mday']."-".$fecha['mon'];
                    if($fecha["wday"]==0 or $fecha["wday"]==6)
                    {
                        $dia_ ++;
                    }
                        elseif(in_array($feriado,$feriados))
                        {   
                            $dia_++;
                        }
}
$rlt = $j - $dia_;
return $rlt;
}


3.- Se llama a las funciones.

  $CantidadDiasHabiles = Evalua(DiasHabiles('19-10-2010','28-12-2010'));

echo   $CantidadDiasHabiles;


Si tiene solución mas simple, favor publiquen.


Saludos

23 comentarios:

  1. el codigo esta muy interesante pero cuando lo intento poner a prueba me vota un error un la declaracion de la variable $newArray, otro en la variable $dia_ ++ y un undefinied offset en
    $dia = $arreglo[$i]; me gustaria que me dieras una pronta solucion pues tu codigo me interesa muchisimo pero no lo puedo poner a funcionar de antemano muchas gracias e intentare seguir revisando el codigo por mi cuenta mientras tanto.....

    ResponderEliminar
    Respuestas
    1. <?php

      //1.- Pasar la fecha inicial y final a maketime y obtener un arreglo con todas los días intermedios.

      function DiasHabiles($fecha_inicial,$fecha_final)
      {
      $newArray = array();
      list($year,$mes,$dia) = explode("-",$fecha_inicial);
      $ini = mktime(0, 0, 0, $mes , $dia, $year);
      list($yearf,$mesf,$diaf) = explode("-",$fecha_final);
      $fin = mktime(0, 0, 0, $mesf , $diaf, $yearf);

      $r = 1;
      while($ini != $fin)
      {
      $ini = mktime(0, 0, 0, $mes , $dia+$r, $year);
      echo "ini = ". $ini ."
      ";
      $newArray[] .=$ini;
      $r++;
      }
      return $newArray;
      }

      //2.- Una función que evalué el arreglo de fechas obtenido, que contenga los feriados nacionales que correspondan (restando) y que reste los sábados y domingos.

      function Evalua($arreglo)
      {
      $feriados = array(
      '1-1', // Año Nuevo (irrenunciable)
      '10-4', // Viernes Santo (feriado religioso)
      '11-4', // Sábado Santo (feriado religioso)
      '1-5', // Día Nacional del Trabajo (irrenunciable)
      '21-5', // Día de las Glorias Navales
      '29-6', // San Pedro y San Pablo (feriado religioso)
      '16-7', // Virgen del Carmen (feriado religioso)
      '15-8', // Asunción de la Virgen (feriado religioso)
      '19-9', // Dia Festivo De Prueba EN EL EJEMPLO <-----
      '12-10', // Aniversario del Descubrimiento de América
      '31-10', // Día Nacional de las Iglesias Evangélicas y Protestantes (feriado religioso)
      '1-11', // Día de Todos los Santos (feriado religioso)
      '8-12', // Inmaculada Concepción de la Virgen (feriado religioso)
      '13-12', // elecciones presidencial y parlamentarias (puede que se traslade al domingo 13)
      '25-12', // Natividad del Señor (feriado religioso) (irrenunciable)
      );

      $j= count($arreglo);
      $dia_NoLab = 0;
      $dia = 0;

      for($i=0;$i<$j;$i++)
      {
      $dia = $arreglo[$i];

      $fecha = getdate($dia);
      $feriado = $fecha['mday']."-".$fecha['mon'];
      if($fecha["wday"]==0 or $fecha["wday"]==6)
      {
      $dia_NoLab++;
      echo "Dia Fin de Semana ". $feriado ."
      ";
      }
      elseif(in_array($feriado,$feriados))
      {
      $dia_NoLab++;
      echo "Dia Festivo dentro del Arreglo de Festivos ". $feriado ."
      ";
      }
      }
      $rlt = $j - $dia_NoLab;
      echo "j= ". $j ." i= ". $i ."
      ";
      return $rlt;
      }

      Eliminar
  2. me acabo de dar cuenta que los errores no influyen en el resulatdo, solo que son muy molestos y me gustaria que no apareciera...y pues tuve que realizar algunas modificaciones porque mi formato de fecha es yyyy/mm/dd pero no me esta tomando los festivos... no se que hacer por favor ayudame...

    ResponderEliminar
    Respuestas
    1. Para usar en formato yyyy/mm/dd debes modificar las siguientes lineas:
      list($dia,$mes,$year) = explode("-",$fecha_inicial);
      list($diaf,$mesf,$yearf) = explode("-",$fecha_final);
      por
      list($year,$mes,$dia) = explode("/",$fecha_inicial);
      list($yearf,$mesf,$diaf) = explode("/",$fecha_final);

      Eliminar
    2. Felipe, yo necesito hacer lo mismo, pero modifiqué esto y no funcionó.... por favor me podrías ayudar?

      Eliminar
  3. puedes poner
    $dia = $arreglo["$i"];

    con las comillas no te saldra la advertencia

    ResponderEliminar
  4. MUCHAS GRACIAS POR TODO...ME SIRVIO DE MUCHO EL CODIGO..

    ResponderEliminar
  5. Saludos, hice algunas modificaciones sobre los errores y trabaja al 100% asi:

    ResponderEliminar
    Respuestas
    1. Hola, podrias Publicar por favor las modificaciones que le hiciste a codigo?, se que es un tema viejo pero me urge este script

      Eliminar
  6. esta bueno el código, cambie los feriados a los de mi país (Bolivia) y funciona al 100%

    ResponderEliminar
  7. Un aporte de mi parte

    /**
    * Metodo getDiasHabiles
    *
    * Permite devolver un arreglo con los dias habiles
    * entre el rango de fechas dado excluyendo los
    * dias feriados dados (Si existen)
    *
    * @param string $fechainicio Fecha de inicio en formato Y-m-d
    * @param string $fechafin Fecha de fin en formato Y-m-d
    * @param array $diasferiados Arreglo de dias feriados en formato Y-m-d
    * @return array $diashabiles Arreglo definitivo de dias habiles
    */
    public function getDiasHabiles($fechainicio, $fechafin, $diasferiados = array()) {
    // Convirtiendo en timestamp las fechas
    $fechainicio = strtotime($fechainicio);
    $fechafin = strtotime($fechafin);

    // Incremento en 1 dia
    $diainc = 24*60*60;

    // Arreglo de dias habiles, inicianlizacion
    $diashabiles = array();

    // Se recorre desde la fecha de inicio a la fecha fin, incrementando en 1 dia
    for ($midia = $fechainicio; $midia <= $fechafin; $midia += $diainc) {
    // Si el dia indicado, no es sabado o domingo es habil
    if (!in_array(date('N', $midia), array(6,7))) { // DOC: http://www.php.net/manual/es/function.date.php
    // Si no es un dia feriado entonces es habil
    if (!in_array(date('Y-m-d', $midia), $diasferiados)) {
    array_push($diashabiles, date('Y-m-d', $midia));
    }
    }
    }

    return $diashabiles;
    }

    ResponderEliminar
    Respuestas
    1. Mejor visualizacion aqui: http://pastebin.com/sKkn61L4

      Eliminar
    2. Este comentario ha sido eliminado por el autor.

      Eliminar
  8. excelente aportación, funcional, fácil de entender e implementar, gracias!!!

    ResponderEliminar
  9. muy buena, era justo lo que buscaba, lo malo es que cuenta un dia menos en cada mes, al final del "Evalua" le agregué un +1 y quedo bien.

    ResponderEliminar
  10. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
    Respuestas
    1. encontre una forma de calcular los dias habiles entre dos fechas incluyendo los festivos para colombia, como el codigo es largo no le deja pegar todo aqui, pero esta en este lugar

      http://pastebin.com/7pdxKy3z

      Eliminar
  11. Perfecto una gran ayuda, yo solo buscaba los dias habiles
    Así que tuve que modificar ese código, ademas de corregir las notificaciones que salen de php, la fecha yo la utilizo
    YYYY-MM-DD.
    Muchas gracias aquí dejare el código resumido en una sola función y una disculpa por modificar muchas cosas:

    function buscaDiasHabiles($fecha_inicial,$fecha_final){
    list($year,$mes,$dia) = explode("-",$fecha_inicial);
    $ini = mktime(0, 0, 0, $mes , $dia, $year);
    list($yearf,$mesf,$diaf) = explode("-",$fecha_final);
    $fin = mktime(0, 0, 0, $mesf , $diaf, $yearf);
    $newArray = array();
    $r = 1; $i = 0; $dia2 = 0;
    while($ini != $fin){
    $ini = mktime(0, 0, 0, $mes , $dia+$r, $year);
    $newArray[$i]=$ini;
    $r++;$i++;
    }
    for($i=0;$i<count($newArray); $i++){
    $dia = $newArray[$i];
    $fecha = getdate($dia);
    if($fecha["wday"]==0 or $fecha["wday"]==6){
    $dia2++;
    }
    }
    $rlt = count($newArray) - $dia2;
    return $rlt;
    }

    ResponderEliminar
  12. EL codigo se ajustaba a mi necesidad, pero no se porque lo suben con errores, igual ya lo ajuste un poco y le elimine los errores aca les dejo mi codigo
    ";

    //1.- Pasar la fecha inicial y final a maketime y obtener un arreglo con todas los días intermedios.

    function DiasHabiles($fecha_inicial,$fecha_final)
    {
    $newArray = array();
    list($year,$mes,$dia) = explode("-",$fecha_inicial);
    $ini = mktime(0, 0, 0, $mes , $dia, $year);
    list($yearf,$mesf,$diaf) = explode("-",$fecha_final);
    $fin = mktime(0, 0, 0, $mesf , $diaf, $yearf);

    $r = 1;
    while($ini != $fin)
    {
    $ini = mktime(0, 0, 0, $mes , $dia+$r, $year);
    echo "ini = ". $ini ."
    ";
    $newArray[] .=$ini;
    $r++;
    }
    return $newArray;
    }

    //2.- Una función que evalué el arreglo de fechas obtenido, que contenga los feriados nacionales que correspondan (restando) y que reste los sábados y domingos.

    function Evalua($arreglo)
    {
    $feriados = array(
    '1-1', // Año Nuevo (irrenunciable)
    '10-4', // Viernes Santo (feriado religioso)
    '11-4', // Sábado Santo (feriado religioso)
    '1-5', // Día Nacional del Trabajo (irrenunciable)
    '21-5', // Día de las Glorias Navales
    '29-6', // San Pedro y San Pablo (feriado religioso)
    '16-7', // Virgen del Carmen (feriado religioso)
    '15-8', // Asunción de la Virgen (feriado religioso)
    '19-9', // Dia Festivo De Prueba EN EL EJEMPLO <-----
    '12-10', // Aniversario del Descubrimiento de América
    '31-10', // Día Nacional de las Iglesias Evangélicas y Protestantes (feriado religioso)
    '1-11', // Día de Todos los Santos (feriado religioso)
    '8-12', // Inmaculada Concepción de la Virgen (feriado religioso)
    '13-12', // elecciones presidencial y parlamentarias (puede que se traslade al domingo 13)
    '25-12', // Natividad del Señor (feriado religioso) (irrenunciable)
    );

    $j= count($arreglo);
    $dia_NoLab = 0;
    $dia = 0;

    for($i=0;$i<$j;$i++)
    {
    $dia = $arreglo[$i];

    $fecha = getdate($dia);
    $feriado = $fecha['mday']."-".$fecha['mon'];
    if($fecha["wday"]==0 or $fecha["wday"]==6)
    {
    $dia_NoLab++;
    echo "Dia Fin de Semana ". $feriado ."
    ";
    }
    elseif(in_array($feriado,$feriados))
    {
    $dia_NoLab++;
    echo "Dia Festivo dentro del Arreglo de Festivos ". $feriado ."
    ";
    }
    }
    $rlt = $j - $dia_NoLab;
    echo "j= ". $j ." i= ". $i ."
    ";
    return $rlt;
    }

    ?>

    ResponderEliminar
    Respuestas
    1. No me funciona :(
      Si quisiera sacar el primer día hábil, incluyendo de manera manual los festivos.. Cómo seria? tendría que meter los festivos en un array, pero como?
      modify('first day of this month');
      echo $fecha->format('d');
      echo $fecha->format('m');
      echo $fecha->format('Y');
      echo "
      ";
      echo "
      ";
      $fecha->modify('last day of this month');
      echo $fecha->format('d');
      echo $fecha->format('m');
      echo $fecha->format('Y');
      >?

      Eliminar
  13. No me funciona ningún código, me faltarà algo? help¡

    ResponderEliminar
  14. https://gist.github.com/angelmartz/7694083

    ResponderEliminar