When calculating differences between two dates you normally use the TimeSpan class. This class however does not have Month and Year which has led me to create a class called TimeSpanWithYearAndMonth.
public TimeSpanWithYearAndMonth(DateTime startDate, DateTime endDate)
{
var span = endDate - startDate;
TotalMonths = 12 * (endDate.Year - startDate.Year) + (endDate.Month - startDate.Month);
Years = TotalMonths / 12;
Months = TotalMonths - (Years * 12);
if (Months == 0 && Years == 0)
{
Days = span.Days;
}
else
{
var startDateExceptYearsAndMonths = startDate.AddYears(Years);
startDateExceptYearsAndMonths = startDateExceptYearsAndMonths.AddMonths(Months);
Days = (endDate - startDateExceptYearsAndMonths).Days;
}
Hours = span.Hours;
Minutes = span.Minutes;
Seconds = span.Seconds;
}
public int Minutes { get; }
public int Hours { get; }
public int Days { get; }
public int Years { get; }
public int Months { get; }
public int Seconds { get; }
public int TotalMonths { get; }
public int TotalHalfYears => TotalMonths / 6;
I extended it with TotalHalfYears ("bi annual") using a simple integer division (you can of course use only this part with your existing TotalMonths calculation if you like).
Here are some unit tests to demonstrate how it works;
[TestFixture]
public class TimeSpanWithYearAndMonthTests
{
[Test]
public void Calculates_correctly()
{
new TimeSpanWithYearAndMonth(new DateTime(2019, 5, 1), new DateTime(2019, 5, 2)).Days.Should().Be(1);
new TimeSpanWithYearAndMonth(new DateTime(2019, 5, 28), new DateTime(2020, 5, 28)).Years.Should().Be(1);
new TimeSpanWithYearAndMonth(new DateTime(2019, 5, 28), new DateTime(2021, 5, 28)).Years.Should().Be(2);
new TimeSpanWithYearAndMonth(new DateTime(2019, 5, 28), new DateTime(2021, 5, 28)).Years.Should().Be(2);
new TimeSpanWithYearAndMonth(new DateTime(2019, 1, 1), new DateTime(2021, 1, 1)).TotalHalfYears.Should().Be(4);
new TimeSpanWithYearAndMonth(new DateTime(2019, 1, 1), new DateTime(2021, 1, 2)).TotalHalfYears.Should().Be(4);
new TimeSpanWithYearAndMonth(new DateTime(2019, 1, 1), new DateTime(2021, 6, 1)).TotalHalfYears.Should().Be(4);
new TimeSpanWithYearAndMonth(new DateTime(2019, 1, 1), new DateTime(2021, 7, 1)).TotalHalfYears.Should().Be(5);
var span = new TimeSpanWithYearAndMonth(new DateTime(2019, 5, 28), new DateTime(2021, 8, 28));
span.Years.Should().Be(2);
span.Months.Should().Be(3);
span.TotalMonths.Should().Be(27);
span = new TimeSpanWithYearAndMonth(new DateTime(2010, 5, 28), new DateTime(2020, 5, 29));
span.Years.Should().Be(10);
span.Months.Should().Be(0);
span.Days.Should().Be(1);
span.TotalMonths.Should().Be(120);
}
}