What would be the best way to calculate someone\'s age in years, months, and days in T-SQL (SQL Server 2000)?
The datediff
function doesn\'t handle year
I use this Function I modified (the Days part) From @Dane answer: https://stackoverflow.com/a/57720/2097023
CREATE FUNCTION dbo.EdadAMD
(
@FECHA DATETIME
)
RETURNS NVARCHAR(10)
AS
BEGIN
DECLARE
@tmpdate DATETIME
, @years INT
, @months INT
, @days INT
, @EdadAMD NVARCHAR(10);
SELECT @tmpdate = @FECHA;
SELECT @years = DATEDIFF(yy, @tmpdate, GETDATE()) - CASE
WHEN (MONTH(@FECHA) > MONTH(GETDATE()))
OR (
MONTH(@FECHA) = MONTH(GETDATE())
AND DAY(@FECHA) > DAY(GETDATE())
) THEN
1
ELSE
0
END;
SELECT @tmpdate = DATEADD(yy, @years, @tmpdate);
SELECT @months = DATEDIFF(m, @tmpdate, GETDATE()) - CASE
WHEN DAY(@FECHA) > DAY(GETDATE()) THEN
1
ELSE
0
END;
SELECT @tmpdate = DATEADD(m, @months, @tmpdate);
IF MONTH(@FECHA) = MONTH(GETDATE())
AND DAY(@FECHA) > DAY(GETDATE())
SELECT @days =
DAY(EOMONTH(GETDATE(), -1)) - (DAY(@FECHA) - DAY(GETDATE()));
ELSE
SELECT @days = DATEDIFF(d, @tmpdate, GETDATE());
SELECT @EdadAMD = CONCAT(@years, 'a', @months, 'm', @days, 'd');
RETURN @EdadAMD;
END;
GO
It works pretty well.
I've seen the question several times with results outputting Years, Month, Days but never a numeric / decimal result. (At least not one that doesn't round incorrectly). I welcome feedback on this function. Might not still need a little adjusting.
-- Input to the function is two dates. -- Output is the numeric number of years between the two dates in Decimal(7,4) format. -- Output is always always a possitive number.
-- Logic is based on three steps. -- 1) Is the difference less than 1 year (0.5000, 0.3333, 0.6667, ect.) -- 2) Is the difference exactly a whole number of years (1,2,3, ect.)
CREATE Function [dbo].[F_Get_Actual_Age](@pi_date1 datetime,@pi_date2 datetime)
RETURNS Numeric(7,4)
AS
BEGIN
Declare
@l_tmp_date DATETIME
,@l_days1 DECIMAL(9,6)
,@l_days2 DECIMAL(9,6)
,@l_result DECIMAL(10,6)
,@l_years DECIMAL(7,4)
--Check to make sure there is a date for both inputs
IF @pi_date1 IS NOT NULL and @pi_date2 IS NOT NULL
BEGIN
IF @pi_date1 > @pi_date2 --Make sure the "older" date is in @pi_date1
BEGIN
SET @l_tmp_date = @pi_date2
SET @pi_date2 = @Pi_date1
SET @pi_date1 = @l_tmp_date
END
--Check #1 If date1 + 1 year is greater than date2, difference must be less than 1 year
IF DATEADD(YYYY,1,@pi_date1) > @pi_date2
BEGIN
--How many days between the two dates (numerator)
SET @l_days1 = DATEDIFF(dd,@pi_date1, @pi_date2)
--subtract 1 year from date2 and calculate days bewteen it and date2
--This is to get the denominator and accounts for leap year (365 or 366 days)
SET @l_days2 = DATEDIFF(dd,dateadd(yyyy,-1,@pi_date2),@pi_date2)
SET @l_years = @l_days1 / @l_days2 -- Do the math
END
ELSE
--Check #2 Are the dates an exact number of years apart.
--Calculate years bewteen date1 and date2, then add the years to date1, compare dates to see if exactly the same.
IF DATEADD(YYYY,DATEDIFF(YYYY,@pi_date1,@pi_date2),@pi_date1) = @pi_date2
SET @l_years = DATEDIFF(YYYY,@pi_date1, @pi_date2) --AS Years, 'Exactly even Years' AS Msg
ELSE
BEGIN
--Check #3 The rest of the cases.
--Check if datediff, returning years, over or under states the years difference
SET @l_years = DATEDIFF(YYYY,@pi_date1, @pi_date2)
IF DATEADD(YYYY,@l_years,@pi_date1) > @pi_date2
SET @l_years = @l_years -1
--use basicly same logic as in check #1
SET @l_days1 = DATEDIFF(dd,DATEADD(YYYY,@l_years,@pi_date1), @pi_date2)
SET @l_days2 = DATEDIFF(dd,dateadd(yyyy,-1,@pi_date2),@pi_date2)
SET @l_years = @l_years + @l_days1 / @l_days2
--SELECT @l_years AS Years, 'Years Plus' AS Msg
END
END
ELSE
SET @l_years = 0 --If either date was null
RETURN @l_Years --Return the result as decimal(7,4)
END
`
For the ones that want to create a calculated column in a table to store the age:
CASE WHEN DateOfBirth< DATEADD(YEAR, (DATEPART(YEAR, GETDATE()) - DATEPART(YEAR, DateOfBirth))*-1, GETDATE())
THEN DATEPART(YEAR, GETDATE()) - DATEPART(YEAR, DateOfBirth)
ELSE DATEPART(YEAR, GETDATE()) - DATEPART(YEAR, DateOfBirth) -1 END
Are you trying to calculate the total days/months/years of an age? do you have a starting date? Or are you trying to dissect it (ex: 24 years, 1 month, 29 days)?
If you have a start date that you're working with, datediff will output the total days/months/years with the following commands:
Select DateDiff(d,'1984-07-12','2008-09-11')
Select DateDiff(m,'1984-07-12','2008-09-11')
Select DateDiff(yyyy,'1984-07-12','2008-09-11')
with the respective outputs being (8827/290/24).
Now, if you wanted to do the dissection method, you'd have to subtract the number of years in days (days - 365*years), and then do further math on that to get the months, etc.
declare @now date,@dob date, @now_i int,@dob_i int, @days_in_birth_month int
declare @years int, @months int, @days int
set @now = '2013-02-28'
set @dob = '2012-02-29' -- Date of Birth
set @now_i = convert(varchar(8),@now,112) -- iso formatted: 20130228
set @dob_i = convert(varchar(8),@dob,112) -- iso formatted: 20120229
set @years = ( @now_i - @dob_i)/10000
-- (20130228 - 20120229)/10000 = 0 years
set @months =(1200 + (month(@now)- month(@dob))*100 + day(@now) - day(@dob))/100 %12
-- (1200 + 0228 - 0229)/100 % 12 = 11 months
set @days_in_birth_month = day(dateadd(d,-1,left(convert(varchar(8),dateadd(m,1,@dob),112),6)+'01'))
set @days = (sign(day(@now) - day(@dob))+1)/2 * (day(@now) - day(@dob))
+ (sign(day(@dob) - day(@now))+1)/2 * (@days_in_birth_month - day(@dob) + day(@now))
-- ( (-1+1)/2*(28 - 29) + (1+1)/2*(29 - 29 + 28))
-- Explain: if the days of now is bigger than the days of birth, then diff the two days
-- else add the days of now and the distance from the date of birth to the end of the birth month
select @years,@months,@days -- 0, 11, 28
The approach of days is different from the accepted answer, the differences shown in the comments below:
dob now years months days
2012-02-29 2013-02-28 0 11 28 --Days will be 30 if calculated by the approach in accepted answer.
2012-02-29 2016-02-28 3 11 28 --Days will be 31 if calculated by the approach in accepted answer, since the day of birth will be changed to 28 from 29 after dateadd by years.
2012-02-29 2016-03-31 4 1 2
2012-01-30 2016-02-29 4 0 30
2012-01-30 2016-03-01 4 1 2 --Days will be 1 if calculated by the approach in accepted answer, since the day of birth will be changed to 30 from 29 after dateadd by years.
2011-12-30 2016-02-29 4 1 30
set @days = CASE WHEN day(@now) >= day(@dob) THEN day(@now) - day(@dob)
ELSE @days_in_birth_month - day(@dob) + day(@now) END
If you want the age of years and months only, it could be simpler
set @years = ( @now_i/100 - @dob_i/100)/100
set @months =(12 + month(@now) - month(@dob))%12
select @years,@months -- 1, 0
NOTE: A very useful link of SQL Server Date Formats
DateTime
values in T-SQL are stored as floats. You can just subtract the dates from each other and you now have a new date that is the timespan between them.
declare @birthdate datetime
set @birthdate = '6/15/1974'
--age in years - short version
print year(getdate() - @birthdate) - year(0)
--age in years - visualization
declare @mindate datetime
declare @span datetime
set @mindate = 0
set @span = getdate() - @birthdate
print @mindate
print @birthdate
print getdate()
print @span
--substract minyear from spanyear to get age in years
print year(@span) - year(@mindate)
print month(@span)
print day(@span)