Calculate age / age next birthday from date of birth without using DateDiff solution

A

Adrian

I hit on this problem converting a VB.NET insurance application to C#.
Age next birthday calculated from date of birth is often needed in
insurance premium calculations.
Originally done using DateDiff in VB.NET which is only available in C#
if you don't mind linking in Microsoft.VisualBasic.dll to your C#
application.
I wanted to avoid this so set about a pure C# solution which uses a
combination of TimeSpan in whole days and the
System.Globalizarion.Calendar class GetDaysInYear() function to work
it out.
The added advantage here over DateDiff is this function should work
with all the different System.Globalization.Calendar types (Japanese
etc.), however I have only tested it with GregorianCalendar.

Here is the C# source code which includes a console application test
harness.
To get "Age" rather than "Age next birthday" just subtract 1 from this
result (sorry for pointing out the obvious!)

<code>
using System;

namespace WorkbenchCSConsole
{
/// <summary>
/// Summary description for AgeNextBirtdatCalculator.
/// </summary>
class AgeNextBirtdatCalculator
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
//
// Test harness for AgeNextBirthday function
//
int AgeNextBirthday;

DateTime DOB;
DateTime DateNow;
System.Globalization.Calendar MyCalendar = new
System.Globalization.GregorianCalendar(); // should work with other
calendars
DateNow = DateTime.Now;
System.Console.WriteLine("Calendar independant date of birth to
age NEXT birtday function (commonly required in the insurance
industry) for C# written by Adrian Hilder, Camdaw Limited
www.camdaw.net. You are free to use and distribute this function in
your applications, you may not sell it for profit as a stand alone
utility without prior consent from Camdaw Limited.\nThis function is
provided as is in the hope it is usefull with no warrantee of any
kind. You should test this function to your satisfaction before using
it in your applications.\nIf you do find any faults please tell
me!\nIf you use this please tell me, I'm interested to know if anyone
uses it (it was about 3 hours work). (contact details at
www.camdaw.net)\n\n");

DOB = DateTime.MinValue;
DOB = DOB.AddYears(1970-1);
DOB = DOB.AddMonths(DateNow.Month-1);
DOB = DOB.AddDays(DateNow.Day-2);
System.Console.WriteLine("Calendar type: " +
MyCalendar.ToString() + "\n");

System.Console.WriteLine("Testing with todays date " +
DateNow.Day + "/" + DateNow.Month + "/" + DateNow.Year + "
(dd/mm/yyyy)\n");

Console.WriteLine("Test with DOB yesterdays date in 1970");
System.Console.WriteLine("DOB entered: " + DOB.Day + "/" +
DOB.Month + "/" + DOB.Year + " (dd/mm/yyyy)");
AgeNextBirthday = DOBToAgeNextBirthday(DOB, MyCalendar);
System.Console.WriteLine("AgeNextBirthdayYears=" +
AgeNextBirthday + "\n");

Console.WriteLine("Test with DOB tommorrows date in 1970");
DOB = DateTime.MinValue;
DOB = DOB.AddYears(1970-1);
DOB = DOB.AddMonths(DateNow.Month-1);
DOB = DOB.AddDays(DateNow.Day);

System.Console.WriteLine("DOB entered: " + DOB.Day + "/" +
DOB.Month + "/" + DOB.Year + " (dd/mm/yyyy)");
AgeNextBirthday = DOBToAgeNextBirthday(DOB, MyCalendar);
System.Console.WriteLine("AgeNextBirthdayYears=" +
AgeNextBirthday + "\n");

Console.WriteLine("Test with DOB this day in 1970");
DOB = DateTime.MinValue;
DOB = DOB.AddYears(1970-1);
DOB = DOB.AddMonths(DateNow.Month-1);
DOB = DOB.AddDays(DateNow.Day-1);

System.Console.WriteLine("DOB entered: " + DOB.Day + "/" +
DOB.Month + "/" + DOB.Year + " (dd/mm/yyyy) ... Happy Birthday!");
AgeNextBirthday = DOBToAgeNextBirthday(DOB, MyCalendar);
System.Console.WriteLine("AgeNextBirthdayYears=" +
AgeNextBirthday + "\n");

while(!(MyCalendar.IsLeapYear(DateNow.Year)))
DateNow = DateNow.AddYears(1);

System.Console.WriteLine("Testing with the next leap year date "
+ DateNow.Day + "/" + DateNow.Month + "/" + DateNow.Year + "
(dd/mm/yyyy)\n");
System.Console.WriteLine("Note: this DOBToAgeNextBirthday
function and test harness was written 3rd August 2004, which was a
leap year.");

Console.WriteLine("Test with DOB yesterdays date in 1970");
System.Console.WriteLine("DOB entered: " + DOB.Day + "/" +
DOB.Month + "/" + DOB.Year + " (dd/mm/yyyy)");
AgeNextBirthday = DOBToAgeNextBirthday(DOB, MyCalendar);
System.Console.WriteLine("AgeNextBirthdayYears=" +
AgeNextBirthday + "\n");

Console.WriteLine("Test with DOB tommorrows date in 1970");
DOB = DateTime.MinValue;
DOB = DOB.AddYears(1970-1);
DOB = DOB.AddMonths(DateNow.Month-1);
DOB = DOB.AddDays(DateNow.Day);

System.Console.WriteLine("DOB entered: " + DOB.Day + "/" +
DOB.Month + "/" + DOB.Year + " (dd/mm/yyyy)");
AgeNextBirthday = DOBToAgeNextBirthday(DOB, MyCalendar);
System.Console.WriteLine("AgeNextBirthdayYears=" +
AgeNextBirthday + "\n");

Console.WriteLine("Test with DOB this day in 1970");
DOB = DateTime.MinValue;
DOB = DOB.AddYears(1970-1);
DOB = DOB.AddMonths(DateNow.Month-1);
DOB = DOB.AddDays(DateNow.Day-1);

System.Console.WriteLine("DOB entered: " + DOB.Day + "/" +
DOB.Month + "/" + DOB.Year + " (dd/mm/yyyy) ... Happy Birthday!");
AgeNextBirthday = DOBToAgeNextBirthday(DOB, MyCalendar);
System.Console.WriteLine("AgeNextBirthdayYears=" +
AgeNextBirthday + "\n");

}

private static int DOBToAgeNextBirthday(DateTime DOB,
System.Globalization.Calendar ThisCalendar)
{
int DaysPassedInYear;
DateTime Now;
int YearIndex;
int DiffDays;

Now = DateTime.Now;
DiffDays = (int)Now.Subtract(DOB).TotalDays;
YearIndex = 0;
DaysPassedInYear = CalcDaysPassedInYear(Now, DOB, YearIndex,
ThisCalendar);
while(DiffDays > DaysPassedInYear)
{
DiffDays = DiffDays - DaysPassedInYear;
YearIndex++; // on to next year
DaysPassedInYear = CalcDaysPassedInYear(Now, DOB, YearIndex,
ThisCalendar);
}
// Now handle last part year
if (DiffDays > 0) // if still some days to account for
{
int DaysPassedToBirthday;
DaysPassedInYear = CalcDaysPassedInYear(Now, DOB, -1,
ThisCalendar);
DaysPassedToBirthday =
CalcDaysPassedInYear(DOB.AddYears(YearIndex), DOB, -1, ThisCalendar);
if (DaysPassedInYear >= DaysPassedToBirthday)
YearIndex++; // birthday has passed this year
}
return YearIndex;
}

private static int CalcDaysPassedInYear(DateTime Now, DateTime
DOB, int YearIndex, System.Globalization.Calendar ThisCalendar)
{
DateTime PartYearStartDate; // 1st Jan <PartYear>
int YearDaysPassed;
int DaysPassedInYear;
if (YearIndex == 0) // days in starting (DOB) part year
{
PartYearStartDate = DateTime.MinValue;
PartYearStartDate = PartYearStartDate.AddYears(DOB.Year-1);
YearDaysPassed =
(int)DOB.Subtract(PartYearStartDate).TotalDays;
DaysPassedInYear = ThisCalendar.GetDaysInYear(DOB.Year) -
YearDaysPassed;
}
else if (YearIndex == -1) // -1 used to indicate last year in
period, i.e. Now.Year
{
PartYearStartDate = DateTime.MinValue;
PartYearStartDate.AddYears(Now.Year);
DaysPassedInYear =
(int)Now.Subtract(PartYearStartDate).TotalDays;
}
else // a year within the period
{
DaysPassedInYear = ThisCalendar.GetDaysInYear(DOB.Year +
YearIndex);
}
return DaysPassedInYear;
}
}
}
</code>
 
N

Nicholas Paldino [.NET/C# MVP]

Adrian,

I am curious, why are you against linking to Microsoft.VisualBasic.dll?
It's distributed as part of the framework, it contains code you know works,
it is managed code, and you save the cost of developing, writing, and
maintaining the code yourself?


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Adrian said:
I hit on this problem converting a VB.NET insurance application to C#.
Age next birthday calculated from date of birth is often needed in
insurance premium calculations.
Originally done using DateDiff in VB.NET which is only available in C#
if you don't mind linking in Microsoft.VisualBasic.dll to your C#
application.
I wanted to avoid this so set about a pure C# solution which uses a
combination of TimeSpan in whole days and the
System.Globalizarion.Calendar class GetDaysInYear() function to work
it out.
The added advantage here over DateDiff is this function should work
with all the different System.Globalization.Calendar types (Japanese
etc.), however I have only tested it with GregorianCalendar.

Here is the C# source code which includes a console application test
harness.
To get "Age" rather than "Age next birthday" just subtract 1 from this
result (sorry for pointing out the obvious!)

<code>
using System;

namespace WorkbenchCSConsole
{
/// <summary>
/// Summary description for AgeNextBirtdatCalculator.
/// </summary>
class AgeNextBirtdatCalculator
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
//
// Test harness for AgeNextBirthday function
//
int AgeNextBirthday;

DateTime DOB;
DateTime DateNow;
System.Globalization.Calendar MyCalendar = new
System.Globalization.GregorianCalendar(); // should work with other
calendars
DateNow = DateTime.Now;
System.Console.WriteLine("Calendar independant date of birth to
age NEXT birtday function (commonly required in the insurance
industry) for C# written by Adrian Hilder, Camdaw Limited
www.camdaw.net. You are free to use and distribute this function in
your applications, you may not sell it for profit as a stand alone
utility without prior consent from Camdaw Limited.\nThis function is
provided as is in the hope it is usefull with no warrantee of any
kind. You should test this function to your satisfaction before using
it in your applications.\nIf you do find any faults please tell
me!\nIf you use this please tell me, I'm interested to know if anyone
uses it (it was about 3 hours work). (contact details at
www.camdaw.net)\n\n");

DOB = DateTime.MinValue;
DOB = DOB.AddYears(1970-1);
DOB = DOB.AddMonths(DateNow.Month-1);
DOB = DOB.AddDays(DateNow.Day-2);
System.Console.WriteLine("Calendar type: " +
MyCalendar.ToString() + "\n");

System.Console.WriteLine("Testing with todays date " +
DateNow.Day + "/" + DateNow.Month + "/" + DateNow.Year + "
(dd/mm/yyyy)\n");

Console.WriteLine("Test with DOB yesterdays date in 1970");
System.Console.WriteLine("DOB entered: " + DOB.Day + "/" +
DOB.Month + "/" + DOB.Year + " (dd/mm/yyyy)");
AgeNextBirthday = DOBToAgeNextBirthday(DOB, MyCalendar);
System.Console.WriteLine("AgeNextBirthdayYears=" +
AgeNextBirthday + "\n");

Console.WriteLine("Test with DOB tommorrows date in 1970");
DOB = DateTime.MinValue;
DOB = DOB.AddYears(1970-1);
DOB = DOB.AddMonths(DateNow.Month-1);
DOB = DOB.AddDays(DateNow.Day);

System.Console.WriteLine("DOB entered: " + DOB.Day + "/" +
DOB.Month + "/" + DOB.Year + " (dd/mm/yyyy)");
AgeNextBirthday = DOBToAgeNextBirthday(DOB, MyCalendar);
System.Console.WriteLine("AgeNextBirthdayYears=" +
AgeNextBirthday + "\n");

Console.WriteLine("Test with DOB this day in 1970");
DOB = DateTime.MinValue;
DOB = DOB.AddYears(1970-1);
DOB = DOB.AddMonths(DateNow.Month-1);
DOB = DOB.AddDays(DateNow.Day-1);

System.Console.WriteLine("DOB entered: " + DOB.Day + "/" +
DOB.Month + "/" + DOB.Year + " (dd/mm/yyyy) ... Happy Birthday!");
AgeNextBirthday = DOBToAgeNextBirthday(DOB, MyCalendar);
System.Console.WriteLine("AgeNextBirthdayYears=" +
AgeNextBirthday + "\n");

while(!(MyCalendar.IsLeapYear(DateNow.Year)))
DateNow = DateNow.AddYears(1);

System.Console.WriteLine("Testing with the next leap year date "
+ DateNow.Day + "/" + DateNow.Month + "/" + DateNow.Year + "
(dd/mm/yyyy)\n");
System.Console.WriteLine("Note: this DOBToAgeNextBirthday
function and test harness was written 3rd August 2004, which was a
leap year.");

Console.WriteLine("Test with DOB yesterdays date in 1970");
System.Console.WriteLine("DOB entered: " + DOB.Day + "/" +
DOB.Month + "/" + DOB.Year + " (dd/mm/yyyy)");
AgeNextBirthday = DOBToAgeNextBirthday(DOB, MyCalendar);
System.Console.WriteLine("AgeNextBirthdayYears=" +
AgeNextBirthday + "\n");

Console.WriteLine("Test with DOB tommorrows date in 1970");
DOB = DateTime.MinValue;
DOB = DOB.AddYears(1970-1);
DOB = DOB.AddMonths(DateNow.Month-1);
DOB = DOB.AddDays(DateNow.Day);

System.Console.WriteLine("DOB entered: " + DOB.Day + "/" +
DOB.Month + "/" + DOB.Year + " (dd/mm/yyyy)");
AgeNextBirthday = DOBToAgeNextBirthday(DOB, MyCalendar);
System.Console.WriteLine("AgeNextBirthdayYears=" +
AgeNextBirthday + "\n");

Console.WriteLine("Test with DOB this day in 1970");
DOB = DateTime.MinValue;
DOB = DOB.AddYears(1970-1);
DOB = DOB.AddMonths(DateNow.Month-1);
DOB = DOB.AddDays(DateNow.Day-1);

System.Console.WriteLine("DOB entered: " + DOB.Day + "/" +
DOB.Month + "/" + DOB.Year + " (dd/mm/yyyy) ... Happy Birthday!");
AgeNextBirthday = DOBToAgeNextBirthday(DOB, MyCalendar);
System.Console.WriteLine("AgeNextBirthdayYears=" +
AgeNextBirthday + "\n");

}

private static int DOBToAgeNextBirthday(DateTime DOB,
System.Globalization.Calendar ThisCalendar)
{
int DaysPassedInYear;
DateTime Now;
int YearIndex;
int DiffDays;

Now = DateTime.Now;
DiffDays = (int)Now.Subtract(DOB).TotalDays;
YearIndex = 0;
DaysPassedInYear = CalcDaysPassedInYear(Now, DOB, YearIndex,
ThisCalendar);
while(DiffDays > DaysPassedInYear)
{
DiffDays = DiffDays - DaysPassedInYear;
YearIndex++; // on to next year
DaysPassedInYear = CalcDaysPassedInYear(Now, DOB, YearIndex,
ThisCalendar);
}
// Now handle last part year
if (DiffDays > 0) // if still some days to account for
{
int DaysPassedToBirthday;
DaysPassedInYear = CalcDaysPassedInYear(Now, DOB, -1,
ThisCalendar);
DaysPassedToBirthday =
CalcDaysPassedInYear(DOB.AddYears(YearIndex), DOB, -1, ThisCalendar);
if (DaysPassedInYear >= DaysPassedToBirthday)
YearIndex++; // birthday has passed this year
}
return YearIndex;
}

private static int CalcDaysPassedInYear(DateTime Now, DateTime
DOB, int YearIndex, System.Globalization.Calendar ThisCalendar)
{
DateTime PartYearStartDate; // 1st Jan <PartYear>
int YearDaysPassed;
int DaysPassedInYear;
if (YearIndex == 0) // days in starting (DOB) part year
{
PartYearStartDate = DateTime.MinValue;
PartYearStartDate = PartYearStartDate.AddYears(DOB.Year-1);
YearDaysPassed =
(int)DOB.Subtract(PartYearStartDate).TotalDays;
DaysPassedInYear = ThisCalendar.GetDaysInYear(DOB.Year) -
YearDaysPassed;
}
else if (YearIndex == -1) // -1 used to indicate last year in
period, i.e. Now.Year
{
PartYearStartDate = DateTime.MinValue;
PartYearStartDate.AddYears(Now.Year);
DaysPassedInYear =
(int)Now.Subtract(PartYearStartDate).TotalDays;
}
else // a year within the period
{
DaysPassedInYear = ThisCalendar.GetDaysInYear(DOB.Year +
YearIndex);
}
return DaysPassedInYear;
}
}
}
</code>
 
J

Jon Skeet [C# MVP]

Nicholas Paldino said:
I am curious, why are you against linking to Microsoft.VisualBasic.dll?
It's distributed as part of the framework, it contains code you know works,
it is managed code, and you save the cost of developing, writing, and
maintaining the code yourself?

There are often good reasons for not using Microsoft.VisualBasic.dll:

1) It's unfamiliar to many C# developers (hey, it's not even documented
in the same place as the main framework, and you don't get to see the
documentation unless you've got VB.NET in your filter)

2) There are often more idiomatic ways of doing things using the
standard framework classes

3) The Microsoft.VisualBasic.dll is less likely to be fully implemented
in other frameworks (such as Mono) than things like DateTime and
TimeSpan

Occasionally a function in the MS.VB assembly does exactly what you
want and it would be a pain to do it using the "normal" .NET classes,
but I would suggest avoiding it in other situations.
 
N

Nicholas Paldino [.NET/C# MVP]

I agree witht this somewhat.

Granted, if there is an alternative in the framework itself outside of
MSVB.dll, then I would take that, assuming I get the behavior I want.

However, if there is a solution that only exists in MSVB.dll, which
would require me to write code otherwise, then I can't see the
justification. The maintenance of code is just not worth it (no matter how
small).

From what I can tell, the DateDiff functionality offers a little more
than what the TimeSpan structure offers (also, the original poster indicated
a specific need for the functionality in DateDiff).
 
N

news.newnet.co.uk

For the pragmatic C# programmer who has no interest calculating age or date
differences in anything other than the Gregorian calendar there is little
reason (as far as I can see) why DateDiff in
Microsoft.VisualBasic.DateAndTime should not be used.
I have even read reports (see
http://www.dotnet247.com/247reference/msgs/14/72863.aspx) that provided you
avoid using legacy methods (e.g. Len(MyString) instead of MyString.Length())
there is no significant measurable peformance penalty in using whats in
Microsoft.VisualBasic since it appears to be "native" to the .NET framework.
I really think that these usefull functions should have been put in a
namespace where everyone can find them whatever the .NET language used.

My reasons for a "pure" C# solution:

1. Microsoft.VisualBasic.DateAndTime.DateDiff are not found located when you
search on DateDiff and filter by C# in Vis.Studio.NET. so I spent a few
minutes thinking and researching about how I would code it in C# before
searching the newsgroups where I found Microsoft.VisualBasic.DateAndTime
existed, by which time I had (re)learned of the globalized calender types.
Having the luxury of time I became attracted to the idea of a solution which
"should" work for other calendar types in System.Globalization which may be
usefull for a future project I have in mind.
2. The application dll is 4K bigger going the DateDiff route (OK hardly
significant).
3. It might be irrational but there are some people out there who insist on
a "pure" C# solution avoiding the Microsoft.VisualBasic namespace, I became
curious to see if anyone would use a "pure" C# solution and if they were
doing it for reasons other than calendar globalization and what those
reasons might be.

TimeSpan might be the nearest C# equivalent to DateDiff, but TimeSpan will
not give you a date difference in months or years, I needed difference in
years to work out age / age next birthday.

If I was pushed for time and found it in MSDN in what I consider a sensible
place I would have gone the Microsoft.VisualBasic.DateAndTime.DateDiff route
and worried about calendar globalization when the need arose.

I have just returned to C# after 2 years programming with VB.NET, I've come
home, stupid I know but I really hate typing "Dim x As Integer" instead of
"int x"; etc. etc. VB.NET makes my fingers ache with all that extra typing.

Nicholas Paldino said:
I agree witht this somewhat.

Granted, if there is an alternative in the framework itself outside of
MSVB.dll, then I would take that, assuming I get the behavior I want.

However, if there is a solution that only exists in MSVB.dll, which
would require me to write code otherwise, then I can't see the
justification. The maintenance of code is just not worth it (no matter how
small).

From what I can tell, the DateDiff functionality offers a little more
than what the TimeSpan structure offers (also, the original poster indicated
a specific need for the functionality in DateDiff).
 
A

AdrianHilder

OK I've fixed my name now ooops.

news.newnet.co.uk said:
For the pragmatic C# programmer who has no interest calculating age or date
differences in anything other than the Gregorian calendar there is little
reason (as far as I can see) why DateDiff in
Microsoft.VisualBasic.DateAndTime should not be used.
I have even read reports (see
http://www.dotnet247.com/247reference/msgs/14/72863.aspx) that provided you
avoid using legacy methods (e.g. Len(MyString) instead of MyString.Length())
there is no significant measurable peformance penalty in using whats in
Microsoft.VisualBasic since it appears to be "native" to the .NET framework.
I really think that these usefull functions should have been put in a
namespace where everyone can find them whatever the .NET language used.

My reasons for a "pure" C# solution:

1. Microsoft.VisualBasic.DateAndTime.DateDiff are not found located when you
search on DateDiff and filter by C# in Vis.Studio.NET. so I spent a few
minutes thinking and researching about how I would code it in C# before
searching the newsgroups where I found Microsoft.VisualBasic.DateAndTime
existed, by which time I had (re)learned of the globalized calender types.
Having the luxury of time I became attracted to the idea of a solution which
"should" work for other calendar types in System.Globalization which may be
usefull for a future project I have in mind.
2. The application dll is 4K bigger going the DateDiff route (OK hardly
significant).
3. It might be irrational but there are some people out there who insist on
a "pure" C# solution avoiding the Microsoft.VisualBasic namespace, I became
curious to see if anyone would use a "pure" C# solution and if they were
doing it for reasons other than calendar globalization and what those
reasons might be.

TimeSpan might be the nearest C# equivalent to DateDiff, but TimeSpan will
not give you a date difference in months or years, I needed difference in
years to work out age / age next birthday.

If I was pushed for time and found it in MSDN in what I consider a sensible
place I would have gone the Microsoft.VisualBasic.DateAndTime.DateDiff route
and worried about calendar globalization when the need arose.

I have just returned to C# after 2 years programming with VB.NET, I've come
home, stupid I know but I really hate typing "Dim x As Integer" instead of
"int x"; etc. etc. VB.NET makes my fingers ache with all that extra typing.

message news:[email protected]...
I agree witht this somewhat.

Granted, if there is an alternative in the framework itself outside of
MSVB.dll, then I would take that, assuming I get the behavior I want.

However, if there is a solution that only exists in MSVB.dll, which
would require me to write code otherwise, then I can't see the
justification. The maintenance of code is just not worth it (no matter how
small).

From what I can tell, the DateDiff functionality offers a little more
than what the TimeSpan structure offers (also, the original poster indicated
a specific need for the functionality in DateDiff).


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Jon Skeet said:
I am curious, why are you against linking to Microsoft.VisualBasic.dll?
It's distributed as part of the framework, it contains code you know works,
it is managed code, and you save the cost of developing, writing, and
maintaining the code yourself?

There are often good reasons for not using Microsoft.VisualBasic.dll:

1) It's unfamiliar to many C# developers (hey, it's not even documented
in the same place as the main framework, and you don't get to see the
documentation unless you've got VB.NET in your filter)

2) There are often more idiomatic ways of doing things using the
standard framework classes

3) The Microsoft.VisualBasic.dll is less likely to be fully implemented
in other frameworks (such as Mono) than things like DateTime and
TimeSpan

Occasionally a function in the MS.VB assembly does exactly what you
want and it would be a pain to do it using the "normal" .NET classes,
but I would suggest avoiding it in other situations.
 
J

Jon Skeet [C# MVP]

news.newnet.co.uk said:
For the pragmatic C# programmer who has no interest calculating age or date
differences in anything other than the Gregorian calendar there is little
reason (as far as I can see) why DateDiff in
Microsoft.VisualBasic.DateAndTime should not be used.
I have even read reports (see
http://www.dotnet247.com/247reference/msgs/14/72863.aspx) that provided you
avoid using legacy methods (e.g. Len(MyString) instead of MyString.Length())
there is no significant measurable peformance penalty in using whats in
Microsoft.VisualBasic since it appears to be "native" to the .NET framework.

It depends on exactly what you're doing - sometimes there *is* a
performance penalty because some VB functions do a lot more than you
need them to. (Other times they do exactly what you need and no more.)
I really think that these usefull functions should have been put in a
namespace where everyone can find them whatever the .NET language used.

I think some of them should possibly have been included in
TimeSpan/DateTime etc. A lot of other ones have direct equivalents in
the library already.

<snip>
 
G

Glenn

Adrian

So, your just after something in C# to work out the ANB.

Apologies if i've gotten the wrong end of the stick.

/// <summary>
/// Returns the age next birthday

/// Note that if the current day is the birthday then the younger age will
be taken

/// this is to keep in line with both the LAP and POS systems.

/// </summary>

/// <param name="dob"></param>

/// <returns></returns>

public static int AgeNextBirthday(DateTime dob)

{

return AgeNextBirthday(DateTime.Now, dob);

}

public static int AgeNextBirthday(DateTime fromDate, DateTime dob)

{

int yearDifference = fromDate.Year - dob.Year;

int ageNextBirthday;

if(fromDate.Month > dob.Month)

{

ageNextBirthday = yearDifference + 1;

}

else if (fromDate.Month == dob.Month)

{

if (fromDate.Day > dob.Day)

{

ageNextBirthday = yearDifference + 1;

}

else

{

ageNextBirthday = yearDifference;

}

}

else

{

ageNextBirthday = yearDifference;

}


return ageNextBirthday;

}

Glenn

Adrian said:
I hit on this problem converting a VB.NET insurance application to C#.
Age next birthday calculated from date of birth is often needed in
insurance premium calculations.
Originally done using DateDiff in VB.NET which is only available in C#
if you don't mind linking in Microsoft.VisualBasic.dll to your C#
application.
I wanted to avoid this so set about a pure C# solution which uses a
combination of TimeSpan in whole days and the
System.Globalizarion.Calendar class GetDaysInYear() function to work
it out.
The added advantage here over DateDiff is this function should work
with all the different System.Globalization.Calendar types (Japanese
etc.), however I have only tested it with GregorianCalendar.

Here is the C# source code which includes a console application test
harness.
To get "Age" rather than "Age next birthday" just subtract 1 from this
result (sorry for pointing out the obvious!)

<code>
using System;

namespace WorkbenchCSConsole
{
/// <summary>
/// Summary description for AgeNextBirtdatCalculator.
/// </summary>
class AgeNextBirtdatCalculator
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
//
// Test harness for AgeNextBirthday function
//
int AgeNextBirthday;

DateTime DOB;
DateTime DateNow;
System.Globalization.Calendar MyCalendar = new
System.Globalization.GregorianCalendar(); // should work with other
calendars
DateNow = DateTime.Now;
System.Console.WriteLine("Calendar independant date of birth to
age NEXT birtday function (commonly required in the insurance
industry) for C# written by Adrian Hilder, Camdaw Limited
www.camdaw.net. You are free to use and distribute this function in
your applications, you may not sell it for profit as a stand alone
utility without prior consent from Camdaw Limited.\nThis function is
provided as is in the hope it is usefull with no warrantee of any
kind. You should test this function to your satisfaction before using
it in your applications.\nIf you do find any faults please tell
me!\nIf you use this please tell me, I'm interested to know if anyone
uses it (it was about 3 hours work). (contact details at
www.camdaw.net)\n\n");

DOB = DateTime.MinValue;
DOB = DOB.AddYears(1970-1);
DOB = DOB.AddMonths(DateNow.Month-1);
DOB = DOB.AddDays(DateNow.Day-2);
System.Console.WriteLine("Calendar type: " +
MyCalendar.ToString() + "\n");

System.Console.WriteLine("Testing with todays date " +
DateNow.Day + "/" + DateNow.Month + "/" + DateNow.Year + "
(dd/mm/yyyy)\n");

Console.WriteLine("Test with DOB yesterdays date in 1970");
System.Console.WriteLine("DOB entered: " + DOB.Day + "/" +
DOB.Month + "/" + DOB.Year + " (dd/mm/yyyy)");
AgeNextBirthday = DOBToAgeNextBirthday(DOB, MyCalendar);
System.Console.WriteLine("AgeNextBirthdayYears=" +
AgeNextBirthday + "\n");

Console.WriteLine("Test with DOB tommorrows date in 1970");
DOB = DateTime.MinValue;
DOB = DOB.AddYears(1970-1);
DOB = DOB.AddMonths(DateNow.Month-1);
DOB = DOB.AddDays(DateNow.Day);

System.Console.WriteLine("DOB entered: " + DOB.Day + "/" +
DOB.Month + "/" + DOB.Year + " (dd/mm/yyyy)");
AgeNextBirthday = DOBToAgeNextBirthday(DOB, MyCalendar);
System.Console.WriteLine("AgeNextBirthdayYears=" +
AgeNextBirthday + "\n");

Console.WriteLine("Test with DOB this day in 1970");
DOB = DateTime.MinValue;
DOB = DOB.AddYears(1970-1);
DOB = DOB.AddMonths(DateNow.Month-1);
DOB = DOB.AddDays(DateNow.Day-1);

System.Console.WriteLine("DOB entered: " + DOB.Day + "/" +
DOB.Month + "/" + DOB.Year + " (dd/mm/yyyy) ... Happy Birthday!");
AgeNextBirthday = DOBToAgeNextBirthday(DOB, MyCalendar);
System.Console.WriteLine("AgeNextBirthdayYears=" +
AgeNextBirthday + "\n");

while(!(MyCalendar.IsLeapYear(DateNow.Year)))
DateNow = DateNow.AddYears(1);

System.Console.WriteLine("Testing with the next leap year date "
+ DateNow.Day + "/" + DateNow.Month + "/" + DateNow.Year + "
(dd/mm/yyyy)\n");
System.Console.WriteLine("Note: this DOBToAgeNextBirthday
function and test harness was written 3rd August 2004, which was a
leap year.");

Console.WriteLine("Test with DOB yesterdays date in 1970");
System.Console.WriteLine("DOB entered: " + DOB.Day + "/" +
DOB.Month + "/" + DOB.Year + " (dd/mm/yyyy)");
AgeNextBirthday = DOBToAgeNextBirthday(DOB, MyCalendar);
System.Console.WriteLine("AgeNextBirthdayYears=" +
AgeNextBirthday + "\n");

Console.WriteLine("Test with DOB tommorrows date in 1970");
DOB = DateTime.MinValue;
DOB = DOB.AddYears(1970-1);
DOB = DOB.AddMonths(DateNow.Month-1);
DOB = DOB.AddDays(DateNow.Day);

System.Console.WriteLine("DOB entered: " + DOB.Day + "/" +
DOB.Month + "/" + DOB.Year + " (dd/mm/yyyy)");
AgeNextBirthday = DOBToAgeNextBirthday(DOB, MyCalendar);
System.Console.WriteLine("AgeNextBirthdayYears=" +
AgeNextBirthday + "\n");

Console.WriteLine("Test with DOB this day in 1970");
DOB = DateTime.MinValue;
DOB = DOB.AddYears(1970-1);
DOB = DOB.AddMonths(DateNow.Month-1);
DOB = DOB.AddDays(DateNow.Day-1);

System.Console.WriteLine("DOB entered: " + DOB.Day + "/" +
DOB.Month + "/" + DOB.Year + " (dd/mm/yyyy) ... Happy Birthday!");
AgeNextBirthday = DOBToAgeNextBirthday(DOB, MyCalendar);
System.Console.WriteLine("AgeNextBirthdayYears=" +
AgeNextBirthday + "\n");

}

private static int DOBToAgeNextBirthday(DateTime DOB,
System.Globalization.Calendar ThisCalendar)
{
int DaysPassedInYear;
DateTime Now;
int YearIndex;
int DiffDays;

Now = DateTime.Now;
DiffDays = (int)Now.Subtract(DOB).TotalDays;
YearIndex = 0;
DaysPassedInYear = CalcDaysPassedInYear(Now, DOB, YearIndex,
ThisCalendar);
while(DiffDays > DaysPassedInYear)
{
DiffDays = DiffDays - DaysPassedInYear;
YearIndex++; // on to next year
DaysPassedInYear = CalcDaysPassedInYear(Now, DOB, YearIndex,
ThisCalendar);
}
// Now handle last part year
if (DiffDays > 0) // if still some days to account for
{
int DaysPassedToBirthday;
DaysPassedInYear = CalcDaysPassedInYear(Now, DOB, -1,
ThisCalendar);
DaysPassedToBirthday =
CalcDaysPassedInYear(DOB.AddYears(YearIndex), DOB, -1, ThisCalendar);
if (DaysPassedInYear >= DaysPassedToBirthday)
YearIndex++; // birthday has passed this year
}
return YearIndex;
}

private static int CalcDaysPassedInYear(DateTime Now, DateTime
DOB, int YearIndex, System.Globalization.Calendar ThisCalendar)
{
DateTime PartYearStartDate; // 1st Jan <PartYear>
int YearDaysPassed;
int DaysPassedInYear;
if (YearIndex == 0) // days in starting (DOB) part year
{
PartYearStartDate = DateTime.MinValue;
PartYearStartDate = PartYearStartDate.AddYears(DOB.Year-1);
YearDaysPassed =
(int)DOB.Subtract(PartYearStartDate).TotalDays;
DaysPassedInYear = ThisCalendar.GetDaysInYear(DOB.Year) -
YearDaysPassed;
}
else if (YearIndex == -1) // -1 used to indicate last year in
period, i.e. Now.Year
{
PartYearStartDate = DateTime.MinValue;
PartYearStartDate.AddYears(Now.Year);
DaysPassedInYear =
(int)Now.Subtract(PartYearStartDate).TotalDays;
}
else // a year within the period
{
DaysPassedInYear = ThisCalendar.GetDaysInYear(DOB.Year +
YearIndex);
}
return DaysPassedInYear;
}
}
}
</code>
 
Top