0

I have tried three ways, all work but get different results:

  1. SELECT @age = DATEDIFF(YY, x.BirthDate, x.LastVisitDate)
    
  2. SELECT @age = (CONVERT(int,CONVERT(char(8),x.LastVisitDate,112)) - CONVERT(char(8),x.BirthDate,112)) / 10000
    
  3. SELECT @age =  FLOOR(DATEDIFF(DAY, x.BirthDate , x.LastVisitDate) / 365.25)
    

The first I get 63, the second and third I get 62, which one is accurate?

GSerg
  • 76,472
  • 17
  • 159
  • 346
hncl
  • 2,295
  • 7
  • 63
  • 129
  • Which dbms are you using? (Those functions are product specific.) – jarlh Jun 04 '19 at 08:12
  • BirthDate and LastVisitDate values? Data types? – jarlh Jun 04 '19 at 08:13
  • MS Sql Server. Thanks – hncl Jun 04 '19 at 08:14
  • Both are DATETIME – hncl Jun 04 '19 at 08:14
  • Looks like ms sql. Then "This function returns the count (as a signed integer value) of the specified datepart boundaries crossed between the specified startdate and enddate." https://learn.microsoft.com/en-us/sql/t-sql/functions/datediff-transact-sql?view=sql-server-2017 . It's up to you how you define "age", this way or other one. – Serg Jun 04 '19 at 08:16
  • See https://stackoverflow.com/a/1404/11683. It is easy to translate to SQL. – GSerg Jun 04 '19 at 08:20
  • Possible duplicate of [Calculate exact date difference in years using SQL](https://stackoverflow.com/questions/23145404/calculate-exact-date-difference-in-years-using-sql) – Serg Jun 04 '19 at 08:22
  • You can check this link. https://www.c-sharpcorner.com/UploadFile/rohatash/calculating-age-in-years-months-and-days-in-sql-server-2012/ – maddy23285 Jun 04 '19 at 08:26

4 Answers4

1

Just use the ancient algorithm from the mainframe era:

SELECT @age = (
           (YEAR(x.LastVisitDate) * 10000 + MONTH(x.LastVisitDate) * 100 + DAY(x.LastVisitDate))
          -
           (YEAR(x.BirthDate)* 10000 + MONTH(x.BirthDate) * 100 + DAY(x.BirthDate))
          ) / 10000
Bliek
  • 466
  • 3
  • 7
0

A person's age at their birthday in a year is (year - BirthYear) This is what datediff with the yy parameter works out. If they have not had their birthday, you have to subtract one year, that's what my IIF does.

As you've found, other methods are flawed. For example I experienced that the days/365.25 will sometimes go wrong around a person's birthday, and is extra trick if they were born on feb 29th

SELECT @age = DATEDIFF(YY, x.BirthDate, x.LastVisitDate) - 
    IIF(MONTH(x.LastVisitDate) < MONTH(x.BirthDate) 
                OR MONTH(x.LastVisitDate) = MONTH(x.BirthDate) AND DAY(x.LastVisitDate) < DAY(x.BirthDate) 
                                  , 1
                                  , 0
        )
Cato
  • 3,652
  • 9
  • 12
0

Try using this scalar function. It takes three parameters.

The start and the end date and an extra option to add one day.

The function returns the result in the form "YY MM DD". There are a few examples

SELECT dbo.CalcDate(NULL, GETDATE(), 0); --> NULL
SELECT dbo.CalcDate(GETDATE(), GETDATE(), 0); --> 0 0 0
SELECT dbo.CalcDate(GETDATE(), GETDATE(), 1); ---> 0 0 1
SELECT dbo.CalcDate('20150101', '20161003', 0); ---> 1 9 2 
SELECT dbo.CalcDate('20031101', '20161003', 0); --->12 11 2
SELECT dbo.CalcDate('20040731', '20040601', 0); ---> 0 1 30 
SELECT dbo.CalcDate('20040731', '20040601', 1); ---> 0 2 0 

And the source code is listed in the snippet below.

CREATE FUNCTION [dbo].[CalcDate]
( 
                @dwstart datetime, @dwend datetime,@extraDay bit
)
RETURNS nvarchar(20)
BEGIN
    DECLARE @yy int;
    DECLARE @mm int;
    DECLARE @dd int;
    DECLARE @increment int;
SET @increment = 0;
    DECLARE @monthDay TABLE
    ( 
                            monthno int, monthdayno int
    );
    DECLARE @dStart AS datetime;
    DECLARE @dEnd AS datetime;
INSERT INTO @monthDay
    VALUES (1, 31);
INSERT INTO @monthDay
    VALUES (2, -1);
INSERT INTO @monthDay
    VALUES (3, 31);
INSERT INTO @monthDay
    VALUES (4, 30);
INSERT INTO @monthDay
    VALUES (5, 31);
INSERT INTO @monthDay
    VALUES (6, 30);
INSERT INTO @monthDay
    VALUES (7, 31);
INSERT INTO @monthDay
    VALUES (8, 31);
INSERT INTO @monthDay
    VALUES (9, 30);
INSERT INTO @monthDay
    VALUES (10, 31);
INSERT INTO @monthDay
    VALUES (11, 30);
INSERT INTO @monthDay
    VALUES (12, 31);
--The order of the arguments is not important
IF @dwStart > @dWEnd
BEGIN
SET @dStart = @dWEnd;
SET @dEnd = @dWStart;
    END;
ELSE
BEGIN
SET @dStart = @dWStart;
SET @dEnd = @dWEnd;
    END;
--

DECLARE @d1 AS INT;
SET @d1 = DAY(@dStart);
    DECLARE @d2 AS int;
SET @d2 = DAY(@dEnd);
    IF @d1 > @d2
    BEGIN
SET @increment = (SELECT
        monthdayno
    FROM @monthDay
    WHERE monthno = MONTH(@dStart));
    END;

IF @increment = -1
BEGIN
--Is it a leap year
SET @increment = (SELECT
        CASE
            WHEN ISDATE(CAST(YEAR(@dStart) AS CHAR(4)) + '0229') = 1 THEN 29
            ELSE 28
        END);
    END;

IF @increment != 0
BEGIN
SET @DD = DAY(@dEnd) + @increment - DAY(@dStart) + (CASE
    WHEN @extraDay = 1 THEN 1
    ELSE 0
END);
SET @increment = 1;
    END;
ELSE
BEGIN
SET @dd = DAY(@dEnd) - DAY(@dStart) + (CASE
    WHEN @extraDay = 1 THEN 1
    ELSE 0
END);
    END;
IF (MONTH(@dStart) + @increment) > MONTH(@dEnd)
BEGIN
SET @mm = MONTH(@dEnd) + 12 - (MONTH(@dStart) + @increment);
SET @increment = 1;
    END;
ELSE
BEGIN
SET @mm = MONTH(@dEnd) - (MONTH(@dStart) + @increment);
SET @increment = 0;
    END;
SET @yy = YEAR(@dEnd) - (YEAR(@dStart) + @increment);
    IF @dd >= 31
    BEGIN
SET @mm = @mm + 1;
SET @dd = @dd - 31;
    END;

IF @mm >= 12
BEGIN
SET @yy = @yy + 1;
SET @mm = @mm - 12;
    END;

RETURN (CONVERT(NVARCHAR(2), @yy) + ' ' + CONVERT(NVARCHAR(2), @mm) + ' ' + CONVERT(NVARCHAR(2), @dd));


END;
0

If you want someone's age, take the difference of "year" and then subtract one based on the ordering of MMDD. So:

select (year(x.LastVisitDate) - year(x.BirthDate) -
        (case when month(x.LastVisitDate) < month(x.BirthDate)
              then 1
              when month(x.LastVisitDate) = month(x.BirthDate) and
                   day(x.LastVisitDate) < day(x.BirthDate)
              then 1
              else 0
         end)
       ) as age

This should be accurate for leap years and leap days and only increment the age on someone's birthday (or if the birthday is Feb 29th, then on Mar 1st).

You can also phrase the case expression using MMDD representation and doing:

        (case when ( month(x.LastVisitDate) * 100 + day(x.LastVisitDate) <
                      month(x.BirthDate) * 100 + day(x.BirthDate
                   )
              then 1
              then 1
              else 0
         end)

Methods using DATEDIFF() simply do not work (easily) because DATEDIFF() is not counting the difference between two periods, but rather the number of time boundaries between them.

Using the difference in days and dividing by 365.25 is an approximation and is going to be off right around the birthday.

Using calendar rules (such as above) should produce the correct results.

Gordon Linoff
  • 1,242,037
  • 58
  • 646
  • 786