223

I have a table listing people along with their date of birth (currently a nvarchar(25))

How can I convert that to a date, and then calculate their age in years?

My data looks as follows

ID    Name   DOB
1     John   1992-01-09 00:00:00
2     Sally  1959-05-20 00:00:00

I would like to see:

ID    Name   AGE  DOB
1     John   17   1992-01-09 00:00:00
2     Sally  50   1959-05-20 00:00:00
Dale K
  • 25,246
  • 15
  • 42
  • 71
Jimmy
  • 16,123
  • 39
  • 133
  • 213
  • 17
    Why are you storing date values as strings using nvarchar(25) instead of using the database's native date or datetime type? – Jesper Oct 15 '09 at 12:44
  • The question is tagged 2005 not 2008 so the native 'Date' type isn't available, but definately a datetime, and it could be argued SmallDateTime since you do not need the accuracy. – Andrew Oct 15 '09 at 12:55
  • Hi, the reason for keeping dates as varchar is because I'm importing this from a non-SQL server schema, there were some issues importing them as datetime (and the other date formats) and varchar converted ok – Jimmy Oct 15 '09 at 13:12
  • 7
    @James.Elsey, so you had issues importing and as a result are all the dates valid? can never be sure unless you use a datetime or smalldatetime, with varchar, you may get your import to work, but have other problems down the line. Also, I'd never store the age, it changes each day, use a View – KM. Oct 15 '09 at 13:38
  • @KM Yes there was an issue importing that data as a date, the only viable solution at the time was to import them as nvarchars. This select is going to be part of a nightly job so storing the age should not be an issue – Jimmy Oct 15 '09 at 14:21

42 Answers42

311

There are issues with leap year/days and the following method, see the update below:

try this:

DECLARE @dob  datetime
SET @dob='1992-01-09 00:00:00'

SELECT DATEDIFF(hour,@dob,GETDATE())/8766.0 AS AgeYearsDecimal
    ,CONVERT(int,ROUND(DATEDIFF(hour,@dob,GETDATE())/8766.0,0)) AS AgeYearsIntRound
    ,DATEDIFF(hour,@dob,GETDATE())/8766 AS AgeYearsIntTrunc

OUTPUT:

AgeYearsDecimal                         AgeYearsIntRound AgeYearsIntTrunc
--------------------------------------- ---------------- ----------------
17.767054                               18               17

(1 row(s) affected)

UPDATE here are some more accurate methods:

BEST METHOD FOR YEARS IN INT

DECLARE @Now  datetime, @Dob datetime
SELECT   @Now='1990-05-05', @Dob='1980-05-05'  --results in 10
--SELECT @Now='1990-05-04', @Dob='1980-05-05'  --results in  9
--SELECT @Now='1989-05-06', @Dob='1980-05-05'  --results in  9
--SELECT @Now='1990-05-06', @Dob='1980-05-05'  --results in 10
--SELECT @Now='1990-12-06', @Dob='1980-05-05'  --results in 10
--SELECT @Now='1991-05-04', @Dob='1980-05-05'  --results in 10

SELECT
    (CONVERT(int,CONVERT(char(8),@Now,112))-CONVERT(char(8),@Dob,112))/10000 AS AgeIntYears

you can change the above 10000 to 10000.0 and get decimals, but it will not be as accurate as the method below.

BEST METHOD FOR YEARS IN DECIMAL

DECLARE @Now  datetime, @Dob datetime
SELECT   @Now='1990-05-05', @Dob='1980-05-05' --results in 10.000000000000
--SELECT @Now='1990-05-04', @Dob='1980-05-05' --results in  9.997260273973
--SELECT @Now='1989-05-06', @Dob='1980-05-05' --results in  9.002739726027
--SELECT @Now='1990-05-06', @Dob='1980-05-05' --results in 10.002739726027
--SELECT @Now='1990-12-06', @Dob='1980-05-05' --results in 10.589041095890
--SELECT @Now='1991-05-04', @Dob='1980-05-05' --results in 10.997260273973

SELECT 1.0* DateDiff(yy,@Dob,@Now) 
    +CASE 
         WHEN @Now >= DATEFROMPARTS(DATEPART(yyyy,@Now),DATEPART(m,@Dob),DATEPART(d,@Dob)) THEN  --birthday has happened for the @now year, so add some portion onto the year difference
           (  1.0   --force automatic conversions from int to decimal
              * DATEDIFF(day,DATEFROMPARTS(DATEPART(yyyy,@Now),DATEPART(m,@Dob),DATEPART(d,@Dob)),@Now) --number of days difference between the @Now year birthday and the @Now day
              / DATEDIFF(day,DATEFROMPARTS(DATEPART(yyyy,@Now),1,1),DATEFROMPARTS(DATEPART(yyyy,@Now)+1,1,1)) --number of days in the @Now year
           )
         ELSE  --birthday has not been reached for the last year, so remove some portion of the year difference
           -1 --remove this fractional difference onto the age
           * (  -1.0   --force automatic conversions from int to decimal
                * DATEDIFF(day,DATEFROMPARTS(DATEPART(yyyy,@Now),DATEPART(m,@Dob),DATEPART(d,@Dob)),@Now) --number of days difference between the @Now year birthday and the @Now day
                / DATEDIFF(day,DATEFROMPARTS(DATEPART(yyyy,@Now),1,1),DATEFROMPARTS(DATEPART(yyyy,@Now)+1,1,1)) --number of days in the @Now year
             )
     END AS AgeYearsDecimal
