none
OBTENER LA FECHA DEL TERCER VIERNES DEL UN MES Y AÑO DADO RRS feed

  • Pregunta

  • Hola,

    Trabajo con SQL Sever 2014 y necesito dar forma a una función la cual me devuelva la fecha del tercer viernes para un mes y año dado.  He estado investigando y no he encontrado la forma de solventarlo.

    ¿alguna idea?

    Muchas gracias

    Un saludo

    Angel

    martes, 6 de diciembre de 2016 12:14

Respuestas

  • Este tipo de problemas se resuelva relativamente facil teniendo una tabla Calendario (ver link sgte).

    https://www.mssqltips.com/sqlservertip/4054/creating-a-date-dimension-or-calendar-table-in-sql-server/

    select [date]
    from dbo.Calendario
    where dia_semana_nombre = 'Viernes' and [date] >= '20160201' and [date] < '20160301'
    order by [date]
    offset 2 rows fetch next 1 row only;

    Otra tecnica seria dada una fecha, primer dia del mes en este caso, calcular el proximo Viernes inclusive, pero en vez del proximo calcular el tercer proximo.

    Si tenemos una fecha dada, en este caso principio de mes, si usamos una fecha ancla que fue un Viernes como '19000105' y calculamos el # de dias entre esa fecha ancla y la fecha en cuestion pero restandole un dia a esta fecha, y ahora dividimos por siete (numero completo de semanas - division de enteros) y multiplicamos este valor por siete y le sumamos siete, entonces tendriamos el proximo viernes inclusive.

    DECLARE @m date = '20160101';
    
    SELECT
    	DATEADD(DAY, (((DATEDIFF(DAY, '19000105', DATEADD(DAY, -1, @m))) / 7) * 7) + 7, '19000105') AS dt;
    GO


    Ahora, en vez de sumar siete (7) para el proximo inclusive entonces adicionar 21 (7 * 3) para el tercero.

    DECLARE @m date = '20160101';
    
    SELECT
    	DATEADD(DAY, (((DATEDIFF(DAY, '19000105', DATEADD(DAY, -1, @m))) / 7) * 7) + 21, '19000105') AS dt;
    GO

    Veamos las fechas del tercer Viernes de cada mes del 2016.

    SELECT
    	col1,
    	DATEADD(DAY, (((DATEDIFF(DAY, '19000105', DATEADD(DAY, -1, col1))) / 7) * 7) + 21, '19000105') AS fecha_tercer_viernes
    FROM
    	(
    	VALUES
    		('20160101'),
    		('20160201'),
    		('20160301'),
    		('20160401'),
    		('20160501'),
    		('20160601'),
    		('20160701'),
    		('20160801'),
    		('20160901'),
    		('20161001'),
    		('20161101'),
    		('20161201')
    	) AS T(col1)
    ORDER BY
    	col1;
    GO


    Aca te dejo el link a unos cuantos articulos sobre calculo de fechas (escritos por el maestro Itzik Ben-Gan).

    http://sqlmag.com/t-sql/datetime-calculations-part-1

    http://sqlmag.com/t-sql/datetime-calculations-part-2

    http://sqlmag.com/t-sql/datetime-calculations-part-3

    http://sqlmag.com/t-sql/datetime-calculations-part-4

    http://sqlmag.com/t-sql/datetime-calculations-part-5


    AMB

    Some guidelines for posting questions...

    AYÚDANOS A AYUDARTE, guía básica de consejos para formular preguntas


    • Editado HunchbackMVP martes, 6 de diciembre de 2016 14:37
    • Propuesto como respuesta Willams Morales martes, 6 de diciembre de 2016 15:27
    • Marcado como respuesta Angeleci martes, 6 de diciembre de 2016 20:06
    martes, 6 de diciembre de 2016 14:27
  • Hombre, pasale el primer dia del mes como identificador de mes. Tambien puedes tomar el anio y mes y convertirlo a fecha.

    CREATE FUNCTION dbo.ufn_TercerViernesDelMes(
    @y int,
    @m tinyint
    )
    RETURNS table
    AS
    RETURN (
    SELECT DATEADD(DAY, (((DATEDIFF(DAY, '19000105', DATEADD(DAY, -1, CAST((((@y * 100) + @m) * 100) + 1 AS CHAR(8))))) / 7) * 7) + 21, '19000105') AS fecha_tercer_viernes
    );
    GO
    SELECT
    	T.col1,
    	R.fecha_tercer_viernes
    FROM
    	(
    	VALUES
    		('20160101'),
    		('20160201'),
    		('20160301'),
    		('20160401'),
    		('20160501'),
    		('20160601'),
    		('20160701'),
    		('20160801'),
    		('20160901'),
    		('20161001'),
    		('20161101'),
    		('20161201')
    	) AS T(col1)
    	CROSS APPLY
    	(SELECT fecha_tercer_viernes FROM dbo.ufn_TercerViernesDelMes(YEAR(T.col1), MONTH(T.col1))) AS R
    ORDER BY
    	T.col1;
    GO
    DROP FUNCTION dbo.ufn_TercerViernesDelMes
    GO


    AMB

    Some guidelines for posting questions...

    AYÚDANOS A AYUDARTE, guía básica de consejos para formular preguntas

    • Marcado como respuesta Angeleci jueves, 8 de diciembre de 2016 14:47
    martes, 6 de diciembre de 2016 20:59
  • hola que tal, te dejo mi scrip, te va mostrar todos los viernes que tiene el mes.

    declare @FechaInicio datetime
    set @FechaInicio = '2016/12/01'
    
    declare @FechaFin datetime 
    set @FechaFin = '2016/12/31'
    
    declare @Cantidad int
    set @Cantidad = (DATEDIFF(DAY, @FechaInicio, @FechaFin)) 
    
    declare @Cont int	
    SET @Cont = 0
    
    declare @Fecha date
    declare @Dia VARCHAR (50)
    
    while @Cont <= @Cantidad
    begin
    	SET @Fecha = DATEADD(DAY, @Cont, @FechaInicio) 
    	SET @Dia = DATENAME(WEEKDAY, @Fecha)
    	
    	if (@Dia = 'Friday')
    	begin
    		select @Fecha
    	end
    	SET @Cont = @Cont +1	
    end

    Ahora, lo que falta es que solo te muestre el tercer viernes..pero es lo que te puedo ayudar. 

    Saludos..!!

     
    • Marcado como respuesta Angeleci jueves, 8 de diciembre de 2016 14:47
    martes, 6 de diciembre de 2016 21:05

