'Why does TimeSpan not have a Years property?

I was writing a converter that takes a person's date of birth and produces their age in years. I wrote something that looked like this:

public class DateOfBirthToAgeConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var date = value as DateTime?;
        if (date == null) return null;
        return (DateTime.Now - date).Years;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

I found that there is no Years property on the TimeSpan that results from the subtraction of two DateTime objects. I was somewhat surprised by this. I thought about why there might not be a Years. I figured that it might be because of the leap day, but by that logic, there shouldn't be Days because of daylight savings.

The absence of Months made sense, since there is no standard month length.

I was able to write some different code to get the correct age, but I still really want to know why there is no Years or Weeks property on TimeSpan. Does anyone know the reason?



Solution 1:[1]

A TimeSpan only contains the difference between two DateTime values. It is unknown which year this TimeSpan is in. That's also why it doesn't have a Months property.

Example:

TimeSpan.FromDays(60)

How many months are that? 1 or 2?


The absence of Months made sense since there is no standard month length.

There is no standard year length either because of leap years.

Workaround: If you really want to display an approximate value, then doing TimeSpan.TotalDays / 365.2425 will do just fine.

Edit: But only for rough estimations and not for birthdays. In birthday calculation, leap days will accumulate every 4 years as pointed out by Henk Holterman in the comments. Take a look here for calculation of birthdays.

Solution 2:[2]

Rhetorical question: Without a point of reference, how long is a year?

Because a TimeSpan does not have a fixed point in time, it is not possible to unambiguously say how long a year at an unknown time will be. In the simplest case, it might be 365 or 366 days. There are considerably more cases that would affect the outcome.

Solution 3:[3]

I figured that it might be because of the leap day, but by that logic, there shouldn't be Days because of daylight savings.

You have a point there; subtracting two dates doesn't handle daylight savings ideally. If the dates are local time, you may get an unexpected result.

A change in daylight savings time means a gap or overlap in the local time, and that is ignored if you do calculations with the dates. So, if you want to get the exact difference between two DateTime values that are local time, you should convert them to UTC first as that has linear time:

TimeSpan diff = date1.ToUniversalTime() - date2.ToUniversalTime();

The reason that the TimeSpan doesn't have years is that years differ in length. The daylight savings issue is an effect of how you calculate the TimeSpan and can be circumvented, but there is no "linear years" that you can use to circumvent leap years.

Solution 4:[4]

Timespan simply stores number of milliseconds. If you have (1000 * 60 * 60 * 24 * 365.5) 365.5 days worth of milliseconds it's impossible to know if that number of milliseconds spans an entire year and into the next, if it's just short of a year, or if it spans across three years. Same with 30.5 days worth of milliseconds, could span into a second month, could be less than a month, could span across three months.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 Guffa
Solution 4