KM.
  • 101,727
  • 34
  • 178
  • 212
  • There were issues importing the varchar as datetime originally (hence DOB is stored as a varchar), but you can use this method? How does that make sense? Don't you get the same 'issues' here? – Kirk Broadhurst Oct 16 '09 at 09:23
  • @Kirk Broadhurst, possibly they loaded them into this table using varchar, and then did a second pass fixing them up. It would be better to load them into a work table using varchar and then fix them before moving them to the actual table where they are datetime. – KM. Oct 16 '09 at 13:46
  • 25
    This is also not an exact solution. If I take my own @dob to be '1986-07-05 00:00:00' and I'd execute this (use another variable instead of `GETDATE()`) on '2013-07-04 23:59:59' it says I'm 27, while at that moment, I'm not yet. Example code: `declare @startDate nvarchar(100) = '1986-07-05 00:00:00' declare @endDate nvarchar(100) = '2013-07-04 23:59:59' SELECT DATEDIFF(hour,@startDate,@endDate)/8766.0 AS AgeYearsDecimal ,CONVERT(int,ROUND(DATEDIFF(hour,@startDate,@endDate)/8766.0,0)) AS AgeYearsIntRound ,DATEDIFF(hour,@startDate,@endDate)/8766 AS AgeYearsIntTrunc` – bartlaarhoven Dec 16 '13 at 10:00
  • 20
    This is not accurate as it assumes 8766 hours per year, which works out to 365.25 days. Since there are no years with 365.25 days, this will be incorrect near the person's birthdate more often than it is correct. [This method](http://stackoverflow.com/questions/57599/how-to-calculate-age-in-t-sql-with-years-months-and-days) is still going to be more accurate. – Bacon Bits Jun 02 '14 at 13:00
  • 1
    Second @Bacon Bits comment - this will often be wrong when current date is near a person's birth date. – flash Jul 03 '14 at 14:37
  • Is it possible to calculate this without using the DOB as SET @dob='1992-01-09 00:00:00' I have a table called Person with a column DOB and need to calculate these Ages for 5000 people. This works, but how does one do this for many DOB entries. – MeachamRob Dec 16 '14 at 20:13
  • @MeachamRob, put his in your query , but replace `@dob` with your DOB column – KM. Dec 16 '14 at 20:43
  • Hi, There's a bug in "Best Method For Years in Decimal" method.. @DOB = '2016-02-29' (which is leap year).. @NOW= '2016-03-01' until '2016-12-31' is no problem, but it got error on @NOW= '2017-01-01' and so on.. – Roland Andreas May 19 '17 at 08:54
  • 4
    I think the first block of text makes this answer confusing. If your updated method does not have the issue with leap years, I'd suggest (if you really want to keep it at all) to move it down the bottom of your answer. – ajbeaven Jul 04 '17 at 23:14
  • This saved my bacon, seriously. Putting it in the Select statement did not occur to me and I just had to put the field I needed rather than the @DOB. Thanks! – Joe Hill Oct 25 '17 at 15:32
  • Very simple one line query SELECT DATEDIFF(year,'1991-07-17 00:00:00',GETDATE()) – Shailendra Mishra Nov 28 '17 at 05:49
  • 1
    If you want an **EXACT** calculation, then DATEDIFF won't do it @ShailendraMishra, because it approximates days,etc. For example, `select datediff(year, '2000-01-05', '2018-01-04')` returns 18, not 17 as it should. I used the example above under heading "BEST METHOD FOR YEARS IN INT" and it works perfectly. Thanks! – openwonk Nov 08 '18 at 01:37
  • Use Solution 1 here, it's the best: https://stackoverflow.com/questions/1572110/how-to-calculate-age-in-years-based-on-date-of-birth-and-getdate/37613556#37613556 – Boogier Mar 17 '21 at 11:11
  • In SSRS, RDL formula for age as an integer accounting for birthdate in the current year: "AGE: " & DateDiff(DateInterval.Year,Fields!BirthDate.Value,Now()) + IIF( RIGHT("0"& DatePart("m",NOW()),2) & RIGHT("0" & DatePart("d",NOW()),2) < RIGHT("0"& DatePart("m",Fields!BirthDate.Value),2) & RIGHT("0" & DatePart("d",Fields!BirthDate.Value),2),-1,0) – user3784130 Mar 31 '22 at 13:53
176

Gotta throw this one out there. If you convert the date using the 112 style (yyyymmdd) to a number you can use a calculation like this...

(yyyyMMdd - yyyyMMdd) / 10000 = difference in full years

declare @as_of datetime, @bday datetime;
select @as_of = '2009/10/15', @bday = '1980/4/20'

select 
    Convert(Char(8),@as_of,112),
    Convert(Char(8),@bday,112),
    0 + Convert(Char(8),@as_of,112) - Convert(Char(8),@bday,112), 
    (0 + Convert(Char(8),@as_of,112) - Convert(Char(8),@bday,112)) / 10000

output

20091015    19800420    290595  29
dotjoe
  • 26,242
  • 5
  • 63
  • 77
  • 21
    This is almost magical in how it solves all the leap year problems. May be worth noting that 112 is a special number for the CONVERT function that formats the date as yyyymmdd. It's possibly not obvious for everyone when you look at it. – Derek Tomes May 27 '15 at 21:12
  • My team was running into an issue when the date we were using to find age was the same day as the date we are comparing it to. We were noticing that when they were the same day (and if the age was going to be odd) the age would be off by one. This worked perfectly! – The Sheek Geek Jun 05 '18 at 15:47
  • 13
    The simplest/shortest code for this exact same calculation method in SQL Server 2012+ is `code: SELECT [Age] = (0+ FORMAT(@as_of,'yyyyMMdd') - FORMAT(@bday,'yyyyMMdd') ) /10000 --The 0+ part tells SQL to calc the char(8) as numbers ` – ukgav Mar 04 '19 at 01:19
  • Just note this is only accurate for the integer component of the age. Don't try and divide by 10000.00 (to result in a decimal rather than int) and expect the decimal component to accurately reflect the age. – Tyson Feb 20 '23 at 12:43
57

I have used this query in our production code for nearly 10 years:

