• subscribe
May 23, 2007 12:00 AM

DATETIME Calculations, Part 5

Calculate working days, age, and next birthday date
SQL Server Pro
InstantDoc ID #95675
Downloads
95675.zip

Calculating Age
The next problem is calculating the age of a person, given an input birth date (@ birth_date). Of course, when you want to calculate age, you also need an event date. We'll use today (GETDATE) as the event date. Web Listing 2 shows an age-calculation technique that I learned from SQL Server MVP Aaron Bertrand.

The expression first uses the DATEDIFF function to calculate the difference in terms of years between the birth date (@ birth_date) and the event date (GETDATE, in our example); call this difference diff_years. Note that DATEDIFF will simply subtract the birth year from the event year, so if the event date is earlier than the birthday date in the event year, you'll need to subtract 1 year from diff_years. You do this in the second part of the calculation—subtraction of 1 year from diff_years if the event <month and day> value is earlier than the birth <month and day> value. The <month and day> value is an integer in the form mmdd, generated by multiplying the month portion of the date by 100 and adding the day portion of the date. Interestingly, this calculation works perfectly well even when leap years are involved.

To test the calculation, you can play with different dates for both the birth date and the event date. For example, I ran the code in Web Listing 2 on Feb 16, 2007, providing my birth date (i.e., February 12, 1971) for the @birth_date variable, and got my correct age (i.e., 36) back. I ran it again, setting @birth_date to ‘19710216' and still got 36 back. I ran it again, setting @birth_date to "19710217," and got 35 back.

You can also use this calculation in a query against a table, in which each row holds the birth date of a different person. To demonstrate this method, first run the code in Web Listing 3, adding two employees to the Employees table in the Northwind database—George Leaping, born on Feb 29, 1972, and Mary Today, born today. Then, run the query in Web Listing 4 and verify that the ages of the employees are correct. I ran this code on Feb 16, 2007, and got the output that Table 1 shows. Of course, you'll get different output because you're running the code on a different date.

Calculating Next Birthday Date
The third and final problem is calculating a person's next birthday date (in respect to today's date, assuming the birth date is today or earlier). I should define the term "next" accurately. If the person's birthday date already occurred this year prior to today, the calculation should produce that person's birthday date next year; otherwise, it should produce this year's birthday date. For example, I was born on February 12, 1971. If the current year is 2007, running the calculation after February 12 should produce February 12, 2008; if I run the calculation on or before February 12, it should produce February 12, 2007.

Also, you need to consider the special case of someone born on February 29 in a leap year, like our George Leaping, who was born on February 29, 1972. Naturally, if the target year is a leap year, the calculation should produce February 29 of that year. The question is how you want to treat a case in which the target year isn't a leap year. Note that if you use the DATEADD function to add a certain number of years to such a date, SQL Server's default behavior is to produce February 29 if the target year is a leap year, and February 28 if it isn't. If your application is such that you're supposed to produce March 1 if the target year isn't a leap year (e.g., for legal purposes), you'll need to identify this special case and adjust the target date. (I'll make that assumption for this article's technique.)

I'll use the Employees table in the Northwind database, with the two additional rows that we added earlier by running the code in Web Listing 3. The query in Web Listing 5 demonstrates how to calculate the next birthday date for each employee. The code defines a CTE called Args1, which queries the Employees table and calculates two result columns. One is a column called Diff, which holds the difference in terms of years between the birth year and the current year. The other is a column called Today that holds today's date at midnight.

The code defines a second CTE called Args2, which queries Args1 and calculates two result columns called BDCur and BDNxt. BDCur is calculated by adding Diff years to BirthDate, and BDNxt is calculated by adding Diff + 1 years to BirthDate. In more logical terms, BDCur represents the employee's birthday date in the current year and BDNxt represents the employee's birthday date in the following year. Remember that the default behavior of the DATEADD function—in a case in which the source date is February 29 in a leap year and the target year isn't a leap year—is to produce a target date of February 28. If you need to produce March 1 in such a case, you need to add logic to perform the adjustment. This is the purpose of the CTE called Args3; the code defining Args3 queries Args2 and adjusts BDCur and BDNxt if they need adjustment. This adjustment happens through CASE expressions that add one day if the special case is identified. (The source day part is 29, and the target day part is 28.)

