[c#] calculating the difference in months between two dates

In C#/.NET TimeSpan has TotalDays, TotalMinutes, etc. but I can't figure out a formula for total months difference. Variable days per month and leap years keep throwing me off. How can I get TotalMonths?

Edit Sorry for not being more clear: I know I can't actually get this from TimeSpan but I thought using TotalDays and TotalMinutes would be a good example to express what I was looking for ... except I'm trying to get Total Months.

Example: Dec 25, 2009 - Oct 6, 2009 = 2 TotalMonths. Oct 6th to Nov 5th equals 0 months. On Nov 6th, 1 month. On Dec 6th, 2 months

This question is related to c# .net datetime timespan

The answer is


The accepted answer works perfectly when you want full months.

I needed partial months. This is the solution I came up with for partial months:

    /// <summary>
    /// Calculate the difference in months.
    /// This will round up to count partial months.
    /// </summary>
    /// <param name="lValue"></param>
    /// <param name="rValue"></param>
    /// <returns></returns>
    public static int MonthDifference(DateTime lValue, DateTime rValue)
    {
        var yearDifferenceInMonths = (lValue.Year - rValue.Year) * 12;
        var monthDifference = lValue.Month - rValue.Month;

        return yearDifferenceInMonths + monthDifference + 
            (lValue.Day > rValue.Day
                ? 1 : 0); // If end day is greater than start day, add 1 to round up the partial month
    }

I also needed a year difference with the same need for partial years. Here is the solution I came up with:

    /// <summary>
    /// Calculate the differences in years.
    /// This will round up to catch partial months.
    /// </summary>
    /// <param name="lValue"></param>
    /// <param name="rValue"></param>
    /// <returns></returns>
    public static int YearDifference(DateTime lValue, DateTime rValue)
    {
        return lValue.Year - rValue.Year +
               (lValue.Month > rValue.Month // Partial month, same year
                   ? 1
                   : ((lValue.Month = rValue.Month) 
                     && (lValue.Day > rValue.Day)) // Partial month, same year and month
                   ? 1 : 0);
    }

There is no built in way to do this accurately in idiomatic-c#. There are some workarounds, such as this CodeProject example that people have coded though.


If you want the exact number, you can't from just the Timespan, since you need to know which months you're dealing, and whether you're dealing with a leap year, like you said.

Either go for an approximate number, or do some fidgetting with the original DateTimes


Calculate no of months between 2 dates:

$date1 = '2017-01-20';
$date2 = '2019-01-20';

$ts1 = strtotime($date1);
$ts2 = strtotime($date2);

$year1 = date('Y', $ts1);
$year2 = date('Y', $ts2);

$month1 = date('m', $ts1);
$month2 = date('m', $ts2);

echo $joining_months = (($year2 - $year1) * 12) + ($month2 - $month1);

Old question I know, but might help someone. I've used @Adam accepted answer above, but then checked if the difference is 1 or -1 then check to see if it is a full calendar month's difference. So 21/07/55 and 20/08/55 would not be a full month, but 21/07/55 and 21/07/55 would be.

/// <summary>
/// Amended date of birth cannot be greater than or equal to one month either side of original date of birth.
/// </summary>
/// <param name="dateOfBirth">Date of birth user could have amended.</param>
/// <param name="originalDateOfBirth">Original date of birth to compare against.</param>
/// <returns></returns>
public JsonResult ValidateDateOfBirth(string dateOfBirth, string originalDateOfBirth)
{
    DateTime dob, originalDob;
    bool isValid = false;

    if (DateTime.TryParse(dateOfBirth, out dob) && DateTime.TryParse(originalDateOfBirth, out originalDob))
    {
        int diff = ((dob.Month - originalDob.Month) + 12 * (dob.Year - originalDob.Year));

        switch (diff)
        {
            case 0:
                // We're on the same month, so ok.
                isValid = true;
                break;
            case -1:
                // The month is the previous month, so check if the date makes it a calendar month out.
                isValid = (dob.Day > originalDob.Day);
                break;
            case 1:
                // The month is the next month, so check if the date makes it a calendar month out.
                isValid = (dob.Day < originalDob.Day);
                break;
            default:
                // Either zero or greater than 1 month difference, so not ok.
                isValid = false;
                break;
        }
        if (!isValid)
            return Json("Date of Birth cannot be greater than one month either side of the date we hold.", JsonRequestBehavior.AllowGet);
    }
    else
    {
        return Json("Date of Birth is invalid.", JsonRequestBehavior.AllowGet);
    }
    return Json(true, JsonRequestBehavior.AllowGet);
}

I've written a very simple extension method on DateTime and DateTimeOffset to do this. I wanted it to work exactly like a TotalMonths property on TimeSpan would work: i.e. return the count of complete months between two dates, ignoring any partial months. Because it's based on DateTime.AddMonths() it respects different month lengths and returns what a human would understand as a period of months.

(Unfortunately you can't implement it as an extension method on TimeSpan because that doesn't retain knowledge of the actual dates used, and for months they're important.)

The code and tests are both available on GitHub. The code is very simple:

public static int GetTotalMonthsFrom(this DateTime dt1, DateTime dt2)
{
    DateTime earlyDate = (dt1 > dt2) ? dt2.Date : dt1.Date;
    DateTime lateDate = (dt1 > dt2) ? dt1.Date : dt2.Date;

    // Start with 1 month's difference and keep incrementing
    // until we overshoot the late date
    int monthsDiff = 1;
    while (earlyDate.AddMonths(monthsDiff) <= lateDate)
    {
        monthsDiff++;
    }

    return monthsDiff - 1;
}

And it passes all these unit test cases:

// Simple comparison
Assert.AreEqual(1, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 2, 1)));
// Just under 1 month's diff
Assert.AreEqual(0, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 1, 31)));
// Just over 1 month's diff
Assert.AreEqual(1, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 2, 2)));
// 31 Jan to 28 Feb
Assert.AreEqual(1, new DateTime(2014, 1, 31).GetTotalMonthsFrom(new DateTime(2014, 2, 28)));
// Leap year 29 Feb to 29 Mar
Assert.AreEqual(1, new DateTime(2012, 2, 29).GetTotalMonthsFrom(new DateTime(2012, 3, 29)));
// Whole year minus a day
Assert.AreEqual(11, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2012, 12, 31)));
// Whole year
Assert.AreEqual(12, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2013, 1, 1)));
// 29 Feb (leap) to 28 Feb (non-leap)
Assert.AreEqual(12, new DateTime(2012, 2, 29).GetTotalMonthsFrom(new DateTime(2013, 2, 28)));
// 100 years
Assert.AreEqual(1200, new DateTime(2000, 1, 1).GetTotalMonthsFrom(new DateTime(2100, 1, 1)));
// Same date
Assert.AreEqual(0, new DateTime(2014, 8, 5).GetTotalMonthsFrom(new DateTime(2014, 8, 5)));
// Past date
Assert.AreEqual(6, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2011, 6, 10)));

