------------------------------------------------------------------------------- $Id$ -----------------------------------------------------------------------------*/ require_once 'Date.php'; require_once 'Date/Span.php'; require_once 'PEAR.php'; /** * Información sobre feriados. * * Abstracción para hacer ABM sobre la información de los días feriados. * También sirve para hacer consultas y búsquedas. * * @package HE * @version $Rev$ * @author Leandro Lucarella * @todo Probar. Agregar _updateDBInfo() a todas las funciones de Date que * modifiquen la fecha. */ class MECON_Feriado extends Date { /** * Base de datos a usar para las consultas. * @private */ var $_db; /** * Tipo de día (Feriado, Laborable o No Laborable). * @private */ var $_tipo; /// Hora en la que empieza el feriado. var $desde; /// Descripción del feriado. var $descripcion; function MECON_Feriado($db, $date = null) { $this->_db = $db; parent::Date($date); } function setDate($date = null) { parent::setDate($date); $this->_updateDBInfo($date); } function copy($date) { parent::copy($date); if (@$date->_tipo) { $this->_tipo = $date->_tipo; $this->desde = $date->desde; $this->descripcion = $date->descripcion; } else { $this->_updateDBInfo(); } } function setYear($y) { parent::setYear($y); $this->_updateDBInfo(); } function setMonth($m) { parent::setMonth($m); $this->_updateDBInfo(); } function setDay($d) { parent::setDay($d); $this->_updateDBInfo(); } function setHour($h) { parent::setHour($h); $this->_updateDBInfo(); } function setMinute($m) { parent::setMinute($m); $this->_updateDBInfo(); } function setSecond($s) { parent::setSecond($s); $this->_updateDBInfo(); } function _updateDBInfo() { // Resetea valores. $this->desde = null; $this->descripcion = null; // Se fija según el tipo de día. $dia = $this->format('%w'); switch ($dia) { case 0: // Domingo. $this->_tipo = 'feriado'; break; case 6: // Sábado. $tipo = 'no laborable'; // sigue abajo, no hay break. default: // Cualquier día. $this->_tipo = isset($tipo) ? $tipo : 'laborable'; // Verificamos si hay algo en la DB. $fecha = $this->getDate(); $datos = $this->_db->getRow("SELECT * FROM novedades.feriados WHERE fecha = '".$fecha."'", null, DB_FETCHMODE_ASSOC); // Si hay un error, lo pasamos a quien nos llama. if (DB::isError($datos)) { return $datos; // Si hay un resultado, actualizamos los datos. } elseif ($datos) { $this->_tipo = $datos['tipo']; $this->desde = $datos['desde']; $this->descripcion = $datos['descripcion']; } } return true; } /** * Obtiene el tipo de día (teniendo en cuenta la hora). * * Si el día es asueto se fija la hora almacenada en esta fecha y devuelve * 'asueto' o 'laborable' dependiendo de a partir de qué hora se declaró el * asueto. * * @return 'laborable', 'no laborable', 'asueto' o 'feriado'. */ function getTipo() { if ($this->_tipo == 'asueto') { $desde = new Date_Span($this->desde); $hora = new Date_Span($this->format('%H:%M')); if ($hora->greaterEqual($desde)) { return $this->_tipo; } else { // Posible pitfall, en teoría sólo puede declararse asueto un // día laborable. return 'laborable'; } } else { return $this->_tipo; } } /** * Indica si un día es laborable. * * @return bool false si no es laborable (o hubo error). * * @access public */ function esLaborable() { return $this->getTipo() == 'laborable'; } /** * Indica si un día es no laborable. * * @return bool false si no es no laborable (o hubo error). * * @access public */ function esNoLaborable() { return $this->getTipo() == 'no laborable'; } /** * Indica si un día es feriado. * * @return bool false si no es feriado (o hubo error). * * @access public */ function esFeriado() { return $this->getTipo() == 'feriado'; } /** * Indica si un día es asueto. * * @param string $fecha Fecha de a chequear (el día actual por * defecto). * @param string $hora Hora en la que se quiere chequear. * * @return bool false si no es asueto (o hubo error). * * @access public */ function esAsueto() { return $this->getTipo() == 'asueto'; } /** * Busca feriados que se encuentren en un rango de fechas. * * Ejemplo: * @code * if ($feriado->buscarRango('2002/10/01', '2002/12/31')) { * while ($f = $feriado->siguiente()) * var_dump( $f ); * } elseif ($feriado->error()) { * trigger_error('Errores: ' . $feriado->errores(), E_USER_ERROR); * } else { * echo 'No se encontraron feriados en ese rango de fechas'; * } * @endcode * * @param string $ini Fecha de inicio del rango en donde buscar (por * defecto el primer día del mes en curso). * @param string $fin Fecha de fin del rango en donde buscar (por * defecto el último día del mes en curso). * @param array $orden Órden de los resultados. Es un array * asociativo con el campo por el cual ordenar * como clave y si es descendente o ascendente * como valor. Por defecto se toma fecha * ascendente. * * @return bool false si no se encontró nada (o hubo error). * * @todo Pasar a método estático y usar Date en vez de strings para las fechas. */ function buscarRango($ini = '', $fin = '', $orden = null ) { $ini = empty( $ini ) ? strftime( '%Y/01/01', time() ) : $ini; $fin = empty( $fin ) ? strftime( '%Y/12/31', time() ) : $fin; $ord = ( $ord != 'fecha' and $ord != 'descripcion' and $ord != 'tipo' ) ? 'fecha' : $ord; // Valida ambas fechas y que una sea anterior a la otra. if ( ! $this->_es_anterior( $ini, $fin ) ) return false; // Si no se especifica orden lo inicializa. if ( is_null( $orden ) ) $orden = array('fecha' => HE_DB_ASC); // Genera un array con las clausulas del ORDER BY del query $order = array(); foreach ( $orden as $campo => $ord ) $order[] = sprintf( "%s %s", $campo, $ord ); // Si no son arrays sale con error. if ( ! is_array( $campos ) or ! is_array( $orden ) ) { $this->raiseError( 'Parámetro incorrecto, no es un array', HE_ERROR ); return false; } // Prepara el query con el rango de fechas $query = sprintf( 'SELECT * FROM %s.%s WHERE %s <= fecha AND fecha <= %s %s', $this->_basededatos, $this->_tabla, $this->_db->quote( $ini ), $this->_db->quote( $fin ), $order ? ( ' ORDER BY ' . join( ',', $order ) ) : '' ); $resultado =& $this->_db->query( $query ); if ( DB::isError( $resultado ) ) { // Hubo un error $this->_error->agregar( $resultado ); return false; } elseif ( $resultado->numRows() ) { // Tiene algún resultado $this->_resultado =& $resultado; return true; } else { return false; } } /** * Busca dias habiles en un rango de fechas y los devuelve en un array. * * Ejemplo: * @code * $diasHabiles = $feriado->buscarDiasHabiles('2002/10/01', '2002/12/31'); * if (count($diasHabiles)) { * echo 'Cantidad de dias habiles en le rango: '. count($diasHabiles); * foreach ($diasHabiles as $diaHabil) { * print '
';var_dump($diaHabil);print '
'; * } * } * @endcode * * @param DB $db Conexión a la base de datos. * @param mixed $ini Fecha de inicio del rango en donde buscar (por * defecto el primer día del mes en curso). Puede ser * un objeto Date. * @param mixed $fin Fecha de fin del rango en donde buscar (por * defecto el día del mes en curso). Puede ser un * objeto Date. * * @return mixed * @static */ function buscarDiasHabiles($db, $ini = '', $fin = '') { //Acomodo las fechas pasadas por parametro $ini = empty( $ini ) ? strftime( '%Y-%m-01', time() ) : $ini; $fin = empty( $fin ) ? strftime( '%Y-%m-%d', time() ) : $fin; $ini = (is_string($ini)) ? new Date($ini) : $ini; $fin = (is_string($fin)) ? new Date($fin) : $fin; //Valido que $ini < $fin if ($ini->after($fin)) { return new PEAR_Error ('La fecha de fin debe ser anterior a la '. 'fecha de inicio.'); } //Obtener los dias feriados entre $ini y $fin $sql = "SELECT fecha FROM novedades.feriados WHERE YEAR(fecha) = ". $db->quote($ini->format("%Y")) . " OR YEAR(fecha) = " . $db->quote($ini->format("%Y")); $feriados = $db->getCol($sql); if (DB::IsError($feriados)) { return $feriados; } //Verifico fecha por fecha si es o no un dia habil $ini =& $ini->getPrevDay(); while (!$ini->equals($fin)) { $ini =& $ini->getNextDay(); //Verifico que la fecha no sea feriado ni sabado o domingo. if (!in_array($ini->format("%Y-%m-%d"), $feriados) && ($ini->getDayOfWeek() != 0) && ($ini->getDayOfWeek() != 6)) { $res[] = $ini; } } return $res; } } // $Id$ ?>