Dan - 6 months ago 43

C# Question

I have a class with 2 date properties:

`FirstDay`

`LastDay`

`LastDay`

`"x year(s) y day(s)"`

2.2 years: "2 years 73 days"

1.002738 years: "1 year 1 day"

0.2 years: "73 days"

2 years: "2 years"

What I have works, but it is long:

`private const decimal DaysInAYear = 365.242M;`

public string LengthInYearsAndDays

{

get

{

var lastDay = this.LastDay ?? DateTime.Today;

var lengthValue = lastDay - this.FirstDay;

var builder = new StringBuilder();

var totalDays = (decimal)lengthValue.TotalDays;

var totalYears = totalDays / DaysInAYear;

var years = (int)Math.Floor(totalYears);

totalDays -= (years * DaysInAYear);

var days = (int)Math.Floor(totalDays);

Func<int, string> sIfPlural = value =>

value > 1 ? "s" : string.Empty;

if (years > 0)

{

builder.AppendFormat(

CultureInfo.InvariantCulture,

"{0} year{1}",

years,

sIfPlural(years));

if (days > 0)

{

builder.Append(" ");

}

}

if (days > 0)

{

builder.AppendFormat(

CultureInfo.InvariantCulture,

"{0} day{1}",

days,

sIfPlural(days));

}

var length = builder.ToString();

return length;

}

}

Is there a more concise way of doing this (but still readable)?

Answer

A `TimeSpan`

doesn't have a sensible concept of "years" because it depends on the start and end point. (Months is similar - how many months are there in 29 days? Well, it depends...)

To give a shameless plug, my Noda Time project makes this really simple though:

```
using System;
using NodaTime;
public class Test
{
static void Main(string[] args)
{
LocalDate start = new LocalDate(2010, 6, 19);
LocalDate end = new LocalDate(2013, 4, 11);
Period period = Period.Between(start, end,
PeriodUnits.Years | PeriodUnits.Days);
Console.WriteLine("Between {0} and {1} are {2} years and {3} days",
start, end, period.Years, period.Days);
}
}
```

Output:

```
Between 19 June 2010 and 11 April 2013 are 2 years and 296 days
```