Below is actually the most accurate way you can do it, since the definition of "1 Month" changes depending on which month it is, and non of the other answers take this into account! If you want more information about the issue which is not built into the framework, you can read this post: A Real Timespan Object With .Years & .Months (however, reading that post isn't necessary to understand and use the function below, it works 100%, without the inherent inaccuracies of the approximation others love to use - and feel free to replace the .ReverseIt function with the built-in .Reverse function you may have on your framework (it's just here for completeness).

Please note that you can get any number of dates/times accuracy, seconds & minutes, or seconds, minutes and days, anywhere up to years (which would contain 6 parts/segments). If you specify top two and it's over a year old, it will return "1 year and 3 months ago" and won't return the rest because you've requested two segments. if it's only a few hours old, then it will only return "2 hours and 1 minute ago". Of course, same rules apply if you specify 1, 2, 3, 4, 5 or 6 segmets (maxes out at 6 because seconds, minutes, hours, days, months, years only make 6 types). It will also correct grammer issues like "minutes" vs "minute" depending on if it's 1 minute or more, same for all types, and the "string" generated will always be grammatically correct.

Here are some examples for use: bAllowSegments identifies how many segments to show... ie: if 3, then return string would be (as an example)... "3 years, 2 months and 13 days" (won't include hours, minutes and seconds as the top 3 time categories are returned), if however, the date was a newer date, such as something a few days ago, specifying the same segments (3) will return "4 days, 1 hour and 13 minutes ago" instead, so it takes everything into account!

if bAllowSegments is 2 it would return "3 years and 2 months" and if 6 (maximum value) would return "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds", but, be reminded that it will NEVER RETURN something like this "0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago" as it understands there is no date data in the top 3 segments and ignores them, even if you specify 6 segments, so don't worry :). Of course, if there is a segment with 0 in it, it will take that into account when forming the string, and will display as "3 days and 4 seconds ago" and ignoring the "0 hours" part! Enjoy and please comment if you like.

 Public Function RealTimeUntilNow(ByVal dt As DateTime, Optional ByVal bAllowSegments As Byte = 2) As String
  ' bAllowSegments identifies how many segments to show... ie: if 3, then return string would be (as an example)...
  ' "3 years, 2 months and 13 days" the top 3 time categories are returned, if bAllowSegments is 2 it would return
  ' "3 years and 2 months" and if 6 (maximum value) would return "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds"
  Dim rYears, rMonths, rDays, rHours, rMinutes, rSeconds As Int16
  Dim dtNow = DateTime.Now
  Dim daysInBaseMonth = Date.DaysInMonth(dt.Year, dt.Month)

  rYears = dtNow.Year - dt.Year
  rMonths = dtNow.Month - dt.Month
  If rMonths < 0 Then rMonths += 12 : rYears -= 1 ' add 1 year to months, and remove 1 year from years.
  rDays = dtNow.Day - dt.Day
  If rDays < 0 Then rDays += daysInBaseMonth : rMonths -= 1
  rHours = dtNow.Hour - dt.Hour
  If rHours < 0 Then rHours += 24 : rDays -= 1
  rMinutes = dtNow.Minute - dt.Minute
  If rMinutes < 0 Then rMinutes += 60 : rHours -= 1
  rSeconds = dtNow.Second - dt.Second
  If rSeconds < 0 Then rSeconds += 60 : rMinutes -= 1

  ' this is the display functionality
  Dim sb As StringBuilder = New StringBuilder()
  Dim iSegmentsAdded As Int16 = 0

  If rYears > 0 Then sb.Append(rYears) : sb.Append(" year" & If(rYears <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rMonths > 0 Then sb.AppendFormat(rMonths) : sb.Append(" month" & If(rMonths <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rDays > 0 Then sb.Append(rDays) : sb.Append(" day" & If(rDays <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rHours > 0 Then sb.Append(rHours) : sb.Append(" hour" & If(rHours <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rMinutes > 0 Then sb.Append(rMinutes) : sb.Append(" minute" & If(rMinutes <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rSeconds > 0 Then sb.Append(rSeconds) : sb.Append(" second" & If(rSeconds <> 1, "s", "") & "") : iSegmentsAdded += 1

parseAndReturn:

  ' if the string is entirely empty, that means it was just posted so its less than a second ago, and an empty string getting passed will cause an error
  ' so we construct our own meaningful string which will still fit into the "Posted * ago " syntax...

  If sb.ToString = "" Then sb.Append("less than 1 second")

  Return ReplaceLast(sb.ToString.TrimEnd(" ", ",").ToString, ",", " and")

 End Function

Of course, you will need a "ReplaceLast" function, which takes a source string, and an argument specifying what needs to be replaced, and another arg specifying what you want to replace it with, and it only replaces the last occurance of that string... i've included my one if you don't have one or dont want to implement it, so here it is, it will work "as is" with no modification needed. I know the reverseit function is no longer needed (exists in .net) but the ReplaceLast and the ReverseIt func are carried over from the pre-.net days, so please excuse how dated it may look (still works 100% tho, been using em for over ten years, can guarante they are bug free)... :). cheers.

<Extension()> _ 
Public Function ReplaceLast(ByVal sReplacable As String, ByVal sReplaceWhat As String, ByVal sReplaceWith As String) As String 
    ' let empty string arguments run, incase we dont know if we are sending and empty string or not. 
    sReplacable = sReplacable.ReverseIt 
    sReplacable = Replace(sReplacable, sReplaceWhat.ReverseIt, sReplaceWith.ReverseIt, , 1) ' only does first item on reversed version! 
    Return sReplacable.ReverseIt.ToString 
End Function 

<Extension()> _ 
Public Function ReverseIt(ByVal strS As String, Optional ByVal n As Integer = -1) As String 
    Dim strTempX As String = "", intI As Integer 

    If n > strS.Length Or n = -1 Then n = strS.Length 

    For intI = n To 1 Step -1 
        strTempX = strTempX + Mid(strS, intI, 1) 
    Next intI 

    ReverseIt = strTempX + Right(strS, Len(strS) - n) 

End Function 

The accepted answer is strongly incorrect:

For these dates: ldate = 2020-08-30 and rdate = 2020-08-01, we have one month, but the accepted answer returns 0.

For these dates: ldate = 2020-08-30 and rdate = 2020-10-01, we have three months, but the accepted answer returns -2.

Here is the correct method (maybe not the unique method, but correct) to calculate the quantity of months between two dates:

  • You don't have to check which date is lower than other.

  • An interval from the first day to the last day of month is counted as a month.

      public static int GetMontsBetween(DateTime date1, DateTime date2)
      {
          int monthCount = 0;
          int direction = date1 < date2 ? 1 : -1;
          date2 = date2.AddDays(direction);
    
          while (date1.Year != date2.Year || date1.Month != date2.Month)
          {
              date1 = date1.AddMonths(direction);
              monthCount++;
          }
    
          return monthCount;
      } 
    

The method returns a list that contains 3 element first is year, second is month and end element is day:

public static List<int> GetDurationInEnglish(DateTime from, DateTime to)
    {
        try
        {
            if (from > to)
                return null;

            var fY = from.Year;
            var fM = from.Month;
            var fD = DateTime.DaysInMonth(fY, fM);

            var tY = to.Year;
            var tM = to.Month;
            var tD = DateTime.DaysInMonth(tY, tM);

            int dY = 0;
            int dM = 0;
            int dD = 0;

            if (fD > tD)
            {
                tM--;

                if (tM <= 0)
                {
                    tY--;
                    tM = 12;
                    tD += DateTime.DaysInMonth(tY, tM);
                }
                else
                {
                    tD += DateTime.DaysInMonth(tY, tM);
                }
            }
            dD = tD - fD;

            if (fM > tM)
            {
                tY--;

                tM += 12;
            }
            dM = tM - fM;

            dY = tY - fY;

            return new List<int>() { dY, dM, dD };
        }
        catch (Exception exception)
        {
            //todo: log exception with parameters in db

            return null;
        }
    }

Here is my contribution to get difference in Months that I've found to be accurate:

namespace System
{
     public static class DateTimeExtensions
     {
         public static Int32 DiffMonths( this DateTime start, DateTime end )
         {
             Int32 months = 0;
             DateTime tmp = start;

             while ( tmp < end )
             {
                 months++;
                 tmp = tmp.AddMonths( 1 );
             }

             return months;
        }
    }
}

Usage:

Int32 months = DateTime.Now.DiffMonths( DateTime.Now.AddYears( 5 ) );

You can create another method called DiffYears and apply exactly the same logic as above and AddYears instead of AddMonths in the while loop.


There are not a lot of clear answers on this because you are always assuming things.

This solution calculates between two dates the months between assuming you want to save the day of month for comparison, (meaning that the day of the month is considered in the calculation)

Example, if you have a date of 30 Jan 2012, 29 Feb 2012 will not be a month but 01 March 2013 will.

It's been tested pretty thoroughly, probably will clean it up later as we use it, and takes in two dates instead of a Timespan, which is probably better. Hope this helps out anyone else.

private static int TotalMonthDifference(DateTime dtThis, DateTime dtOther)
{
    int intReturn = 0;
    bool sameMonth = false;

    if (dtOther.Date < dtThis.Date) //used for an error catch in program, returns -1
        intReturn--;

    int dayOfMonth = dtThis.Day; //captures the month of day for when it adds a month and doesn't have that many days
    int daysinMonth = 0; //used to caputre how many days are in the month

    while (dtOther.Date > dtThis.Date) //while Other date is still under the other
    {
        dtThis = dtThis.AddMonths(1); //as we loop, we just keep adding a month for testing
        daysinMonth = DateTime.DaysInMonth(dtThis.Year, dtThis.Month); //grabs the days in the current tested month

        if (dtThis.Day != dayOfMonth) //Example 30 Jan 2013 will go to 28 Feb when a month is added, so when it goes to march it will be 28th and not 30th
        {
            if (daysinMonth < dayOfMonth) // uses day in month max if can't set back to day of month
                dtThis.AddDays(daysinMonth - dtThis.Day);
            else
                dtThis.AddDays(dayOfMonth - dtThis.Day);
        }
        if (((dtOther.Year == dtThis.Year) && (dtOther.Month == dtThis.Month))) //If the loop puts it in the same month and year
        {
            if (dtOther.Day >= dayOfMonth) //check to see if it is the same day or later to add one to month
                intReturn++;
            sameMonth = true; //sets this to cancel out of the normal counting of month
        }
        if ((!sameMonth)&&(dtOther.Date > dtThis.Date))//so as long as it didn't reach the same month (or if i started in the same month, one month ahead, add a month)
            intReturn++;
    }
    return intReturn; //return month
}

DateTime start = new DateTime(2003, 12, 25);
DateTime end = new DateTime(2009, 10, 6);
int compMonth = (end.Month + end.Year * 12) - (start.Month + start.Year * 12);
double daysInEndMonth = (end - end.AddMonths(1)).Days;
double months = compMonth + (start.Day - end.Day) / daysInEndMonth;

Here's how we did it:

int DifferenceInMonth(DateTime startDate, DateTime endDate) {
    long ToTicks(DateTime date) => new DateTimeOffset(date).Ticks;
    var daysPerMonth = 30.4;
    return (int)Math.Round((ToTicks(endDate) - ToTicks(startDate)) / TimeSpan.TicksPerDay / daysPerMonth);
}

Way late to the game but I imagine this may be helpful to someone. The majority of people tend to measure month to month by date excluding the fact that months come in different variations. Using that frame of thought I created a one liner which compares the dates for us. Using the the following process.

  1. Any # of years over 1 when comparing year will be multiplied by 12, there is no case where this can be equal to less than 1 full year.
  2. If the end year is greater we need to evaluate if the current day is greater or equal to the previous day 2A. If the end day is greater or equal we take the current month and then add 12 months subtract the month of the start month 2B. If the end day is less than the start day we perform the the same as above except we add 1 to the start month before subtracting
  3. If the end year is not greater we perform the same as 2A/2B, but without adding the 12 months because we do not need to evaluate around the year.

        DateTime date = new DateTime(2003, 11, 25);
        DateTime today = new DateTime(2004, 12, 26);
        var time = (today.Year - date.Year > 1 ? (today.Year - date.Year - 1) * 12 : 0) +  (today.Year > date.Year ? (today.Day >= date.Day ? today.Month + 12 - date.Month : today.Month + 12 - (date.Month + 1)) : (today.Day >= date.Day ? today.Month - date.Month : today.Month - (date.Month + 1)));
    

This library calculates the difference of months, considering all parts of DateTime:

// ----------------------------------------------------------------------
public void DateDiffSample()
{
  DateTime date1 = new DateTime( 2009, 11, 8, 7, 13, 59 );
  Console.WriteLine( "Date1: {0}", date1 );
  // > Date1: 08.11.2009 07:13:59
  DateTime date2 = new DateTime( 2011, 3, 20, 19, 55, 28 );
  Console.WriteLine( "Date2: {0}", date2 );
  // > Date2: 20.03.2011 19:55:28

  DateDiff dateDiff = new DateDiff( date1, date2 );

  // differences
  Console.WriteLine( "DateDiff.Years: {0}", dateDiff.Years );
  // > DateDiff.Years: 1
  Console.WriteLine( "DateDiff.Quarters: {0}", dateDiff.Quarters );
  // > DateDiff.Quarters: 5
  Console.WriteLine( "DateDiff.Months: {0}", dateDiff.Months );
  // > DateDiff.Months: 16
  Console.WriteLine( "DateDiff.Weeks: {0}", dateDiff.Weeks );
  // > DateDiff.Weeks: 70
  Console.WriteLine( "DateDiff.Days: {0}", dateDiff.Days );
  // > DateDiff.Days: 497
  Console.WriteLine( "DateDiff.Weekdays: {0}", dateDiff.Weekdays );
  // > DateDiff.Weekdays: 71
  Console.WriteLine( "DateDiff.Hours: {0}", dateDiff.Hours );
  // > DateDiff.Hours: 11940
  Console.WriteLine( "DateDiff.Minutes: {0}", dateDiff.Minutes );
  // > DateDiff.Minutes: 716441
  Console.WriteLine( "DateDiff.Seconds: {0}", dateDiff.Seconds );
  // > DateDiff.Seconds: 42986489

  // elapsed
  Console.WriteLine( "DateDiff.ElapsedYears: {0}", dateDiff.ElapsedYears );
  // > DateDiff.ElapsedYears: 1
  Console.WriteLine( "DateDiff.ElapsedMonths: {0}", dateDiff.ElapsedMonths );
  // > DateDiff.ElapsedMonths: 4
  Console.WriteLine( "DateDiff.ElapsedDays: {0}", dateDiff.ElapsedDays );
  // > DateDiff.ElapsedDays: 12
  Console.WriteLine( "DateDiff.ElapsedHours: {0}", dateDiff.ElapsedHours );
  // > DateDiff.ElapsedHours: 12
  Console.WriteLine( "DateDiff.ElapsedMinutes: {0}", dateDiff.ElapsedMinutes );
  // > DateDiff.ElapsedMinutes: 41
  Console.WriteLine( "DateDiff.ElapsedSeconds: {0}", dateDiff.ElapsedSeconds );
  // > DateDiff.ElapsedSeconds: 29
} // DateDiffSample

You need to work it out yourself off the datetimes. How you deal with the stub days at the end will depend on what you want to use it for.

One method would be to count month and then correct for days at the end. Something like:

   DateTime start = new DateTime(2003, 12, 25);
   DateTime end = new DateTime(2009, 10, 6);
   int compMonth = (end.Month + end.Year * 12) - (start.Month + start.Year * 12);
   double daysInEndMonth = (end - end.AddMonths(1)).Days;
   double months = compMonth + (start.Day - end.Day) / daysInEndMonth;

The problem with months is that it isn't really a simple measure - they aren't constant size. You would need to define your rules for what you want to include, and work from there. For example 1 Jan to 1 Feb - you could argue 2 months are involved there, or you could say that is one month. Then what about "1 Jan 20:00" to "1 Feb 00:00" - that isn't quite an entire full month. Is that 0? 1? what about the other way around (1 Jan 00:00 to 1 Feb 20:00)... 1? 2?

First define the rules, then you'll have to code it yourself, I'm afraid...


If you want to have a result 1 between 28th Feb and 1st March:

DateTime date1, date2;
int monthSpan = (date2.Year - date1.Year) * 12 + date2.Month - date1.Month

If you're dealing with months and years you need something that knows how many days each month has and which years are leap years.

Enter the Gregorian Calendar (and other culture-specific Calendar implementations).

While Calendar doesn't provide methods to directly calculate the difference between two points in time, it does have methods such as

DateTime AddWeeks(DateTime time, int weeks)
DateTime AddMonths(DateTime time, int months)
DateTime AddYears(DateTime time, int years)

Maybe you don't want to know about month fractions; What about this code?


public static class DateTimeExtensions
{
    public static int TotalMonths(this DateTime start, DateTime end)
    {
        return (start.Year * 12 + start.Month) - (end.Year * 12 + end.Month);
    }
}

//  Console.WriteLine(
//     DateTime.Now.TotalMonths(
//         DateTime.Now.AddMonths(-1))); // prints "1"



case IntervalType.Month:
    returnValue = start.AddMonths(-end.Month).Month.ToString();
    break;
case IntervalType.Year:
    returnValue = (start.Year - end.Year).ToString();
    break;

Combing two of the answers above, another extension method is:

public static int ElapsedMonths(this DateTime date1, DateTime date2)
{
    DateTime earlierDate = (date1 > date2) ? date2 : date1;
    DateTime laterDate = (date1 > date2) ? date1 : date2;
    var eMonths = (laterDate.Month - earlierDate.Month) + 12 * (laterDate.Year - earlierDate.Year) - 
                                            ((earlierDate.Day > laterDate.Day) ? 1 : 0);
    return eMonths;
}

Thanks to @AdamRobinson and @MarkWhittaker


(I realize this is an old question, but...)

This is relatively painful to do in pure .NET. I'd recommend my own Noda Time library, which is particularly designed for things like this:

LocalDate start = new LocalDate(2009, 10, 6);
LocalDate end = new LocalDate(2009, 12, 25);
Period period = Period.Between(start, end);
int months = period.Months;

(There are other options, e.g. if you only want a count of months even across years, you'd use Period period = Period.Between(start, end, PeriodUnits.Months);)


You will have to define what you mean by TotalMonths to start with.
A simple definition puts a month at 30.4 days (365.25 / 12).

Beyond that, any definition including fractions seems useless, and the more common integer value (whole months between dates) also depends on non-standard business rules.


http://www.astro.uu.nl/~strous/AA/en/reken/juliaansedag.html

If you can get the time converted from a Gregorian Date into Julian day number, you can just create an operator to do comparisons of the zulian day number, which can be type double to get months, days, seconds, etc. Check out the above link for an algorithm for converting from Gregorian to Julian.


My take on this answer also uses an extension method, but it can return a positive or negative result.

public static int MonthsBefore(this DateTime dt1, DateTime dt2)
{
    (DateTime early, DateTime late, bool dt2After) = dt2 > dt1 ? (dt1,dt2,true) : (dt2,dt1,false);
    DateTime tmp; // Save the result so we don't repeat work
    int months = 1;
    while ((tmp = early.AddMonths(1)) <= late)
    {
        early = tmp;
        months++;
    }
    return (months-1)*(dt2After ? 1 : -1);
}

A couple tests:

// Just under 1 month's diff
Assert.AreEqual(0, new DateTime(2014, 1, 1).MonthsBefore(new DateTime(2014, 1, 31)));
// Just over 1 month's diff
Assert.AreEqual(1, new DateTime(2014, 1, 1).MonthsBefore(new DateTime(2014, 2, 2)));    
// Past date returns NEGATIVE
Assert.AreEqual(-6, new DateTime(2012, 1, 1).MonthsBefore(new DateTime(2011, 6, 10)));

I would do it like this:

static int TotelMonthDifference(this DateTime dtThis, DateTime dtOther)
{
    int intReturn = 0;

    dtThis = dtThis.Date.AddDays(-(dtThis.Day-1));
    dtOther = dtOther.Date.AddDays(-(dtOther.Day-1));

    while (dtOther.Date > dtThis.Date)
    {
        intReturn++;     
        dtThis = dtThis.AddMonths(1);
    }

    return intReturn;
}

Examples related to c#

How can I convert this one line of ActionScript to C#? Microsoft Advertising SDK doesn't deliverer ads How to use a global array in C#? How to correctly write async method? C# - insert values from file into two arrays Uploading into folder in FTP? Are these methods thread safe? dotnet ef not found in .NET Core 3 HTTP Error 500.30 - ANCM In-Process Start Failure Best way to "push" into C# array

Examples related to .net

You must add a reference to assembly 'netstandard, Version=2.0.0.0 How to use Bootstrap 4 in ASP.NET Core No authenticationScheme was specified, and there was no DefaultChallengeScheme found with default authentification and custom authorization .net Core 2.0 - Package was restored using .NetFramework 4.6.1 instead of target framework .netCore 2.0. The package may not be fully compatible Update .NET web service to use TLS 1.2 EF Core add-migration Build Failed What is the difference between .NET Core and .NET Standard Class Library project types? Visual Studio 2017 - Could not load file or assembly 'System.Runtime, Version=4.1.0.0' or one of its dependencies Nuget connection attempt failed "Unable to load the service index for source" Token based authentication in Web API without any user interface

Examples related to datetime

Comparing two joda DateTime instances How to format DateTime in Flutter , How to get current time in flutter? How do I convert 2018-04-10T04:00:00.000Z string to DateTime? How to get current local date and time in Kotlin Converting unix time into date-time via excel Convert python datetime to timestamp in milliseconds SQL Server date format yyyymmdd Laravel Carbon subtract days from current date Check if date is a valid one Why is ZoneOffset.UTC != ZoneId.of("UTC")?

Examples related to timespan

How to Convert string "07:35" (HH:MM) to TimeSpan Check if a given time lies between two times regardless of date Measuring code execution time Work with a time span in Javascript Getting time span between two times in C#? TimeSpan to DateTime conversion What is the correct SQL type to store a .Net Timespan with values > 24:00:00? Check if datetime instance falls in between other two datetime objects Showing Difference between two datetime values in hours C#: what is the easiest way to subtract time?