lunes, 28 de diciembre de 2015

Calculo de días festivos para Colombia

El siguiente post tiene como fin, publicar las funciones necesarias para calcular los días festivos en Colombia. Esta desarrollado en T-SQL compatible con Sql Server 2008 o superior.

Está compuesto por 5 funciones de las cuales tres son para acciones auxiliares y las dos restantes permiten listar los festivos de un año determinado o verificar si una fecha es festivo.

Espero sea de utilidad.

1. Función que nos permite crear una fecha a partir del día, mes y año. Para evitar problemas con la configuración regional.

DROP FUNCTION F_CREAR_FECHA
GO
CREATE FUNCTION F_CREAR_FECHA(@ANIO INT, @MES INT, @DIA INT)
RETURNS DATE
AS
BEGIN
 RETURN DATEADD(mm, (@ANIO - 1900) * 12 + @MES - 1 , @DIA - 1)
END
2. Función que a partir de una fecha obtiene el siguiente lunes.


DROP FUNCTION F_OBTENER_SIGUIENTE_LUNES
GO
CREATE FUNCTION F_OBTENER_SIGUIENTE_LUNES(@FECHA DATE)
RETURNS DATE
AS
BEGIN
 DECLARE @LUNES INT = 1
 IF @@DATEFIRST = 7 
 BEGIN 
  SET @LUNES = 2
 END
 IF @@DATEFIRST = 1 
 BEGIN 
  SET @LUNES = 1
 END
 IF @@DATEFIRST = 2 
 BEGIN 
  SET @LUNES = 7
 END
 IF @@DATEFIRST = 3 
 BEGIN 
  SET @LUNES = 6
 END 
 IF @@DATEFIRST = 4 
 BEGIN 
  SET @LUNES = 5
 END   
 IF @@DATEFIRST = 5 
 BEGIN 
  SET @LUNES = 4
 END    
 IF @@DATEFIRST = 6
 BEGIN 
  SET @LUNES = 3
 END     
 DECLARE @FECHA_DEVOLVER  DATE
 IF DATEPART(DW, @FECHA) = @LUNES
 BEGIN
  SET @FECHA_DEVOLVER = @FECHA
 END
 ELSE
 BEGIN
  SET @FECHA_DEVOLVER = DATEADD([day], ((DATEDIFF([day], '19000101', @FECHA) / 7) * 7) + 7, '19000101');          
 END 
 RETURN @FECHA_DEVOLVER
END
3. Función que calcula el domingo de pascua para un año en particular.
DROP FUNCTION F_CALCULA_DOMINGO_PASCUA
GO
CREATE FUNCTION F_CALCULA_DOMINGO_PASCUA(@A_ANO INT)
RETURNS DATE
AS
BEGIN
 DECLARE @M  INT = 25
 DECLARE @N  INT = 5
 DECLARE @A  INT
 DECLARE @B  INT 
 DECLARE @C  INT 
 DECLARE @D  INT 
 DECLARE @E  INT 
 DECLARE @DIA INT
 DECLARE @MES INT
 
 IF  (@A_ANO >= 1583 AND @A_ANO <= 1699)  
 BEGIN
   SET @M = 22
   SET @N = 2
 END 
 ELSE IF (@A_ANO >= 1700 AND @A_ANO <= 1799)  
 BEGIN 
  SET @M = 23
  SET @N = 3 
 END
 ELSE IF (@A_ANO >= 1800 AND @A_ANO <= 1899)  
 BEGIN
  SET @M = 23
  SET @N = 4 
 END
 ELSE IF (@A_ANO >= 1900 AND @A_ANO <= 2099)  
 BEGIN
  SET @M = 24
  SET @N = 5 
 END
 ELSE IF (@A_ANO >= 2100 AND @A_ANO <= 2199)  
 BEGIN 
  SET @M = 24
  SET @N = 6; 
 END 
 ELSE IF (@A_ANO >= 2200 AND @A_ANO <= 2299)  
 BEGIN
  SET @M = 25
  SET @N = 0
 END  
 
 SET @A = @A_ANO % 19
 SET @B = @A_ANO % 4
 SET @C = @A_ANO % 7
 SET @D = (19 * @A + @M) % 30;
 SET @E = (2 * @B + 4 * @C + 6 * @D + @N) % 7; 

 
 IF (@D + @E < 10) 
 BEGIN
  SET @DIA = @D + @E + 22
  SET @MES = 3
 END
 ELSE 
 BEGIN 
  SET @DIA = @D + @E - 9
  SET @MES = 4
 END
 
 
 IF (@DIA = 26 AND @MES = 4)
 BEGIN 
  SET @DIA = 19
 END
 IF (@DIA = 25 AND @MES = 4 AND @D = 28 AND @E = 6 AND @A > 10)
 BEGIN
  SET @DIA = 18
 END
  
 RETURN dbo.F_CREAR_FECHA(@A_ANO, @MES, @DIA);
