Building an Age Class in C#

posted on 1st May 2007

It may seem like a fairly inconsequential idea since we already have the DateTime and TimeSpan structs. You can take two DateTime instances and easily calculate a TimeSpan. Once you have the TimeSpan, you can retrieve the Days property, divide that by 365 for the round about number of years and you're good to go.

But wait, you can display that as an integer, or perhaps as a decimal for a bit more accuracy, but what if you want a human readable and understandable string like years and months? or even weeks, days or seconds for even younger things?

The C# Vitamins Age encapsulates that functionality into one convenient class that ultimately saves you time! Is that a pun I see before me? Sorry, on to the code...

Using the Age object

DateTime birthdate = new DateTime(1978, 5, 31);

/// creating an Age with a single DateTime argument will
/// calculate the age between that date and the current DateTime.
Age currentAge = new Age(birthdate);

/// or to create a specific age
Age fixedAge = new Age(birthdate, new DateTime(2007, 5, 24, 14, 19, 23));

/// to display the age
/// e.g. 28 years 11 months 3 weeks 4 days 14 hours 19 minutes 23 seconds
Console.WriteLine(fixedAge.ToString());

/// to control the length of the string simply pass
/// in the number of parts to include
Console.WriteLine(fixedAge.ToString(2));
Console.WriteLine(fixedAge.ToString(3));
Console.WriteLine(string.Format("{0:4}", fixedAge));

/// the result from the last three statements are:
/// 28 years 11 months
/// 28 years 11 months 3 weeks
/// 28 years 11 months 3 weeks 4 days

Access to the actual DateTime's and the elapsed TimeSpan are also available:

TimeSpan time = currentAge.Elapsed;
DateTime bornOn = currentAge.Advent;
DateTime stillGoingAt = currentAge.Terminus;

This is quite handy for exposing from your business objects. e.g.

public class Customer
{
	// .. class constructors, members etc

	DateTime _created = DateTime.Now;

	public DateTime Created
	{
		get { return _created; }
	}

	public Age AccountAge
	{
		get { return new Age(_created); }
	}

	// .. other class members etc
}

Then you can use like so:

Customer customer = new Customer();
Console.WriteLine(customer.AccountAge);
/// which actually prints "less than a second"

An Equality Quandary

Age's are sortable and comparable in the context of how we think of age i.e. the value of the age. They are essentially TimeSpans.

However, depending on the context, this becomes a little bit of a contentious issue. If we think about age, we think about a number. Often we say we are the same age as someone else if we were both born in the same year, but logically, we're not of equal age unless we were born at precisely the same time.

Or if you will allow me an analogy. My first DVD Player lasted 3 years and 1 day before it imploded, (let's say conveniently just after the manufacturers warranty expired). After that, a few months passed before getting the next DVD Player and it, funnily enough, also lasted exactly 3 years and 1 day!

Both DVD Players were exactly the same age. But the dates that they started and ended with are completely different.

So the question is, if each age has different start and end dates, but the same elapsed time, can they be equal?

My first thoughts were to work off the elapsed time, so yes. But it's a little ambiguous, so I've purposely left the implementation for Equals and GetHashCode out of this version for the time being (although one answer is included in the source, just commented out).

How do You think it should be handled?

You can download the Age class source code here.

PS: No DVD Players were harmed in the making of this blog post - I've actually only ever owned one, and it's still going :)