SELECT FLOOR((CAST (GetDate() AS INTEGER) - CAST(Date_of_birth AS INTEGER)) / 365.25) AS Age
J__
  • 3,777
  • 1
  • 23
  • 31
  • 9
    It's not bad, but is not 100%, 2007/10/16 will report an age of 2 on 2009/10/15 – Andrew Oct 15 '09 at 13:11
  • It doesn't on mine, it returns 1 which is the correct answer. – J__ Oct 15 '09 at 13:27
  • Weird, I get 2, Today cast as int is 40100, 2007/10/16 is 36939, the difference is 731, divided by 365.25 gives 2.001368 which floors to 2, but the birthday is 1 day away so should give the 1 you are getting. I'm running it on 2005 but shouldn't make a difference. – Andrew Oct 15 '09 at 15:07
  • Here's something interesting: If I CAST (GetDate() AS INT), I get 40100 as you do. But if I CAST ({d '2009-10-15'} AS INT (which is today's date), I get 40099. Hence the day's difference. So re-running my query with GetDate() returns 2, but re-running it with the CAST ({d '2009-10-15'} returns 1. I wonder if it is somethig to do with the time element? SELECT {d '2009-10-15'} returns '15/10/2009' but SELECT GetDate() returns 15/10/2009 4:57:57 pm – J__ Oct 15 '09 at 15:58
  • 4
    Doh, we're missing the obvious, it's after mid-day, getdate returns an int so will be rounding up of course. I copy pasted your answer and ran it, so automatically used getdate, not the literal. – Andrew Oct 15 '09 at 16:28
  • 1
    It doesn't work for DOB='1969-01-10' and Today='2014-01-10'. Age should be 45, not 44. You should use "365.23076923074" instead of "365.25", as in @mediantiba's post. – Granger Jan 22 '14 at 17:08
  • 12
    If we're talking about human ages, you should calculate it the way humans calculate age. It has nothing to do with how fast the earth moves and everything to do with the calendar. Every time the same month and day elapses as the date of birth, you increment age by 1. This means the following is the most accurate because it mirrors what humans mean when they say "age": `DATEDIFF(yy, @BirthDate, GETDATE()) - CASE WHEN (MONTH(@BirthDate) >= MONTH(GETDATE())) AND DAY(@BirthDate) > DAY(GETDATE()) THEN 1 ELSE 0 END` – Bacon Bits Jun 02 '14 at 13:06
  • 8
    Sorry, that syntax is wrong. `CASE WHEN (MONTH(@date) > MONTH(GETDATE())) OR (MONTH(@date) = MONTH(GETDATE()) AND DAY(@date) > DAY(GETDATE())) THEN 1 ELSE 0 END` – Bacon Bits Jun 03 '14 at 12:43
39

You need to consider the way the datediff command rounds.

SELECT CASE WHEN dateadd(year, datediff (year, DOB, getdate()), DOB) > getdate()
            THEN datediff(year, DOB, getdate()) - 1
            ELSE datediff(year, DOB, getdate())
       END as Age
FROM <table>

Which I adapted from here.

Note that it will consider 28th February as the birthday of a leapling for non-leap years e.g. a person born on 29 Feb 2020 will be considered 1 year old on 28 Feb 2021 instead of 01 Mar 2021.

Salman A
  • 262,204
  • 82
  • 430
  • 521
Ed Harper
  • 21,127
  • 4
  • 54
  • 80
  • @Andrew - Corrected - I missed one of the substitutions – Ed Harper Oct 15 '09 at 13:27
  • 4
    Simplified version `SELECT DATEDIFF(year, DOB, getdate()) + CASE WHEN (DATEADD(year,DATEDIFF(year, DOB, getdate()) , DOB) > getdate()) THEN - 1 ELSE 0 END)` – Peter Feb 10 '17 at 14:26
  • 2
    This is the correct approach; I don't understand why hacks are upvoted so much. – Salman A Mar 13 '20 at 12:39
  • This is the way. The logic is very easy to follow too. Take the year difference between two dates and add this the original DOB. This is the DOB this year. If this date is after today, the birthday has not happened yet; subtract 1 to account for this. Otherwise, the birthday is today or the birthday has already passed; return the actual year difference. This works for leap years too. – Patrick Tucci Dec 16 '21 at 15:41
  • This one makes sense to me as well. – Alain Mar 04 '22 at 20:33
  • 1
    It's also more that 16 times faster than using FORMAT. – Jeff Moden Apr 14 '22 at 03:23
35

So many of the above solutions are wrong DateDiff(yy,@Dob, @PassedDate) will not consider the month and day of both dates. Also taking the dart parts and comparing only works if they're properly ordered.

THE FOLLOWING CODE WORKS AND IS VERY SIMPLE:

create function [dbo].[AgeAtDate](
    @DOB    datetime,
    @PassedDate datetime
)

returns int
with SCHEMABINDING
as
begin

declare @iMonthDayDob int
declare @iMonthDayPassedDate int


select @iMonthDayDob = CAST(datepart (mm,@DOB) * 100 + datepart  (dd,@DOB) AS int) 
select @iMonthDayPassedDate = CAST(datepart (mm,@PassedDate) * 100 + datepart  (dd,@PassedDate) AS int) 

return DateDiff(yy,@DOB, @PassedDate) 
- CASE WHEN @iMonthDayDob <= @iMonthDayPassedDate
  THEN 0 
  ELSE 1
  END

