Salve pessoal!
Situação problema deste post
Como calcular a diferença entre duas datas usando Transact-SQL, considerando o mês e o dia das datas envolvidas?
Usando o DATEDIFFa variação de dias é desconsiderada:

E o DATEDIFF também não considera o mês para fazer a diferença:

Somente com o DATEDIFF, portanto, não conseguimos resolver. Esta faz uma subtração dos valores absolutas dos anos das datas, logo, não resolve nosso problema.
Demonstro, a seguir, duas soluções:
- Na primeira calculo a diferença em anos entre duas datas (idade) considerando, é claro, o dia do “aniversário” da data comparada.
2. Na segunda calculo e apresento esta diferença em anos, meses e dias destas duas datas.
Calculando a diferença entre duas datas em anos
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
IF (OBJECT_ID('dbo.Fc_Calcula_Idade') IS NOT NULL) DROP FUNCTION dbo.Fc_Calcula_Idade GO /**************************************** * Fc_Calcula_Idade: Função para calculo de Idade ou quantidade de anos entre duas datas ****************************************/ CREATE FUNCTION [dbo].[Fc_Calcula_Idade] (@dt_Inicio date, @Dt_Fim date) RETURNS INT AS BEGIN RETURN DATEDIFF(YEAR, @dt_Inicio, @Dt_Fim) + CASE WHEN (MONTH(@dt_Inicio) > MONTH(@Dt_Fim) OR (MONTH(@dt_Inicio) = MONTH(@Dt_Fim) AND DAY(@dt_Inicio) > DAY(@Dt_Fim))) THEN -1 ELSE 0 END END |
Para ajustar o calculo da idade em anos, na função Fc_Calcula_Idade é somado -1 caso o mês de início seja igual OU maior que o mês da data final E se o mês for igual, compara-se o dia também.
Testando…