Todas las respuestas

  • fijate si te sirve, tenes que ir cambiando el año

    with tmp(plant_date,dw) as
    (
       select cast('20150101' as datetime),DATENAME(weekday,'20150101')
       union all
       select plant_date + 1,DATENAME (weekday,plant_date+1)
         from tmp
        where plant_date < '20151231'
    )
    select  MIN(plant_date)
      from  tmp where dw='Friday'
      GROUP BY DATEADD(m,DATEDIFF(m,0,plant_date),0)
    option (maxrecursion 0)


    Carlos Ignacio Aguero. DBA SQL Server. Toda mi respeto al pueblo Peruano por la ayuda prestada en la guerra de Malvinas.

    martes, 6 de diciembre de 2016 13:17
  • Este tipo de problemas se resuelva relativamente facil teniendo una tabla Calendario (ver link sgte).

    https://www.mssqltips.com/sqlservertip/4054/creating-a-date-dimension-or-calendar-table-in-sql-server/

    select [date]
    from dbo.Calendario
    where dia_semana_nombre = 'Viernes' and [date] >= '20160201' and [date] < '20160301'
    order by [date]
    offset 2 rows fetch next 1 row only;

    Otra tecnica seria dada una fecha, primer dia del mes en este caso, calcular el proximo Viernes inclusive, pero en vez del proximo calcular el tercer proximo.

    Si tenemos una fecha dada, en este caso principio de mes, si usamos una fecha ancla que fue un Viernes como '19000105' y calculamos el # de dias entre esa fecha ancla y la fecha en cuestion pero restandole un dia a esta fecha, y ahora dividimos por siete (numero completo de semanas - division de enteros) y multiplicamos este valor por siete y le sumamos siete, entonces tendriamos el proximo viernes inclusive.

    DECLARE @m date = '20160101';
    
    SELECT
    	DATEADD(DAY, (((DATEDIFF(DAY, '19000105', DATEADD(DAY, -1, @m))) / 7) * 7) + 7, '19000105') AS dt;
    GO


    Ahora, en vez de sumar siete (7) para el proximo inclusive entonces adicionar 21 (7 * 3) para el tercero.

    DECLARE @m date = '20160101';
    
    SELECT
    	DATEADD(DAY, (((DATEDIFF(DAY, '19000105', DATEADD(DAY, -1, @m))) / 7) * 7) + 21, '19000105') AS dt;
    GO

    Veamos las fechas del tercer Viernes de cada mes del 2016.

    SELECT
    	col1,
    	DATEADD(DAY, (((DATEDIFF(DAY, '19000105', DATEADD(DAY, -1, col1))) / 7) * 7) + 21, '19000105') AS fecha_tercer_viernes
    FROM
    	(
    	VALUES
    		('20160101'),
    		('20160201'),
    		('20160301'),
    		('20160401'),
    		('20160501'),
    		('20160601'),
    		('20160701'),
    		('20160801'),
    		('20160901'),
    		('20161001'),
    		('20161101'),
    		('20161201')
    	) AS T(col1)
    ORDER BY
    	col1;
    GO


    Aca te dejo el link a unos cuantos articulos sobre calculo de fechas (escritos por el maestro Itzik Ben-Gan).

    http://sqlmag.com/t-sql/datetime-calculations-part-1

    http://sqlmag.com/t-sql/datetime-calculations-part-2

    http://sqlmag.com/t-sql/datetime-calculations-part-3

    http://sqlmag.com/t-sql/datetime-calculations-part-4

    http://sqlmag.com/t-sql/datetime-calculations-part-5


    AMB

    Some guidelines for posting questions...

    AYÚDANOS A AYUDARTE, guía básica de consejos para formular preguntas


    • Editado HunchbackMVP martes, 6 de diciembre de 2016 14:37
    • Propuesto como respuesta Willams Morales martes, 6 de diciembre de 2016 15:27
    • Marcado como respuesta Angeleci martes, 6 de diciembre de 2016 20:06
    martes, 6 de diciembre de 2016 14:27
  • Hola Hunchback:

    He probado tu propuesta:

    SELECT
    	col1,
    	DATEADD(DAY, (((DATEDIFF(DAY, '19000105', DATEADD(DAY, -1, col1))) / 7) * 7) + 21, '19000105') AS fecha_tercer_viernes
    FROM
    	(
    	VALUES
    		('20160101'),
    		('20160201'),
    		('20160301'),
    		('20160401'),
    		('20160501'),
    		('20160601'),
    		('20160701'),
    		('20160801'),
    		('20160901'),
    		('20161001'),
    		('20161101'),
    		('20161201')
    	) AS T(col1)
    ORDER BY
    	col1;
    GO

    Como bien indicas nos arroja la fecha del tercer viernes para cada uno de los meses de 2016.

    No obstante, llevo un buen rato dándole vueltas a cómo utilizar tu script en una función en la cual le indique un mes a través de un numero (6, por ejemplo seria Junio) y 2016 y la función me indique la fecha del tercer viernes de ese mes concreto.

    ¿sabrías darme alguna idea?

    Muchas gracias

    Angel

    martes, 6 de diciembre de 2016 20:05
  • Hola Carlos,

    He probado tu script y no he conseguido obtener ningun resultado. Debo añadirle algo más en algún sitio.

    Muchas gracias

    Angel

    martes, 6 de diciembre de 2016 20:07
  • Hombre, pasale el primer dia del mes como identificador de mes. Tambien puedes tomar el anio y mes y convertirlo a fecha.

    CREATE FUNCTION dbo.ufn_TercerViernesDelMes(
    @y int,
    @m tinyint
    )
    RETURNS table
    AS
    RETURN (
    SELECT DATEADD(DAY, (((DATEDIFF(DAY, '19000105', DATEADD(DAY, -1, CAST((((@y * 100) + @m) * 100) + 1 AS CHAR(8))))) / 7) * 7) + 21, '19000105') AS fecha_tercer_viernes
    );
    GO
    SELECT
    	T.col1,
    	R.fecha_tercer_viernes
    FROM
    	(
    	VALUES
    		('20160101'),
    		('20160201'),
    		('20160301'),
    		('20160401'),
    		('20160501'),
    		('20160601'),
    		('20160701'),
    		('20160801'),
    		('20160901'),
    		('20161001'),
    		('20161101'),
    		('20161201')
    	) AS T(col1)
    	CROSS APPLY
    	(SELECT fecha_tercer_viernes FROM dbo.ufn_TercerViernesDelMes(YEAR(T.col1), MONTH(T.col1))) AS R
    ORDER BY
    	T.col1;
    GO
    DROP FUNCTION dbo.ufn_TercerViernesDelMes
    GO


    AMB

    Some guidelines for posting questions...

    AYÚDANOS A AYUDARTE, guía básica de consejos para formular preguntas

    • Marcado como respuesta Angeleci jueves, 8 de diciembre de 2016 14:47
    martes, 6 de diciembre de 2016 20:59
  • hola que tal, te dejo mi scrip, te va mostrar todos los viernes que tiene el mes.

    declare @FechaInicio datetime
    set @FechaInicio = '2016/12/01'
    
    declare @FechaFin datetime 
    set @FechaFin = '2016/12/31'
    
    declare @Cantidad int
    set @Cantidad = (DATEDIFF(DAY, @FechaInicio, @FechaFin)) 
    
    declare @Cont int	
    SET @Cont = 0
    
    declare @Fecha date
    declare @Dia VARCHAR (50)
    
    while @Cont <= @Cantidad
    begin
    	SET @Fecha = DATEADD(DAY, @Cont, @FechaInicio) 
    	SET @Dia = DATENAME(WEEKDAY, @Fecha)
    	
    	if (@Dia = 'Friday')
    	begin
    		select @Fecha
    	end
    	SET @Cont = @Cont +1	
    end

    Ahora, lo que falta es que solo te muestre el tercer viernes..pero es lo que te puedo ayudar. 

    Saludos..!!

     
    • Marcado como respuesta Angeleci jueves, 8 de diciembre de 2016 14:47
    martes, 6 de diciembre de 2016 21:05
  • Hola Hunchback.

    Finalmente he optado por crear una tabla calendario tal y como me indicabas a la cual he agregado nuevos campos. Con ella ha sido realmente facil obtener el tercer viernes de un mes y año dado.

    Muchas gracias

    Un saludo


    Angel

    jueves, 8 de diciembre de 2016 14:46