Finally, the outer query simply checks which of the two values (BDCur or BDNxt) is the one that really represents the employee's next birthday date. If the current year's birthday date (BDCur) is greater than or equal to today, BDCur is returned. Otherwise, the next year's birthday date (BDNxt) is returned. I ran this query on February 16, 2007, and got the output that Table 2 shows.

As you can see in the output, Nancy Davolio, born on December 8, 1948, will celebrate her next birthday on December 8, 2007. Laura Callahan, born on January 9, 1958, will celebrate her next birthday on January 9, 2008. George Leaping, born on February 29, 1972, will celebrate his next birthday on March 1, 2007. Mary Today, born today, celebrates her next birthday today. After you're done experimenting with these calculations, run the following code to delete the rows for George Leaping and Mary Today from the Employees table:

DELETE FROM Northwind.dbo.
  Employees WHERE EmployeeID > 9;

Now It's Up to You
There are so many other datetime-related calculations that I haven't covered—so many that I could continue covering the subject for quite a few more months. But five datetime articles in a row should give you enough tools to continue the explorations yourself.

For those of you who can't get enough of temporal calculations and queries, you can download the PDF version of the book "Developing Time-Oriented Database Applications in SQL" by Richard T. Snodgrass (Morgan Kaufmann Publishers, July 1999). Mr. Snodgrass made the PDF version of his book available for download for free at his Web site (http://www.cs.arizona.edu/~rts/). Enjoy!

Corrections to this Article:

  • Table 2 contains a typo. The correct date for George Leaping should be as follows: Table 2: Next birthday date of employees LastName FirstName BirthDate NextBirthDay -------------------- ---------- ----------------------- ----------------------- Leaping George 1972-02-29 00:00:00.000 2007-03-01 00:00:00.000 DianaMay- June 01, 2007


ARTICLE TOOLS

Comments
  • barry@idealsoftware.co.uk
    3 years ago
    Jan 26, 2009

    I've looked (as I'm a subscriber) at your SQL Server Magazine articles on date calculations, but I'm still struggling!

    I'm trying to calculate the number of months between two dates (in my case to work out how many standing order payments should have been received). For example:

    Start Date - 30/10/2008

    If End Date is, I want it to return:

    29/4/2009 - 6
    30/4/2009 - 7
    1/5/2009 - 7

    Thank you for any time you may be able to give.

    Regards,

    Barry

  • Anne
    5 years ago
    Jun 28, 2007

    ukribe1, thanks for the suggestion about adding links to all the article's in Itzik's popular DATETIME series. I'll add a box with the article links.
    Anne Grubb, Web Lead Editor, Windows IT Pro and SQL Server Magazine

  • Richard
    5 years ago
    Jun 28, 2007

    Great Article. Just a quick point about the website: why are the other articles in this series not available from the "Related Articles" listing?

  • Cid
    5 years ago
    Jun 04, 2007

    good article

  • Craig
    5 years ago
    Jun 01, 2007

    I found a neat trick several years ago for calculating the age in years between two dates. It takes advantage of integer truncation on division, and works for leap years as well. Working through an example:
    (20070601 - 19710212) / 10000
    360389 / 10000
    36

    SELECT (ABS(CONVERT(INT,(CONVERT(CHAR(8),@i_Date2,112))) - CONVERT(INT,(CONVERT(CHAR(8),@i_Date1,112))))) / 10000

    [Itzik: This is pretty cool!]

You must log on before posting a comment.

Are you a new visitor? Register Here