Perfeito! Primeira proposta de solução concluída.
Calculando a diferença entre duas datas em anos, mês e dia
Vamos complicar um pouquinho!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
" ]IF (OBJECT_ID('dbo.Fc_Calcula_Idade_Completa') IS NOT NULL) DROP FUNCTION dbo.Fc_Calcula_Idade_Completa GO /************************************************************ * [Fc_Calcula_Idade_Completa]: Função para calculo de Idade ou quantidade de anos entre duas datas ************************************************************/ CREATE FUNCTION [dbo].[Fc_Calcula_Idade_Completa] (@dt_Inicio date, @dt_Fim date) RETURNS VARCHAR(50) AS BEGIN DECLARE @dt_Ultimo_Aniversario date, @int_Ano_Ultimo_Aniversario INT, @int_Qtd_Anos INT = 0, @int_Qtd_Meses INT = 0, @int_Qtd_Dias INT = 0, @dt_Ultimo_Mesversario date, @str_Idade varchar(50); SET @int_Ano_Ultimo_Aniversario = YEAR(@Dt_Fim) + CASE WHEN (MONTH(@dt_Inicio) > MONTH(@Dt_Fim) OR (MONTH(@dt_Inicio) = MONTH(@Dt_Fim) AND DAY(@dt_Inicio) > DAY(@Dt_Fim)) ) THEN -1 ELSE 0 END; SET @dt_Ultimo_Aniversario = CONVERT(DATE, CAST( IIF(MONTH(@dt_Inicio)=2 and DAY(@dt_Inicio)>28,28,DAY(@dt_Inicio)) AS VARCHAR)+'/'+CAST(MONTH(@dt_Inicio) AS VARCHAR)+'/'+CAST(@int_Ano_Ultimo_Aniversario as varchar) , 103) SELECT @int_Qtd_Anos = DATEDIFF(YEAR, @dt_Inicio, @Dt_Fim) + CASE WHEN (MONTH(@dt_Inicio) > MONTH(@Dt_Fim) OR (MONTH(@dt_Inicio) = MONTH(@Dt_Fim) AND DAY(@dt_Inicio) > DAY(@Dt_Fim)) ) THEN -1 ELSE 0 END, @int_Qtd_Meses = DATEDIFF(MONTH, @dt_Ultimo_Aniversario, @Dt_Fim) + CASE WHEN (MONTH(@dt_Ultimo_Aniversario) > MONTH(@Dt_Fim) OR (MONTH(@dt_Inicio) = MONTH(@Dt_Fim) AND DAY(@dt_Inicio) > DAY(@Dt_Fim)) OR (MONTH(@dt_Inicio) < MONTH(@Dt_Fim) AND DAY(@dt_Inicio) > DAY(@Dt_Fim)) ) THEN -1 ELSE 0 END SET @dt_Ultimo_Mesversario = DATEADD(MONTH, @int_Qtd_Meses, DATEADD(YEAR, @int_Qtd_Anos, @dt_Inicio) ) SET @int_Qtd_Dias = DATEDIFF(DAY, @dt_Ultimo_Mesversario, @Dt_Fim) SET @str_Idade = CASE WHEN @int_Qtd_Anos=1 THEN CAST(@int_Qtd_Anos as varchar)+' ano' + IIF(@int_Qtd_Dias=0 and @int_Qtd_Meses>0,' e ',', ') WHEN @int_Qtd_Anos>1 THEN CAST(@int_Qtd_Anos as varchar)+' anos' + IIF(@int_Qtd_Dias=0 and @int_Qtd_Meses>0,' e ',', ') ELSE '' END SET @str_Idade = @str_Idade + CASE WHEN @int_Qtd_Meses=1 THEN CAST(@int_Qtd_Meses as varchar)+' mês' + IIF(@int_Qtd_Dias>0,' e ','') WHEN @int_Qtd_Meses>1 THEN CAST(@int_Qtd_Meses as varchar)+' meses' + IIF(@int_Qtd_Dias>0,' e ','') ELSE '' END SET @str_Idade = @str_Idade + CASE WHEN @int_Qtd_Dias=1 THEN CAST(@int_Qtd_Dias as varchar)+' dia' WHEN @int_Qtd_Dias>1 THEN CAST(@int_Qtd_Dias as varchar)+' dias' ELSE '' END RETURN @str_Idade END |
Para calcular e apresentar anos, mês e dia resolvi da seguinte forma:
- A data do aniversário mais recente, para isso preciso calcular o ano deste aniversário ( @int_Ano_Ultimo_Aniversario ).
1 |
SET @int_Ano_Ultimo_Aniversario = YEAR(@Dt_Fim) ... |
1 |
SET @dt_Ultimo_Aniversario = CONVERT(DATE, ... |
A diferença em anos: @int_Qtd_Anos.
1 |
SELECT @int_Qtd_Anos = DATEDIFF(YEAR ... |
A quantidade de meses, desde de o último aniversário:
1 2 3 |
SELECT ... @int_Qtd_Meses = DATEDIFF(MONTH ... |
O último mesversário ( @dt_Ultimo_Mesversario ) para conclui a diferença em dias: @int_Qtd_Dias.
1 2 3 |
SET @dt_Ultimo_Mesversario = DATEADD(MONTH ... SET @int_Qtd_Dias = DATEDIFF(DAY ... |
Concateno os valores que serão retornados como VARCHAR.
Testando…

Perfeito!
Já com tratamentos para o mês de fevereiro e os meses de 31 dias. Penso que esta função atenda em vários cenários.
Essas funções têm me atendido em algumas demandas.
Espero que sejam úteis e no seu dia-a-dia e no seu aprendizado.
Estejam a vontade em comentar e compartilhar.
Grande abraço, até a próxima.
Bom dia Professor, excelente artigo.
Mas tem erro ( provavelmente no meu codigo ).
Copiei exatamente o seu codigo acima e quando excecutei minha consulta, deu um valor errado.
dbo.fc_calcula_Idade_Completa(convert(date,c.Dataadmissao,103), convert(date,c.datademissao,103)) as ‘Tempo’
Admissao: 02/10/2015 Demissao: 08/05/2023 Temmpo: 7 anos, 6 meses e 36 dias
Não deveria ser 7 anos, 7 meses e 6 ou 5 dias ( depednendo se for mes com 30 ou com 31?
Obrigado.
Olá Bartolomeu, bom dia.
Obrigado pelo comentário.
Revisarei o código e comento aqui.
Abraço.