]> git.llucax.com Git - mecon/meconlib.git/blob - lib/MECON/Feriado.php
BugFix.
[mecon/meconlib.git] / lib / MECON / Feriado.php
1 <?php /* vim: set binary expandtab tabstop=4 shiftwidth=4 textwidth=80:
2 -------------------------------------------------------------------------------
3                              Ministerio de Economía
4                                     meconlib
5 -------------------------------------------------------------------------------
6 This file is part of meconlib.
7
8 meconlib is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2 of the License, or (at your option)
11 any later version.
12
13 meconlib is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16  
17 You should have received a copy of the GNU General Public License; if not,
18 write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19 Boston, MA  02111-1307  USA
20 -------------------------------------------------------------------------------
21 Creado: mié abr 10 18:05:33 ART 2002                               |
22 Autor:  Leandro Lucarella <llucar@mecon.gov.ar>
23 -------------------------------------------------------------------------------
24 $Id$
25 -----------------------------------------------------------------------------*/
26
27 require_once 'Date.php';
28 require_once 'Date/Span.php';
29 require_once 'PEAR.php';
30
31 /**
32  * Información sobre feriados.
33  *
34  * Abstracción para hacer ABM sobre la información de los días feriados.
35  * También sirve para hacer consultas y búsquedas.
36  *
37  * @package HE
38  * @version $Rev$
39  * @author  Leandro Lucarella <llucar@mecon.gov.ar>
40  * @todo Probar. Agregar _updateDBInfo() a todas las funciones de Date que
41  *       modifiquen la fecha.
42  */
43 class MECON_Feriado extends Date {
44
45     /**
46      * Base de datos a usar para las consultas.
47      * @private
48      */
49     var $_db;
50
51     /**
52      * Tipo de día (Feriado, Laborable o No Laborable).
53      * @private
54      */
55     var $_tipo;
56
57     /// Hora en la que empieza el feriado.
58     var $desde;
59
60     /// Descripción del feriado.
61     var $descripcion;
62
63     function MECON_Feriado($db, $date = null) {
64         $this->_db = $db;
65         parent::Date($date);
66     }
67
68     function setDate($date = null) {
69         parent::setDate($date);
70         $this->_updateDBInfo($date);
71     }
72
73     function copy($date) {
74         parent::copy($date);
75         if (@$date->_tipo) {
76             $this->_tipo = $date->_tipo;
77             $this->desde = $date->desde;
78             $this->descripcion = $date->descripcion;
79         } else {
80             $this->_updateDBInfo();
81         }
82     }
83
84     function setYear($y) {
85         parent::setYear($y);
86         $this->_updateDBInfo();
87     }
88
89     function setMonth($m) {
90         parent::setMonth($m);
91         $this->_updateDBInfo();
92     }
93
94     function setDay($d) {
95         parent::setDay($d);
96         $this->_updateDBInfo();
97     }
98
99     function setHour($h) {
100         parent::setHour($h);
101         $this->_updateDBInfo();
102     }
103
104     function setMinute($m) {
105         parent::setMinute($m);
106         $this->_updateDBInfo();
107     }
108
109     function setSecond($s) {
110         parent::setSecond($s);
111         $this->_updateDBInfo();
112     }
113
114     function _updateDBInfo() {
115         // Resetea valores.
116         $this->desde = null;
117         $this->descripcion = null;
118         // Se fija según el tipo de día.
119         $dia = $this->format('%w');
120         switch ($dia) {
121             case 0: // Domingo.
122                 $this->_tipo = 'feriado';
123                 break;
124             case 6: // Sábado.
125                 $tipo = 'no laborable';
126                 // sigue abajo, no hay break.
127             default: // Cualquier día.
128                 $this->_tipo = isset($tipo) ? $tipo : 'laborable';
129                 // Verificamos si hay algo en la DB.
130                 $fecha = $this->getDate();
131                 $datos = $this->_db->getRow("SELECT * FROM novedades.feriados WHERE fecha = '".$fecha."'", null, DB_FETCHMODE_ASSOC);
132                 // Si hay un error, lo pasamos a quien nos llama.
133                 if (DB::isError($datos)) {
134                     return $datos;
135                 // Si hay un resultado, actualizamos los datos.
136                 } elseif ($datos) {
137                     $this->_tipo = $datos['tipo'];
138                     $this->desde = $datos['desde'];
139                     $this->descripcion = $datos['descripcion'];
140                 }
141         }
142         return true;
143     }
144
145     /**
146      * Obtiene el tipo de día (teniendo en cuenta la hora).
147      * 
148      * Si el día es asueto se fija la hora almacenada en esta fecha y devuelve
149      * 'asueto' o 'laborable' dependiendo de a partir de qué hora se declaró el
150      * asueto.
151      *
152      * @return 'laborable', 'no laborable', 'asueto' o 'feriado'.
153      */
154     function getTipo() {
155         if ($this->_tipo == 'asueto') {
156             $desde = new Date_Span($this->desde);
157             $hora = new Date_Span($this->format('%H:%M'));
158             if ($hora->greaterEqual($desde)) {
159                 return $this->_tipo;
160             } else {
161                 // Posible pitfall, en teoría sólo puede declararse asueto un
162                 // día laborable.
163                 return 'laborable';
164             }
165         } else {
166             return $this->_tipo;
167         }
168     }
169
170     /**
171      * Indica si un día es laborable.
172      *
173      * @return bool   false si no es laborable (o hubo error).
174      *
175      * @access public
176      */
177     function esLaborable() {
178         return $this->getTipo() == 'laborable';
179     }
180
181     /**
182      * Indica si un día es no laborable.
183      *
184      * @return bool   false si no es no laborable (o hubo error).
185      *
186      * @access public
187      */
188     function esNoLaborable() {
189         return $this->getTipo() == 'no laborable';
190     }
191
192     /**
193      * Indica si un día es feriado.
194      *
195      * @return bool   false si no es feriado (o hubo error).
196      *
197      * @access public
198      */
199     function esFeriado() {
200         return $this->getTipo() == 'feriado';
201     }
202
203     /**
204      * Indica si un día es asueto.
205      *
206      * @param  string $fecha Fecha de a chequear (el día actual por
207      *                       defecto).
208      * @param  string $hora  Hora en la que se quiere chequear.
209      *
210      * @return bool   false si no es asueto (o hubo error).
211      *
212      * @access public
213      */
214     function esAsueto() {
215         return $this->getTipo() == 'asueto';
216     }
217
218     /**
219      * Busca feriados que se encuentren en un rango de fechas.
220      *
221      * Ejemplo:
222      * @code
223      * if ($feriado->buscarRango('2002/10/01', '2002/12/31')) {
224      *     while ($f = $feriado->siguiente())
225      *         var_dump( $f );
226      * } elseif ($feriado->error()) {
227      *     trigger_error('Errores: ' . $feriado->errores(), E_USER_ERROR);
228      * } else {
229      *     echo 'No se encontraron feriados en ese rango de fechas';
230      * }
231      * @endcode
232      *
233      * @param  string $ini   Fecha de inicio del rango en donde buscar (por
234      *                       defecto el primer día del mes en curso).
235      * @param  string $fin   Fecha de fin del rango en donde buscar (por
236      *                       defecto el último día del mes en curso).
237      * @param  array $orden  Órden de los resultados. Es un array
238      *                       asociativo con el campo por el cual ordenar
239      *                       como clave y si es descendente o ascendente
240      *                       como valor. Por defecto se toma fecha
241      *                       ascendente.
242      *
243      * @return bool  false si no se encontró nada (o hubo error).
244      *
245      * @todo Pasar a método estático y usar Date en vez de strings para las fechas.
246      */
247     function buscarRango($ini = '', $fin = '', $orden = null ) {
248
249         $ini = empty( $ini ) ? strftime( '%Y/01/01', time() ) : $ini;
250         $fin = empty( $fin ) ? strftime( '%Y/12/31', time() ) : $fin;
251         $ord = ( $ord != 'fecha' and $ord != 'descripcion' and $ord != 'tipo' ) ? 'fecha' : $ord;
252
253         // Valida ambas fechas y que una sea anterior a la otra.
254         if ( ! $this->_es_anterior( $ini, $fin ) )
255             return false;
256
257         // Si no se especifica orden lo inicializa.
258         if ( is_null( $orden ) )
259             $orden = array('fecha' => HE_DB_ASC);
260
261         // Genera un array con las clausulas del ORDER BY del query
262         $order = array();
263         foreach ( $orden as $campo => $ord )
264             $order[] = sprintf( "%s %s", $campo, $ord );
265             
266         // Si no son arrays sale con error.
267         if ( ! is_array( $campos ) or ! is_array( $orden ) ) {
268             $this->raiseError( 'Parámetro incorrecto, no es un array', HE_ERROR );
269             return false;
270         }
271
272         // Prepara el query con el rango de fechas
273         $query = sprintf(
274             'SELECT *
275                 FROM
276                     %s.%s
277                 WHERE
278                     %s <= fecha AND
279                     fecha <= %s
280                 %s',
281             $this->_basededatos,
282             $this->_tabla,
283             $this->_db->quote( $ini ),
284             $this->_db->quote( $fin ),
285             $order ? ( ' ORDER BY ' . join( ',', $order ) ) : ''
286         );
287
288         $resultado =& $this->_db->query( $query );
289         if ( DB::isError( $resultado ) ) {   // Hubo un error
290             $this->_error->agregar( $resultado );
291             return false;
292         } elseif ( $resultado->numRows() ) { // Tiene algún resultado
293             $this->_resultado =& $resultado;
294             return true;
295         } else {
296             return false;
297         }
298
299     }
300
301     /**
302      * Busca dias habiles en un rango de fechas y los devuelve en un array.
303      *
304      * Ejemplo:
305      * @code
306      * $diasHabiles = $feriado->buscarDiasHabiles('2002/10/01', '2002/12/31');
307      * if (count($diasHabiles)) {
308      *     echo 'Cantidad de dias habiles en le rango: '. count($diasHabiles);
309      *     foreach ($diasHabiles as $diaHabil) {
310      *         print '<PRE>';var_dump($diaHabil);print '</PRE>';
311      *     }
312      * }
313      * @endcode
314      *
315      * @param  DB    $db    Conexión a la base de datos.
316      * @param  mixed $ini   Fecha de inicio del rango en donde buscar (por
317      *                      defecto el primer día del mes en curso). Puede ser
318      *                      un objeto Date.
319      * @param  mixed $fin   Fecha de fin del rango en donde buscar (por
320      *                      defecto el día del mes en curso). Puede ser un 
321      *                      objeto Date.
322      *
323      * @return mixed
324      * @static
325      */
326     function buscarDiasHabiles($db, $ini = '', $fin = '') {
327         //Acomodo las fechas pasadas por parametro
328         $ini = empty( $ini ) ? strftime( '%Y-%m-01', time() ) : $ini;
329         $fin = empty( $fin ) ? strftime( '%Y-%m-%d', time() ) : $fin;
330         $ini = (is_string($ini)) ? new Date($ini) : $ini;
331         $fin = (is_string($fin)) ? new Date($fin) : $fin;
332         
333         //Valido que $ini < $fin
334         if ($ini->after($fin)) {
335             return new PEAR_Error ('La fecha de fin debe ser anterior a la '.
336                     'fecha de inicio.');
337         }
338         
339         //Obtener los dias feriados entre $ini y $fin
340         $sql = "SELECT fecha FROM novedades.feriados WHERE YEAR(fecha) = ". 
341             $db->quote($ini->format("%Y")) . " OR YEAR(fecha) = " . 
342             $db->quote($ini->format("%Y"));
343
344         $feriados = $db->getCol($sql);
345         if (DB::IsError($feriados)) {
346             return $feriados;
347         }
348         
349         //Verifico fecha por fecha si es o no un dia habil
350         $ini =& $ini->getPrevDay();
351         while (!$ini->equals($fin)) {
352             $ini =& $ini->getNextDay();
353             //Verifico que la fecha no sea feriado ni sabado o domingo.
354             if (!in_array($ini->format("%Y-%m-%d"), $feriados) &&
355                     ($ini->getDayOfWeek() != 0) && 
356                     ($ini->getDayOfWeek() != 6)) {
357                 $res[] = $ini;
358             }
359         }
360         return $res;
361     }
362 }
363
364 // $Id$
365 ?>