END

4. Función Recibe un año y retorna el listado de los festivos para ese año.
DROP FUNCTION F_LISTA_FESTIVOS
GO
CREATE FUNCTION F_LISTA_FESTIVOS(@ANIO INT)
 RETURNS @ListaFestivos TABLE(FechaFestivo DATE, Descripcion VARCHAR(50))
AS
BEGIN
 DECLARE @PASCUA DATE = dbo.F_CALCULA_DOMINGO_PASCUA(@ANIO)
 DECLARE @ES_FESTIVO BIT = 0
 
 
 -- Festivos Fecha Fija 
   INSERT INTO @ListaFestivos VALUES(dbo.F_CREAR_FECHA(@ANIO, 1, 1), 'Año Nuevo')
   INSERT INTO @ListaFestivos VALUES(dbo.F_CREAR_FECHA(@ANIO, 5, 1), 'Día del Trabajo')
   INSERT INTO @ListaFestivos VALUES(dbo.F_CREAR_FECHA(@ANIO, 7, 20), 'Grito de la Independencia')
   INSERT INTO @ListaFestivos VALUES(dbo.F_CREAR_FECHA(@ANIO, 8, 7), 'Batalla de Boyacá')   
   INSERT INTO @ListaFestivos VALUES(dbo.F_CREAR_FECHA(@ANIO, 12, 8), 'Inmaculada Concepción')      
   INSERT INTO @ListaFestivos VALUES(dbo.F_CREAR_FECHA(@ANIO, 12, 25), 'Navidad') 
 -- Festivos Siguiente Lunes
   INSERT INTO @ListaFestivos VALUES(dbo.F_OBTENER_SIGUIENTE_LUNES(dbo.F_CREAR_FECHA(@ANIO, 1, 6)), 'Epifanía (Reyes Magos)')
   INSERT INTO @ListaFestivos VALUES(dbo.F_OBTENER_SIGUIENTE_LUNES(dbo.F_CREAR_FECHA(@ANIO, 3, 19)), 'San José esposo de la virgen María')   
   INSERT INTO @ListaFestivos VALUES(dbo.F_OBTENER_SIGUIENTE_LUNES(dbo.F_CREAR_FECHA(@ANIO, 6, 29)), 'San Pedro y San Pablo')      
   INSERT INTO @ListaFestivos VALUES(dbo.F_OBTENER_SIGUIENTE_LUNES(dbo.F_CREAR_FECHA(@ANIO, 8, 15)), 'Asunción de la Virgen')         
   INSERT INTO @ListaFestivos VALUES(dbo.F_OBTENER_SIGUIENTE_LUNES(dbo.F_CREAR_FECHA(@ANIO, 10, 12)), 'Día de la Raza')            
   INSERT INTO @ListaFestivos VALUES(dbo.F_OBTENER_SIGUIENTE_LUNES(dbo.F_CREAR_FECHA(@ANIO, 11, 1)), 'Todos los Santos')               
   INSERT INTO @ListaFestivos VALUES(dbo.F_OBTENER_SIGUIENTE_LUNES(dbo.F_CREAR_FECHA(@ANIO, 11, 11)), 'Independencia de Cartagena')                  
 -- Festivos Dependientes de la pascua
   INSERT INTO @ListaFestivos VALUES(DATEADD(DAY,-7,@PASCUA), 'Domingo De Ramos')                  
   INSERT INTO @ListaFestivos VALUES(DATEADD(DAY,-3,@PASCUA), 'Jueves Santo')                     
   INSERT INTO @ListaFestivos VALUES(DATEADD(DAY,-2,@PASCUA), 'Viernes Santo')                        
   INSERT INTO @ListaFestivos VALUES(@PASCUA, 'Domingo De Pascua')                           
   INSERT INTO @ListaFestivos VALUES(DATEADD(DAY,43,@PASCUA), 'Ascensión de Jesús') 
   INSERT INTO @ListaFestivos VALUES(DATEADD(DAY,64,@PASCUA), 'Corpus Christi')    
   INSERT INTO @ListaFestivos VALUES(DATEADD(DAY,71,@PASCUA), 'Sagrado Corazón de Jesús')       

 RETURN;