End
user2634514
  • 351
  • 3
  • 2
  • Why are you multiplying by 100? This works for me as I'm trying to replicate in the database what exists in our code library - but I couldn't explain your function. This might be a stupid question :) – Jen Sep 25 '13 at 05:30
  • 7
    Thanks! Exactly the code I was expecting here. This is the **only exactly correct** code in this thread without (ugly) string transformations! @Jen It takes the month and day of the DoB (like September 25) and turns it into an integer value `0925` (or `925`). It does the same with the current date (like December 16 becomes `1216`) and then checks whether the DoB integer value has passed already. To create this integer, the month should be multiplied by 100. – bartlaarhoven Dec 16 '13 at 10:23
  • I'll just mention that while this does avoid string transformations, it does a lot of casting instead. My testing shows it's not significantly faster than [dotjoe's answer](https://stackoverflow.com/a/1572938/120955), and the code is more verbose. – StriplingWarrior Dec 20 '18 at 15:46
  • the accepted answer has a much simpler INT answer: `(CONVERT(int,CONVERT(char(8),@Now,112))-CONVERT(char(8),@Dob,112))/10000` – KM. Feb 20 '19 at 20:54
  • @KM. It also appears (based on comments on the accepted answer) that the accepted answer is incorrect and will produce wrong values. – jinglesthula Mar 20 '20 at 15:49
  • This works perfectly if you do not care about hours, it really depends on the requirements. – Sylvain Gantois May 17 '23 at 01:48
10

EDIT: THIS ANSWER IS INCORRECT. I leave it in here as a warning to anyone tempted to use dayofyear, with a further edit at the end.


If, like me, you do not want to divide by fractional days or risk rounding/leap year errors, I applaud @Bacon Bits comment in a post above https://stackoverflow.com/a/1572257/489865 where he says:

If we're talking about human ages, you should calculate it the way humans calculate age. It has nothing to do with how fast the earth moves and everything to do with the calendar. Every time the same month and day elapses as the date of birth, you increment age by 1. This means the following is the most accurate because it mirrors what humans mean when they say "age".

He then offers:

DATEDIFF(yy, @date, GETDATE()) -
CASE WHEN (MONTH(@date) > MONTH(GETDATE())) OR (MONTH(@date) = MONTH(GETDATE()) AND DAY(@date) > DAY(GETDATE()))
THEN 1 ELSE 0 END

There are several suggestions here involving comparing the month & day (and some get it wrong, failing to allow for the OR as correctly here!). But nobody has offered dayofyear, which seems so simple and much shorter. I offer:

DATEDIFF(year, @date, GETDATE()) -
CASE WHEN DATEPART(dayofyear, @date) > DATEPART(dayofyear, GETDATE()) THEN 1 ELSE 0 END

[Note: Nowhere in SQL BOL/MSDN is what DATEPART(dayofyear, ...) returns actually documented! I understand it to be a number in the range 1--366; most importantly, it does not change by locale as per DATEPART(weekday, ...) & SET DATEFIRST.]


EDIT: Why dayofyear goes wrong: As user @AeroX has commented, if the birth/start date is after February in a non leap year, the age is incremented one day early when the current/end date is a leap year, e.g. '2015-05-26', '2016-05-25' gives an age of 1 when it should still be 0. Comparing the dayofyear in different years is clearly dangerous. So using MONTH() and DAY() is necessary after all.

Community
  • 1
  • 1
JonBrave
  • 4,045
  • 3
  • 38
  • 115
  • This should be voted up or even marked as the answer. It is short, elegant and logically correct. – z00l Mar 23 '16 at 09:49
  • 2
    For everyone born after February their Age is incremented one day early on every leap year using the `DayOfYear` method. – AeroX May 23 '16 at 10:49
  • 4
    @AeroX Thank you for spotting this flaw. I decided to leave my solution in as a warning to anyone who might or has used `dayofyear`, but clearly edited to show why it goes wrong. I hope that is suitable. – JonBrave May 25 '16 at 09:26
8

I believe this is similar to other ones posted here.... but this solution worked for the leap year examples 02/29/1976 to 03/01/2011 and also worked for the case for the first year.. like 07/04/2011 to 07/03/2012 which the last one posted about leap year solution did not work for that first year use case.

SELECT FLOOR(DATEDIFF(DAY, @date1 , @date2) / 365.25)

Found here.

AHiggins
  • 7,029
  • 6
  • 36
  • 54
lepert
  • 515
  • 6
  • 8
6

Since there isn't one simple answer that always gives the correct age, here's what I came up with.

SELECT DATEDIFF(YY, DateOfBirth, GETDATE()) - 
     CASE WHEN RIGHT(CONVERT(VARCHAR(6), GETDATE(), 12), 4) >= 
               RIGHT(CONVERT(VARCHAR(6), DateOfBirth, 12), 4) 
     THEN 0 ELSE 1 END AS AGE 

This gets the year difference between the birth date and the current date. Then it subtracts a year if the birthdate hasn't passed yet.

Accurate all the time - regardless of leap years or how close to the birthdate.

Best of all - no function.

Hannover Fist
  • 10,393
  • 1
  • 18
  • 39
  • There is one simple answer using the FORMAT function for SQL Server 2012+ `SELECT [Age] = (0+ FORMAT(GETDATE(),'yyyyMMdd') - FORMAT(DateOfBirth,'yyyyMMdd') ) /10000` The 0+ part tells SQL to calc the char(8) as numbers – ukgav Nov 19 '20 at 11:53
  • FORMAT gives the correct answer but isn't the right answer because it's 16 times slower than the right answer. – Jeff Moden Apr 14 '22 at 03:25
5

I've done a lot of thinking and searching about this and I have 3 solutions that

  • calculate age correctly
  • are short (mostly)
  • are (mostly) very understandable.

Here are testing values:

DECLARE @NOW DATETIME = '2013-07-04 23:59:59' 
DECLARE @DOB DATETIME = '1986-07-05' 

Solution 1: I found this approach in one js library. It's my favourite.

DATEDIFF(YY, @DOB, @NOW) - 
  CASE WHEN DATEADD(YY, DATEDIFF(YY, @DOB, @NOW), @DOB) > @NOW THEN 1 ELSE 0 END

It's actually adding difference in years to DOB and if it is bigger than current date then subtracts one year. Simple right? The only thing is that difference in years is duplicated here.

But if you don't need to use it inline you can write it like this:

DECLARE @AGE INT = DATEDIFF(YY, @DOB, @NOW)
IF DATEADD(YY, @AGE, @DOB) > @NOW
SET @AGE = @AGE - 1

Solution 2: This one I originally copied from @bacon-bits. It's the easiest to understand but a bit long.

DATEDIFF(YY, @DOB, @NOW) - 
  CASE WHEN MONTH(@DOB) > MONTH(@NOW) 
    OR MONTH(@DOB) = MONTH(@NOW) AND DAY(@DOB) > DAY(@NOW) 
  THEN 1 ELSE 0 END

It's basically calculating age as we humans do.


Solution 3: My friend refactored it into this:

DATEDIFF(YY, @DOB, @NOW) - 
  CEILING(0.5 * SIGN((MONTH(@DOB) - MONTH(@NOW)) * 50 + DAY(@DOB) - DAY(@NOW)))

This one is the shortest but it's most difficult to understand. 50 is just a weight so the day difference is only important when months are the same. SIGN function is for transforming whatever value it gets to -1, 0 or 1. CEILING(0.5 * is the same as Math.max(0, value) but there is no such thing in SQL.

drinovc
  • 521
  • 5
  • 16
4
select floor((datediff(day,0,@today) - datediff(day,0,@birthdate)) / 365.2425) as age

There are a lot of 365.25 answers here. Remember how leap years are defined:

  • Every four years
    • except every 100 years
      • except every 400 years
brianary
  • 8,996
  • 2
  • 35
  • 29
  • 1
    Excellent answer. For those who are curious here is an explanation of why 365.2425 is the correct value to use: https://www.grc.nasa.gov/WWW/k-12/Numbers/Math/Mathematical_Thinking/calendar_calculations.htm – vvvv4d Aug 12 '19 at 19:51
3

What about:

DECLARE @DOB datetime
SET @DOB='19851125'   
SELECT Datepart(yy,convert(date,GETDATE())-@DOB)-1900

Wouldn't that avoid all those rounding, truncating and ofsetting issues?

ub_coding
  • 139
  • 5
  • You calculation is not accurate. For example: it fails if you take `'1986-07-05 00:00:00'` for DOB and `'2013-07-04 23:59:59'` for current time. – drinovc Jun 03 '16 at 11:08
  • @ub_coding Are you offering and answer or asking another question? – Aaron C Dec 08 '16 at 18:01
  • this: DECLARE @DOB datetime SET @DOB='19760229' SELECT Datepart(yy,convert(datetime,'19770228')-@DOB)-1900 =1 the main problem is fev 29 gap for most solutions , thats explain truncating rounding etc. – Leonardo Marques de Souza Jun 14 '17 at 21:01
3

Just check whether the below answer is feasible.

DECLARE @BirthDate DATE = '09/06/1979'

SELECT 
 (
 YEAR(GETDATE()) - YEAR(@BirthDate) - 
 CASE  WHEN (MONTH(GETDATE()) * 100) + DATEPART(dd, GETDATE()) >     
 (MONTH(@BirthDate) * 100) + DATEPART(dd, @BirthDate)
 THEN 1             
 ELSE 0             
 END        
 )
3

There are many answers to this question, but I think this one is close to the truth.

The datediff(year,…,…) function, as we all know, only counts the boundaries crossed by the date part, in this case the year. As a result it ignores the rest of the year.

This will only give the age in completed years if the year were to start on the birthday. It probably doesn’t, but we can fake it by adjusting the asking date back by the same amount.

In pseudopseudo code, it’s something like this:

adjusted_today = today - month(dob) + 1 - day(dob) + 1
age = year(adjusted_today - dob)
  • The + 1 is to allow for the fact that the month and day numbers start from 1 and not 0.
  • The reason we subtract the month and the day separately rather than the day of the year is because February has the annoying tendency to change its length.

The calculation in SQL is:

datediff(year,dob,dateadd(month,-month(dob)+1,dateadd(day,-day(dob)+1,today)))

where dob and today are presumed to be the date of birth and the asking date.

You can test this as follows:

WITH dates AS (
    SELECT
        cast('2022-03-01' as date) AS today,
        cast('1943-02-25' as date) AS dob
)
select
    datediff(year,dob,dateadd(month,-month(dob)+1,dateadd(day,-day(dob)+1,today))) AS age
from dates;

which gives you George Harrison’s age in completed years.

This is much cleaner than fiddling about with quarter days which will generally give you misleading values on the edges.

If you have the luxury of creating a scalar function, you can use something like this:

DROP FUNCTION IF EXISTS age;
GO
CREATE FUNCTION age(@dob date, @today date) RETURNS INT AS
BEGIN
    SET @today = dateadd(month,-month(@dob)+1,@today);
    SET @today = dateadd(day,-day(@dob)+1,@today);
    RETURN datediff(year,@dob,@today);
END;
GO

Remember, you need to call dbo.age() because, well, Microsoft.

Manngo
  • 14,066
  • 10
  • 88
  • 110
2
DECLARE @DOB datetime
set @DOB ='11/25/1985'

select floor(
( cast(convert(varchar(8),getdate(),112) as int)-
cast(convert(varchar(8),@DOB,112) as int) ) / 10000
)

source: http://beginsql.wordpress.com/2012/04/26/how-to-calculate-age-in-sql-server/

celsowm
  • 846
  • 9
  • 34
  • 59
  • A shorter way of doing this in SQL Server 2012+ is as follows, and avoids the converts to 112 and floor is not required: `code: SELECT [Age] = (0+ FORMAT(@ToDate,'yyyyMMdd') - FORMAT(@DOB,'yyyyMMdd') ) /10000` – ukgav Mar 04 '19 at 01:11
2

Try This

DECLARE @date datetime, @tmpdate datetime, @years int, @months int, @days int
SELECT @date = '08/16/84'

SELECT @tmpdate = @date

SELECT @years = DATEDIFF(yy, @tmpdate, GETDATE()) - CASE WHEN (MONTH(@date) > MONTH(GETDATE())) OR (MONTH(@date) = MONTH(GETDATE()) AND DAY(@date) > DAY(GETDATE())) THEN 1 ELSE 0 END
SELECT @tmpdate = DATEADD(yy, @years, @tmpdate)
SELECT @months = DATEDIFF(m, @tmpdate, GETDATE()) - CASE WHEN DAY(@date) > DAY(GETDATE()) THEN 1 ELSE 0 END
SELECT @tmpdate = DATEADD(m, @months, @tmpdate)
SELECT @days = DATEDIFF(d, @tmpdate, GETDATE())

SELECT Convert(Varchar(Max),@years)+' Years '+ Convert(Varchar(max),@months) + ' Months '+Convert(Varchar(Max), @days)+'days'
Ed Heal
  • 59,252
  • 17
  • 87
  • 127
  • This answer could be put together better, but it has the only correct and good calculation. The top voted and accepted is only an estimate, and the other highly-voted answer relies on string conversions, which are notoriously slow and prone to issues around internationalization. – Joel Coehoorn Aug 14 '20 at 14:33
2

After trying MANY methods, this works 100% of the time using the modern MS SQL FORMAT function instead of convert to style 112. Either would work but this is the least code.

Can anyone find a date combination which does not work? I don't think there is one :)

--Set parameters, or choose from table.column instead:

DECLARE @DOB    DATE = '2000/02/29' -- If @DOB is a leap day...
       ,@ToDate DATE = '2018/03/01' --...there birthday in this calculation will be 

--0+ part tells SQL to calc the char(8) as numbers:
SELECT [Age] = (0+ FORMAT(@ToDate,'yyyyMMdd') - FORMAT(@DOB,'yyyyMMdd') ) /10000
ukgav
  • 109
  • 1
  • 3
  • While it provides the correct answer, it's not the right answer because FORMAT makes it more than 16 times slower than the right answer. – Jeff Moden Apr 14 '22 at 03:28
  • I'm honoured that legend Jeff Moden would tell me its the correct answer, but the slowest :) Noted. What is the 16X faster correct answer? :) – ukgav Sep 07 '22 at 14:24
  • What rules do you follow for when someone was born on the 29 of February? Once I know that, I can show you one of two methods with test data. – Jeff Moden Sep 09 '22 at 03:21
1
CASE WHEN datepart(MM, getdate()) < datepart(MM, BIRTHDATE) THEN ((datepart(YYYY, getdate()) - datepart(YYYY, BIRTH_DATE)) -1 )
     ELSE 
        CASE WHEN datepart(MM, getdate()) = datepart(MM, BIRTHDATE)
            THEN 
                CASE WHEN datepart(DD, getdate()) < datepart(DD, BIRTHDATE) THEN ((datepart(YYYY, getdate()) - datepart(YYYY, BIRTHDATE)) -1 )
                    ELSE (datepart(YYYY, getdate()) - datepart(YYYY, BIRTHDATE))
                END
        ELSE (datepart(YYYY, getdate()) - datepart(YYYY, BIRTHDATE)) END            
    END
0

How about this:

SET @Age = CAST(DATEDIFF(Year, @DOB, @Stamp) as int)
IF (CAST(DATEDIFF(DAY, DATEADD(Year, @Age, @DOB), @Stamp) as int) < 0) 
    SET @Age = @Age - 1
pajics
  • 2,938
  • 3
  • 23
  • 27
0
SELECT ID,
Name,
DATEDIFF(yy,CONVERT(DATETIME, DOB),GETDATE()) AS AGE,
DOB
FROM MyTable
bv8z
  • 965
  • 2
  • 9
  • 19
  • 1
    You want getdate as the second argument not the first otherwise you get negative number results, and datediff rounds, so select datediff(yy, '20081231', getdate()) will report an age of 1, but they would only be 10 months old. – Andrew Oct 15 '09 at 12:54
  • 2
    This calculation gives incorrect calculations for people who've not yet had their birthday this year. –  Apr 02 '14 at 16:45
0

Try this solution:

declare @BirthDate datetime
declare @ToDate datetime

set @BirthDate = '1/3/1990'
set @ToDate = '1/2/2008'
select @BirthDate [Date of Birth], @ToDate [ToDate],(case when (DatePart(mm,@ToDate) <  Datepart(mm,@BirthDate)) 
        OR (DatePart(m,@ToDate) = Datepart(m,@BirthDate) AND DatePart(dd,@ToDate) < Datepart(dd,@BirthDate))
        then (Datepart(yy, @ToDate) - Datepart(yy, @BirthDate) - 1)
        else (Datepart(yy, @ToDate) - Datepart(yy, @BirthDate))end) Age
snoopy
  • 1
0

This will correctly handle the issues with the birthday and rounding:

DECLARE @dob  datetime
SET @dob='1992-01-09 00:00:00'

SELECT DATEDIFF(YEAR, '0:0', getdate()-@dob)
Digi314
  • 53
  • 2
  • 5
  • 2
    Doesn't handle leap years correctly SELECT DATEDIFF(YEAR, '0:0', convert(datetime, '2014-02-28') -'2012-02-29') gives 2, but should only be 1 – Peter Kerr Dec 11 '14 at 10:10
0

Ed Harper's solution is the simplest I have found which never returns the wrong answer when the month and day of the two dates are 1 or less days apart. I made a slight modification to handle negative ages.

DECLARE @D1 AS DATETIME, @D2 AS DATETIME
SET @D2 = '2012-03-01 10:00:02'
SET @D1 = '2013-03-01 10:00:01'
SELECT
   DATEDIFF(YEAR, @D1,@D2)
   +
   CASE
      WHEN @D1<@D2 AND DATEADD(YEAR, DATEDIFF(YEAR,@D1, @D2), @D1) > @D2
      THEN - 1
      WHEN @D1>@D2 AND DATEADD(YEAR, DATEDIFF(YEAR,@D1, @D2), @D1) < @D2
      THEN 1
      ELSE 0
   END AS AGE
0

The answer marked as correct is nearer to accuracy but, it fails in following scenario - where Year of birth is Leap year and day are after February month

declare @ReportStartDate datetime = CONVERT(datetime, '1/1/2014'),
@DateofBirth datetime = CONVERT(datetime, '2/29/1948')

FLOOR(DATEDIFF(HOUR,@DateofBirth,@ReportStartDate )/8766)


OR

FLOOR(DATEDIFF(HOUR,@DateofBirth,@ReportStartDate )/8765.82) -- Divisor is more accurate than 8766

-- Following solution is giving me more accurate results.

FLOOR(DATEDIFF(YEAR,@DateofBirth,@ReportStartDate) - (CASE WHEN DATEADD(YY,DATEDIFF(YEAR,@DateofBirth,@ReportStartDate),@DateofBirth) > @ReportStartDate THEN 1 ELSE 0 END ))

It worked in almost all scenarios, considering leap year, date as 29 feb, etc.

Please correct me if this formula have any loophole.

  • Final formula is almost exact like in this answer http://stackoverflow.com/a/1572235/168747. But the one here is less readable and contains unnecessary `floor`. – Marek Aug 05 '14 at 08:56
0
Declare @dob datetime
Declare @today datetime

Set @dob = '05/20/2000'
set @today = getdate()

select  CASE
            WHEN dateadd(year, datediff (year, @dob, @today), @dob) > @today 
            THEN datediff (year, @dob, @today) - 1
            ELSE datediff (year, @dob, @today)
        END as Age
AHiggins
  • 7,029
  • 6
  • 36
  • 54
0
CREATE function dbo.AgeAtDate(
    @DOB    datetime,
    @CompareDate datetime
)

returns INT
as
begin

return CASE WHEN @DOB is null
THEN 
    null
ELSE 
DateDiff(yy,@DOB, @CompareDate) 
- CASE WHEN datepart(mm,@CompareDate) > datepart(mm,@DOB) OR (datepart(mm,@CompareDate) = datepart(mm,@DOB) AND datepart(dd,@CompareDate) >= datepart(dd,@DOB))
  THEN 0 
  ELSE 1
  END
END
End

GO
Volodymyr
  • 1,209
  • 1
  • 15
  • 26
  • -1: Will be wrong any time `month(compare) > month(dob)` BUT `day(compare) < day(dob)`, e.g. `select dbo.AgeAtDate('2000-01-14', '2016-02-12')`. – JonBrave Feb 17 '16 at 12:05
  • You are right, thank you for this case, have updated function – Volodymyr Feb 17 '16 at 12:33
0

Here is how i calculate age given a birth date and current date.

select case 
            when cast(getdate() as date) = cast(dateadd(year, (datediff(year, '1996-09-09', getdate())), '1996-09-09') as date)
                then dateDiff(yyyy,'1996-09-09',dateadd(year, 0, getdate()))
            else dateDiff(yyyy,'1996-09-09',dateadd(year, -1, getdate()))
        end as MemberAge
go
Komengem
  • 3,662
  • 7
  • 33
  • 57
0
DECLARE @FromDate DATETIME = '1992-01-2623:59:59.000', 
        @ToDate   DATETIME = '2016-08-10 00:00:00.000',
        @Years INT, @Months INT, @Days INT, @tmpFromDate DATETIME
SET @Years = DATEDIFF(YEAR, @FromDate, @ToDate)
 - (CASE WHEN DATEADD(YEAR, DATEDIFF(YEAR, @FromDate, @ToDate),
          @FromDate) > @ToDate THEN 1 ELSE 0 END) 


SET @tmpFromDate = DATEADD(YEAR, @Years , @FromDate)
SET @Months =  DATEDIFF(MONTH, @tmpFromDate, @ToDate)
 - (CASE WHEN DATEADD(MONTH,DATEDIFF(MONTH, @tmpFromDate, @ToDate),
          @tmpFromDate) > @ToDate THEN 1 ELSE 0 END) 

SET @tmpFromDate = DATEADD(MONTH, @Months , @tmpFromDate)
SET @Days =  DATEDIFF(DAY, @tmpFromDate, @ToDate)
 - (CASE WHEN DATEADD(DAY, DATEDIFF(DAY, @tmpFromDate, @ToDate),
          @tmpFromDate) > @ToDate THEN 1 ELSE 0 END) 

SELECT @FromDate FromDate, @ToDate ToDate, 
       @Years Years,  @Months Months, @Days Days
Masum
  • 119
  • 11
0

What about a solution with only date functions, not math, not worries about leap year

CREATE FUNCTION dbo.getAge(@dt datetime) 
RETURNS int
AS
BEGIN
    RETURN 
        DATEDIFF(yy, @dt, getdate())
        - CASE 
            WHEN 
                MONTH(@dt) > MONTH(GETDATE()) OR 
                (MONTH(@dt) = MONTH(GETDATE()) AND DAY(@dt) > DAY(GETDATE())) 
            THEN 1 
            ELSE 0 
        END
END
fcaserio
  • 726
  • 1
  • 9
  • 18
0
declare @birthday as datetime
set @birthday = '2000-01-01'
declare @today as datetime
set @today = GetDate()
select 
    case when ( substring(convert(varchar, @today, 112), 5,4) >= substring(convert(varchar, @birthday, 112), 5,4)  ) then
        (datepart(year,@today) - datepart(year,@birthday))
    else 
        (datepart(year,@today) - datepart(year,@birthday)) - 1
    end
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
Wasiqul Islam
  • 303
  • 3
  • 11
0

The following script checks the difference in years between now and the given date of birth; the second part checks whether the birthday is already past in the current year; if not, it subtracts it:

SELECT year(NOW()) - year(date_of_birth) - (CONCAT(year(NOW()), '-', month(date_of_birth), '-', day(date_of_birth)) > NOW()) AS Age
FROM tableName;
codeling
  • 11,056
  • 4
  • 42
  • 71
0

Easy and simple

SELECT (YEAR(CURRENT_TIMESTAMP) - YEAR(birthday)) as age FROM db_deirvlon_monyo_users
Kamran Gasimov
  • 1,445
  • 1
  • 14
  • 11
0

There are a lot of answers to this question. Some of them are very lengthy, prone to bugs, or imprecise. As I was scrolling through the solutions, I noticed an especially good one in the comment section. I could not resist sharing it.

SELECT
    DATEDIFF(year, birthday, GETDATE()) -
        CASE
            WHEN (
                MONTH(GETDATE()) > MONTH(birthday)
                ) THEN 1
            WHEN (
                MONTH(GETDATE()) = MONTH(birthday) AND
                DAY(GETDATE()) > DAY(birthday)
                ) THEN 1
            ELSE 0
        END AS age
FROM people

If the month and the day of the birth are more than the current month and day then the date difference is valid. Otherwise, subtract 1 from the date difference. IMO the solution is:

  • Easy to understand: It does not use any string manipulation and magic numbers. It is only 10 lines of code and contains the most necessary functions.
  • Follows human intuition: It has nothing to do with how fast the earth moves around the sun.
  • Precise and performant: No rounding, converting, multiplying, or dividing is used. It is simply comparing and subtracting which is more efficient than dividing by a float.

Credit to @Bacon Bits's comment under this answer.

Péter Szilvási
  • 362
  • 4
  • 17
  • 1
    What happens if my DOB is '10th June 2000' so my age is still 22 as my birthday hasn't arrived yet but by this code logic it gives 23. Am I missing something ? – Coder1990 May 19 '23 at 07:41
  • @Coder1990 You are right. Another case is needed when the DOB month is more than the current month then subtract one from the difference. If the DOB and current month are the same, check the day difference. – Péter Szilvási May 19 '23 at 11:25
  • Small correction: when current month is more than the DOB then subtract one. If months are the same and one if current day is more then DOB day then subtract one. – Péter Szilvási May 20 '23 at 06:26
0

The problem with calculating ages with datetime format is that we do not have uniformity in the year. Not all the years have the same amount of days (365 or 366), neither the months.

The only certain thing is that every year has 12 months, so I think a right usage of the DATEDIFF() function is with month.

In this case:

SELECT ID, Name, DATEDIFF(MONTH,DOB,GETDATE())/12 -
CASE WHEN MONTH(DOB) = MONTH(GETDATE()) AND DAY(DOB)>DAY(GETDATE()) THEN 1 ELSE 0 END AS AGE, DOB
FROM myTable
Bbbgl
  • 9
  • 2
0
declare @birthday varchar(12) = '1986-06-05'

select DATEDIFF(year, @birthday, getdate()) - Case 
         when month(@birthday) > month(getdate()) 
                 or (month(@birthday) = month(getdate()) 
                  and day(@birthday) > day(getdate()))  
         then 1 else 0 
      End As Age
Md Shahriar
  • 2,072
  • 22
  • 11
-1
select datediff(day,'1991-03-16',getdate()) \\for days,get date refers today date
select datediff(year,'1991-03-16',getdate()) \\for years
select datediff(month,'1991-03-16',getdate()) \\for month
AHiggins
  • 7,029
  • 6
  • 36
  • 54
Meera K
  • 11
-1

you should count years by following way :-

select cast(datediff(DAY, '2000-03-01 10:00:01', '2013-03-01 10:00:00') / (365.23076923074) as int) as 'Age'

it's very easy...

Nimesh khatri
  • 763
  • 12
  • 29
-1

If you find your age. select (months_between(sysdate,dob)/12) from table_name; If you find your approximate age. select round(months_between(sysdate,dob)/12) from table_name;enter code here There dob is column name. sysdate used for current date . months_between used for find total month dob to currentdate

  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Mar 21 '22 at 18:45
-2

We used something like here, but then taking the average age:

ROUND(avg(CONVERT(int,DATEDIFF(hour,DOB,GETDATE())/8766.0)),0) AS AverageAge

Notice, the ROUND is outside rather than inside. This will allow for the AVG to be more accurate and we ROUND only once. Making it faster too.

PCPGMR
  • 340
  • 2
  • 7
-2
select DATEDIFF(yy,@DATE,GETDATE()) -
case when DATEPART(mm,GETDATE())*100+DATEPART(dd,GETDATE())>=
DATEPART(mm,@DATE)*100+DATEPART(dd,@DATE) THEN 0
ELSE 1 END 
Will
  • 11,276
  • 9
  • 68
  • 76
-2
SELECT CAST(DATEDIFF(dy, @DOB, GETDATE()+1)/365.25 AS int)
Alex
  • 1,979
  • 16
  • 24
-3

you should use
select FLOOR(DATEDIFF(CURDATE(),DATE(DOB))/365.25) from table_name;
here CURDATE() uses current date you can give own date in 'yyyy-mm-dd' format DATE(DOB) extract yyyy-mm-dd year from your column which is in DATETIME format here DOB is your column name (but you should alter table to modify the data type to be DATETIME in your case which is nvarchar) Note- this query is used in mysql this return age in whole year

-4
DECLARE @yourBirthDate DATETIME = '1987-05-25'
SELECT YEAR(DATEADD(DAY, DATEDIFF(DAY, @yourBirthDate, GETDATE()), CAST('0001-01-01' AS DATETIME2))) - 1
user1080381
  • 1,597
  • 1
  • 16
  • 22