Wednesday, March 16, 2011

Calculate the next anniversary date after today.

What's the quickest/neatest way to calculate the next anniversary of someone's birthday.

For example, if I knew a person was born on 31st January, 1990, and today is the 10th February 2000, their next anniversary will be 31st January, 2001.

February 29th should roll onto March 1st (e.g. if they were born on February 29th 1990, their first birthday will be March 1st, 1991).

EDIT : Wow - I thought this would be a lot more trivial. I really assumed there would be some library function I could use. Anyhoo, thanks to all of you, I've got what I think is a working solution, that deals with all the stupid Feb 29th issues. It's not very pretty though :-(

Function NextBirthDay2(ByVal dStartDate As Date, ByVal dNow As Date) As Date
    Dim oDate As Date
    Dim bFeb29thHack As Boolean = dStartDate.Month = 2 And dStartDate.Day = 29

    If bFeb29thHack Then
        oDate = New Date(dNow.Year, 3, 1)
    Else
        oDate = New Date(dNow.Year, dStartDate.Month, dStartDate.Day)
    End If

    If (oDate <= dNow) Then
        oDate = oDate.AddYears(1)
    End If

    If Date.IsLeapYear(oDate.Year) And bFeb29thHack Then
        oDate = oDate.AddDays(-1)
    End If
    Return oDate

End Function
From stackoverflow
  • I haven't worked in VB.Net, but I think the C# code will make enough sense:

    private DateTime nextDate(DateTime currentDate, DateTime anniversaryDate)
    {
        DateTime nextDate;
        try{
            nextDate = new DateTime(currentDate.Year, anniversaryDate.Month, anniversaryDate.Day);
        } catch (ArgumentOutOfRangeException)
        {
            //for 29 Feb case.
            nextDate = new DateTime(currentDate.Year, anniversaryDate.Month, anniversaryDate.Day-1).AddDays(1);
        }
    
        if (nextDate <= currentDate)
            nextDate = nextDate.AddYears(1);
        return nextDate;
    }
    
    RB : Gah - so obvious!!! I was having a total brain-freeze. Many thanks :-)
    biozinc : Pleasure. Also, just fixed code so that it works with 29 Feb case.
    RB : I think your solution returns February 28th if you use "2007/03/15" and "2000/02/29" as input :-)
    biozinc : Think it should work now. Thanks for pointing that out.
    bgbg : If (nextDate <= currentDate) evaluates to true, the correction for 29 Feb will result in a wrong date
  • Try this:

    int bMon      = 3; // for March
    int bDayOfMon = 26 // for March 26th
    
    DateTime nextBirthDay = 
           (new DateTime(DateTime.Today.Year, bMon, bDayOfMon - 1 ))
            .AddDays(1).AddYears((DateTime.Today.Month > bMon || 
              (DateTime.Today.Month == bMon && 
               DateTime.Today.Day > bDayOfMon ))? 1: 0);
    

    if your birthdate is Feb 29th this will give you the next Feb 29th, or Mar 1, depending on whether next year is leap year or not...

    RB : I think your solution fails if bMon = 2 and bDayOfMon = 29 with an ArgumentOutOfRangeException.
    Charles Bretana : Just tried it and discovered I had the 1:0 reversed... fixed it, but in C# at least, it does not throw an exception, although for Feb 29th and today, it returns Feb 28th 2009... not March 1st... But if you try it with currrent date next year (not a leap year) it crashes.. fixed that too...
  • Edit: Changed my example so it handles birthdays on leapday.

    Function NextBirthDay(ByVal BirthDate As Date) As Date
    
        If Not Date.IsLeapYear(Now.Year) And BirthDate.Month = 2 And BirthDate.Day = 29 Then BirthDate.AddDays(1)
    
        Dim TestDate As Date = New Date(Now.Year, BirthDate.Month, BirthDate.Day)
    
        If DateDiff(DateInterval.Day, TestDate, Now) > 0 Then
    
            TestDate.AddYears(1)
            REM now check if NEXT year are leapyear, if so and birthday was a leapday, change back to leapday
            If Date.IsLeapYear(TestDate.Year) AndAlso BirthDate.Month = 2 AndAlso BirthDate.Day = 29 Then
                Return New Date(TestDate.Year, 2, 29)
            Else
                Return TestDate
            End If
    
        Else
    
            Return TestDate
    
        End If
    
    End Function
    

    Should work as expected now.

    RB : These both fail when the current year is 2009, and the birthday is 2000-02-29. There's a lot of annoying edge cases around this leap year thing it seems!
    Stefan : Ha. darn.leap years.. I wanted to make my own version so I didnt cheat and look at your examples first. ;) It will be handled with a try catch or maybe one more nested IIF.. ;)
    Stefan : I must say this was an interesting question, something to use in an interview situation when recruiting
  • Does it have to be .NET code ? The easiest way to implement this in SQL will be to use an Auxiliary calendar table. There are plenty of references in google about it for example here.

    RB : Let's assume I don't want to have to licence a (many hundreds of pounds) SQL server installation along with my application shall we? And that even if I did, I would prefer to avoid the overhead of a round trip to the server for what should be a fairly trivial calculation.

0 comments:

Post a Comment