END
5.Función Recibe una fecha y retorna un Booleano indicando si es festivo o no
DROP FUNCTION F_CALCULA_ES_FESTIVO
GO
CREATE FUNCTION F_CALCULA_ES_FESTIVO(@FECHA DATE)
RETURNS BIT
AS
BEGIN
 DECLARE @ES_FESTIVO BIT = 0
 IF EXISTS (SELECT * FROM dbo.F_LISTA_FESTIVOS(YEAR(@FECHA)) WHERE FechaFestivo = @FECHA) 
 BEGIN
  SET @ES_FESTIVO = 1
 END
 RETURN (@ES_FESTIVO);
END
Por último la manera de verificar las funciones:


SELECT dbo.F_CALCULA_ES_FESTIVO(CONVERT(DATE, '01/01/2016')) EsFestivo
SELECT * FROM dbo.F_LISTA_FESTIVOS(2016) ORDER BY FechaFestivo

Y el resultado de las consultas seria el siguiente:



4 comentarios:

  1. Excelente algoritmo y explicaciones ¡¡¡

    ResponderEliminar
  2. Hola, saca mucho errores, no ha sido posible probarla:

    Mens 156, Nivel 15, Estado 1, Procedimiento F_CREAR_FECHA, Línea 13
    Sintaxis incorrecta cerca de la palabra clave 'CREATE'.
    Mens 111, Nivel 15, Estado 1, Procedimiento F_CREAR_FECHA, Línea 13
    'CREATE FUNCTION' debe ser la primera instrucción en un lote de consultas.
    Mens 137, Nivel 15, Estado 2, Procedimiento F_CREAR_FECHA, Línea 48
    Debe declarar la variable escalar "@FECHA".
    Mens 137, Nivel 15, Estado 2, Procedimiento F_CREAR_FECHA, Línea 50
    Debe declarar la variable escalar "@FECHA".
    Mens 137, Nivel 15, Estado 2, Procedimiento F_CREAR_FECHA, Línea 54
    Debe declarar la variable escalar "@FECHA".
    Mens 156, Nivel 15, Estado 1, Procedimiento F_CALCULA_DOMINGO_PASCUA, Línea 138
    Sintaxis incorrecta cerca de la palabra clave 'DROP'.
    Mens 156, Nivel 15, Estado 1, Procedimiento F_LISTA_FESTIVOS, Línea 175
    Sintaxis incorrecta cerca de la palabra clave 'DROP'.
    Mens 156, Nivel 15, Estado 1, Procedimiento F_CALCULA_ES_FESTIVO, Línea 189
    Sintaxis incorrecta cerca de la palabra clave 'SELECT'.
    Mens 195, Nivel 15, Estado 10, Procedimiento F_CALCULA_ES_FESTIVO, Línea 189
    'F_CALCULA_ES_FESTIVO' no es un nombre de función integrada reconocido.

    ResponderEliminar
  3. muchas gracia por sus aportes me has ayudado, lo modifique para sql server 2005.

    ResponderEliminar
  4. Excelente!!!, no entendí la verdad por que se calculan de esa forma, tengo entendido que es basado en el método de gauss con algebra modular, lo que me pareció mas difícil fue lo del domingo de pascua.
    Lo implemente y corre de maravilla has hecho un excelente trabajo, muuuchas gracias!!!.

    ResponderEliminar