416

How to calculate the difference in months between two dates in C#?

Is there is equivalent of VB's DateDiff() method in C#. I need to find difference in months between two dates that are years apart. The documentation says that I can use TimeSpan like:

TimeSpan ts = date1 - date2;

but this gives me data in Days. I don't want to divide this number by 30 because not every month is 30 days and since the two operand values are quite apart from each other, I am afraid dividing by 30 might give me a wrong value.

Any suggestions?

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Rauf
  • 12,326
  • 20
  • 77
  • 126
  • 30
    Define "difference in months", what's the difference in months between "May 1,2010" and "June 16,2010"? 1.5, 1 or something else? – Cheng Chen Jan 09 '11 at 12:04
  • 8
    Or, to stress this point further, what's the difference in months between 31 December 2010 and 1 Jan 2011? Depending on the daytime this could be a difference of only 1 second; would you count this as a difference of one month? – stakx - no longer contributing Jan 09 '11 at 12:36
  • Here is the simple and short code in case, you still couldn't get the answer, see this [POST](http://stackoverflow.com/questions/8820603/how-a-month-is-defined-in-the-rest-of-the-world) http://stackoverflow.com/questions/8820603/how-a-month-is-defined-in-the-rest-of-the-world – wirol Jan 11 '12 at 15:45
  • 15
    Danny: 1 month and 15 days. stakx: 0 months and 1 day. The point is to get the *month* component. This seems pretty obvious to me and is a good question. – Kirk Woll Feb 09 '12 at 17:30
  • `DateDiff` implantation: http://referencesource.microsoft.com/#Microsoft.VisualBasic/DateAndTime.vb,229. – dovid Jun 28 '15 at 10:04
  • What comes immediately to my mind is the number of calendar months covered by the date range. The common business meaning of month refers to calendar time. – Suncat2000 Jun 14 '19 at 14:15
  • I would use NodaTime for that: https://nodatime.org/2.4.x/userguide/arithmetic --> Search "Finding a period between two values". – FranzHuber23 Jan 22 '20 at 09:12
  • 3
    I think since OP mentions vb's DateDiff, all these questions are answered. The answers happen to be the same as w/ SQL Server's datediff. Just answer the question... To be clear, its the number of month boundaries that are crossed between the two dates, (inclusive) – greg Mar 31 '20 at 14:28

45 Answers45

575

Assuming the day of the month is irrelevant (i.e. the diff between 2011.1.1 and 2010.12.31 is 1), with date1 > date2 giving a positive value and date2 > date1 a negative value

((date1.Year - date2.Year) * 12) + date1.Month - date2.Month

Or, assuming you want an approximate number of 'average months' between the two dates, the following should work for all but very huge date differences.

date1.Subtract(date2).Days / (365.25 / 12)

Note, if you were to use the latter solution then your unit tests should state the widest date range which your application is designed to work with and validate the results of the calculation accordingly.


Update (with thanks to Gary)

If using the 'average months' method, a slightly more accurate number to use for the 'average number of days per year' is 365.2425.

Adam Ralph
  • 29,453
  • 4
  • 60
  • 67
  • 3
    @Kurru - 365 / 12 is only an approximate measure of the average length of a month in days. It is an inaccurate measure. For small date ranges this inaccuracy can be tolerated but for very huge date ranges this inaccuracy may become significant. – Adam Ralph Jan 09 '11 at 12:40
  • Ahhh forgot about the variable month thing. +1 – Kurru Jan 09 '11 at 12:44
  • @Kurru - However, it is also worth pointing out if this 'average month' number is indeed what is being sought after then it is likely that it will be used in a calculation which itself will be an approximation which may mean that the inaccuracy is not significant. We would need to know the intended usage of the result in order to judge this. – Adam Ralph Jan 09 '11 at 12:49
  • Adam, the way I read the question, what is being sought after is your first code snippet. Was about to post it myself. – Roman Starkov Jan 09 '11 at 13:06
  • 35
    I think it is necessary to consider Day component. Something like this `(date1.Year - date2.Year) * 12 + date1.Month - date2.Month + (date1.Day >= date2.Day ? 0 : -1)` – DrunkCoder Nov 21 '12 at 22:04
  • 2
    @DrunkCoder it depends on the requirements of a given system. In some cases your solution may indeed be the best choice. E.g. it's important to consider what happens when two dates span a 31 day month, a 30 day month, a 28 day February or a 29 day February. If the results of your formula deliver what the system requires then it's clearly the right choice. If not, then something else is required. – Adam Ralph Nov 23 '12 at 07:32
  • 6
    To second what Adam said, I spent years writing code for Acturaries. Some calculations were _divide by number of days, round up by 30 to get monthly figure_. Sometimes counting months assumed _every date starts on the first of the month, count whole months accordingly_. There is no _best_ method when it comes to calculating dates. Unless **you** are the customer you're writing code for, push this back up the chain and get it clarified, possibly by you customers accountant. – Binary Worrier Sep 30 '13 at 07:53
  • 3
    365.2425 is slightly more accurate number of days in a Gregorian Calendar, if that is what you are using. However, by DateTime.MaxValue (January 1, 10000) that's only about 59 days difference. Also, the definition of a year can be much different depending on your perspective https://en.wikipedia.org/wiki/Year. – Gary Sep 22 '15 at 20:39
  • Thanks @Gary, I wonder if should update my answer to use 365.2425. It's a cheap change and does indeed improve the accuracy. – Adam Ralph Sep 23 '15 at 21:13
  • Note that the first solution is invalid if one is asking for a date range that lapses over December to January, but doesn't actually complete a year. i.e: Sep 2017 - February 2018 would provide incorrect result – Tjad Clark Dec 12 '17 at 11:12
  • @TjadClark I just tried the first solution for the dates you mention and it gave me the expected answer of 5 months. – Adam Ralph Dec 12 '17 at 18:13
  • what is the result of total months between 20-01-2021 and 20-03-2021? I guess it should be 2.0xx, but if you considered Feb as 30 days, you'll get a different answer "1.8xx or something" I've answered this in another question check it out if you like https://stackoverflow.com/a/65284425/105445 – Wahid Bitar Dec 14 '20 at 06:19
  • the second instruction works better than first because if I've date1 = "2021-02-01 00:00:00" and date2 = "2020-12-31 00:00:00" than I've (2021-2020)*12 +2-12 = 2... – Francesco Feb 02 '21 at 11:19
  • @Francesco it depends what you want. The first suggestion solves one requirement, the second solves another. – Adam Ralph Feb 03 '21 at 18:36
  • Interesting this gets such high marks,.var date1 = new DateTime(2021, 1, 1); var date2 = new DateTime(2021, 2, 23); var r = ((date1.Year - date2.Year) * 12) + date1.Month - date2.Month; // returns -1 var z = date1.Subtract(date2).Days / (365.25 / 12); // returns -1.74127310061602 – Russ Ebbing Feb 22 '21 at 20:27
  • Note that not in every calendar every year has 12 month! – sich Apr 29 '21 at 14:24
  • Sure. I believe Gregorian calendar was a reasonable assumption in making my answer. – Adam Ralph Apr 30 '21 at 16:11
  • This method will not work for lunar calendar where there is year with two same month. For example, year 2020 in Vietnamese Calendar will have two April – Vu Thai Duy Nov 03 '21 at 15:41
  • @VuThaiDuy I refer you to the comment directly before yours. – Adam Ralph Nov 04 '21 at 17:58
  • 1
    For those who suggested using the Microsoft.VisualBasic.dll it should be made known that the suggestion by @AdamRalph matches the code in the dll pretty much verbatim: case DateInterval.Month: { Calendar currentCalendar = CurrentCalendar; return (currentCalendar.GetYear(Date2) - currentCalendar.GetYear(Date1)) * 12 + currentCalendar.GetMonth(Date2) - currentCalendar.GetMonth(Date1); } – SpaceGhost440 Aug 11 '22 at 19:57
236

Here is a comprehensive solution to return a DateTimeSpan, similar to a TimeSpan, except that it includes all the date components in addition to the time components.

Usage:

void Main()
{
    DateTime compareTo = DateTime.Parse("8/13/2010 8:33:21 AM");
    DateTime now = DateTime.Parse("2/9/2012 10:10:11 AM");
    var dateSpan = DateTimeSpan.CompareDates(compareTo, now);
    Console.WriteLine("Years: " + dateSpan.Years);
    Console.WriteLine("Months: " + dateSpan.Months);
    Console.WriteLine("Days: " + dateSpan.Days);
    Console.WriteLine("Hours: " + dateSpan.Hours);
    Console.WriteLine("Minutes: " + dateSpan.Minutes);
    Console.WriteLine("Seconds: " + dateSpan.Seconds);
    Console.WriteLine("Milliseconds: " + dateSpan.Milliseconds);
}

Outputs:

Years: 1
Months: 5
Days: 27
Hours: 1
Minutes: 36
Seconds: 50
Milliseconds: 0

For convenience, I've lumped the logic into the DateTimeSpan struct, but you may move the method CompareDates wherever you see fit. Also note, it doesn't matter which date comes before the other.

public struct DateTimeSpan
{
    public int Years { get; }
    public int Months { get; }
    public int Days { get; }
    public int Hours { get; }
    public int Minutes { get; }
    public int Seconds { get; }
    public int Milliseconds { get; }

    public DateTimeSpan(int years, int months, int days, int hours, int minutes, int seconds, int milliseconds)
    {
        Years = years;
        Months = months;
        Days = days;
        Hours = hours;
        Minutes = minutes;
        Seconds = seconds;
        Milliseconds = milliseconds;
    }

    enum Phase { Years, Months, Days, Done }

    public static DateTimeSpan CompareDates(DateTime date1, DateTime date2)
    {
        if (date2 < date1)
        {
            var sub = date1;
            date1 = date2;
            date2 = sub;
        }

        DateTime current = date1;
        int years = 0;
        int months = 0;
        int days = 0;

        Phase phase = Phase.Years;
        DateTimeSpan span = new DateTimeSpan();
        int officialDay = current.Day;

        while (phase != Phase.Done)
        {
            switch (phase)
            {
                case Phase.Years:
                    if (current.AddYears(years + 1) > date2)
                    {
                        phase = Phase.Months;
                        current = current.AddYears(years);
                    }
                    else
                    {
                        years++;
                    }
                    break;
                case Phase.Months:
                    if (current.AddMonths(months + 1) > date2)
                    {
                        phase = Phase.Days;
                        current = current.AddMonths(months);
                        if (current.Day < officialDay && officialDay <= DateTime.DaysInMonth(current.Year, current.Month))
                            current = current.AddDays(officialDay - current.Day);
                    }
                    else
                    {
                        months++;
                    }
                    break;
                case Phase.Days:
                    if (current.AddDays(days + 1) > date2)
                    {
                        current = current.AddDays(days);
                        var timespan = date2 - current;
                        span = new DateTimeSpan(years, months, days, timespan.Hours, timespan.Minutes, timespan.Seconds, timespan.Milliseconds);
                        phase = Phase.Done;
                    }
                    else
                    {
                        days++;
                    }
                    break;
            }
        }

        return span;
    }
}
Kirk Woll
  • 76,112
  • 22
  • 180
  • 195
  • 2
    @KirkWoll thanks. But why is that DateTimeSpan returns `34` days for this date time difference actually it is `35` http://www.timeanddate.com/date/durationresult.html?d26=01&m1=11&y1=2012&d2=31&m2=12&y2=2012 – Deeptechtons Nov 26 '12 at 03:36
  • @Deeptechtons, nice catch. There were a couple issues you've brought to my attention, both having to do with the start date is `31` and the date "passes through" months with fewer days. I've inverted the logic (so that it goes from early to later than vice versa) and now accumulates the months without modifying the current date (and thus passing through in-between months with fewer days) Still not entirely sure what the ideal result should be when comparing `10/31/2012` to `11/30/2012`. Right now the result is `1` month. – Kirk Woll Nov 26 '12 at 14:09
  • @KirkWoll thanks for the update, maybe i got few more gotchas let me affirm it after some tests Good Job :) – Deeptechtons Nov 27 '12 at 04:16
  • 2
    I wrote an answer http://stackoverflow.com/a/17537472/1737957 to a similar question which tested proposed answers (and found that most of them don't work). This answer is one of the few that does work (according to my test suite). Link to github on my answer. – jwg Aug 21 '13 at 09:51
  • @KirkWoll - This answer doesn't appear to work for edge cases where the from date has a day value higher than the to date's month or where the source date is a leap day. Try `2020-02-29` to `2021-06-29` - it returns "1y 4m 1d", but the value should be "1y 4m 0d", right? – Enigmativity Jun 03 '15 at 09:29
  • @Enigmativity, thanks for your comments. I've updated my answer by handling this scenario in the `Phase.Months` section. Seems to work for me. – Kirk Woll Apr 02 '16 at 21:45
  • Very cool, but please note this algorithm has some edge-case issues. If you happen to use `DateTime.MinValue` or `DateTime.MaxValue`in date values this will fall over. e.g. `System.ArgumentOutOfRangeException': "The added or subtracted value results in an un-representable DateTime."` – iCollect.it Ltd Apr 19 '16 at 09:40
50

You could do

if ( date1.AddMonths(x) > date2 )
Mongus Pong
  • 11,337
  • 9
  • 44
  • 72
  • This is so simple and works perfect for me. I was pleasantly surprised to see it works as intended when calculating a date from the end of 1 month to a date at the end of the next month that has less days. For example.. 1-31-2018 + 1 month = Feb 28 218 – lucky.expert Feb 26 '18 at 15:05
  • This is one of the better solutions. – barnacle.m May 22 '18 at 12:39
  • Really simple and efficient solution! The best answer proposed. – Cedric Arnould Feb 26 '19 at 21:03
  • 3
    What if date1 = 2018-10-28 and date2 = 2018-12-21 ? Answer will be 2. while correct answer should be 3. Because of date range is for 3 months. if we count only months ignoring days. So this answer is NOT correct. – Tommix Mar 08 '19 at 17:57
  • More logical would be: `if ( date1.AddMonths(x).Month == date2.Month )` then you just use x + 1 as months count – Tommix Mar 08 '19 at 18:00
  • 1
    Am I missing something... this is a true / false check on whether the dates differ by at least the given number of months, not a calculation of that number of months, which is what I thought the o/p asked for. – Chris Peacock Jan 19 '21 at 19:13
37

If you want the exact number of full months, always positive (2000-01-15, 2000-02-14 returns 0), considering a full month is when you reach the same day the next month (something like the age calculation)

public static int GetMonthsBetween(DateTime from, DateTime to)
{
    if (from > to) return GetMonthsBetween(to, from);

    var monthDiff = Math.Abs((to.Year * 12 + (to.Month - 1)) - (from.Year * 12 + (from.Month - 1)));

    if (from.AddMonths(monthDiff) > to || to.Day < from.Day)
    {
        return monthDiff - 1;
    }
    else
    {
        return monthDiff;
    }
}

Edit reason: the old code was not correct in some cases like :

new { From = new DateTime(1900, 8, 31), To = new DateTime(1901, 8, 30), Result = 11 },

Test cases I used to test the function:

var tests = new[]
{
    new { From = new DateTime(1900, 1, 1), To = new DateTime(1900, 1, 1), Result = 0 },
    new { From = new DateTime(1900, 1, 1), To = new DateTime(1900, 1, 2), Result = 0 },
    new { From = new DateTime(1900, 1, 2), To = new DateTime(1900, 1, 1), Result = 0 },
    new { From = new DateTime(1900, 1, 1), To = new DateTime(1900, 2, 1), Result = 1 },
    new { From = new DateTime(1900, 2, 1), To = new DateTime(1900, 1, 1), Result = 1 },
    new { From = new DateTime(1900, 1, 31), To = new DateTime(1900, 2, 1), Result = 0 },
    new { From = new DateTime(1900, 8, 31), To = new DateTime(1900, 9, 30), Result = 0 },
    new { From = new DateTime(1900, 8, 31), To = new DateTime(1900, 10, 1), Result = 1 },
    new { From = new DateTime(1900, 1, 1), To = new DateTime(1901, 1, 1), Result = 12 },
    new { From = new DateTime(1900, 1, 1), To = new DateTime(1911, 1, 1), Result = 132 },
    new { From = new DateTime(1900, 8, 31), To = new DateTime(1901, 8, 30), Result = 11 },
};
DisplayName
  • 13,283
  • 2
  • 11
  • 19
Guillaume86
  • 14,341
  • 4
  • 53
  • 53
  • Just so as to avoid confusion for other people, I think this solution is not correct. Using the test case: ```new { From = new DateTime(2015, 12, 31), To = new DateTime(2015, 6, 30), Result = 6 }``` the test will fail as the result is 5. – Cristian Badila Dec 16 '15 at 17:58
  • Added a quick gist with the fix I propose [here](https://gist.github.com/cristi-badila/5c3b92e420126ed2922c) – Cristian Badila Dec 16 '15 at 18:57
  • I'm not sure I get it, my function returns 6 as it should: https://dotnetfiddle.net/MRZNnC – Guillaume86 Dec 17 '15 at 16:48
  • I copied the test case here by hand and It has a mistake. The failing spec should be: ``new { From = new DateTime(2015, 12, 31), To = new DateTime(2016, 06, 30), Result = 6 }``. The "bug" lies in the ``to.Day < from.Day`` code which doesn't take into account that months can end in a different "day of the month". In this case from December 31st 2015, until June 30 2016, 6 complete months will have passed (since June has 30 days) but your code would return 5. – Cristian Badila Dec 18 '15 at 13:13
  • 3
    It's expected behavior in my opinion, well or it's the behavior I do expect at least. I precised a complete month is when you reach the same day (or the next month like in this case). – Guillaume86 Dec 18 '15 at 15:32
  • I understand, that's why I said that "I think the solution is not correct". I still tried to offer an alternative which results in less confusion. In my mind a month is a calendar subunit with a variable number of days and it makes sense to consider a month complete when it's last day is reached. As another example (taking into account February can have 28/29 days) using your code the difference between 2016, 2, 29 and 2017, 2, 28 would be 11 months which to me is "surprising". – Cristian Badila Dec 18 '15 at 17:01
  • Well I guess it's a just a matter of preference. I was also going to mention the leap day case to justify my choice but it's not universal either. There's countries that have set the rule for leap day birthdays in one way and some the other, I side with UK in this case: https://en.wikipedia.org/wiki/February_29#Legal_status . – Guillaume86 Dec 18 '15 at 17:53
  • If you want to take into account the last in the month (31 to 30). Don't know if this works for all scenarios, but if you ignore the day check if the "to date" is the last in the month, since then you have all the days that month can have. You should get 6 month between 2015,12,31 and 2016,06,30 – Gaotter Jan 31 '22 at 09:26
25

I checked the usage of this method in VB.NET via MSDN and it seems that it has a lot of usages. There is no such a built-in method in C#. (Even it's not a good idea) you can call VB's in C#.

  1. Add Microsoft.VisualBasic.dll to your project as a reference
  2. use Microsoft.VisualBasic.DateAndTime.DateDiff in your code
Cheng Chen
  • 42,509
  • 16
  • 113
  • 174
  • 7
    Why do you think it's not a good idea? Intuitively, I would guess that the library is 'just another .NET library' to the runtime. Note, I am playing devil's advocate here, I would also be relunctant to do this since it just 'feels wrong' (kind of cheating) but I wonder if there is any convincing technical reason not to do this. – Adam Ralph Jan 09 '11 at 12:33
  • 3
    @AdamRalph: No reason at all not to do it. Those libraries are implemented in 100% managed code, so it's all the same as everything else. The only conceivable difference is that the `Microsoft.VisualBasic.dll` module has to be loaded, but the time it takes to do that is negligible. There's no reason to cheat yourself out of thoroughly tested and useful features just because you've chosen to write your program in C#. (This goes for things like `My.Application.SplashScreen` as well.) – Cody Gray - on strike Jan 09 '11 at 12:43
  • 4
    Would you change your mind if you knew that it was written in C#? It was. By the same logic, using System.Data and PresentationFramework is cheating too, substantial parts of it written in C++/CLI. – Hans Passant Jan 09 '11 at 12:49
  • @Cody Gray and Hans Passant - both your arguments are convincing and I agree with them. There is no technical reason not to use this library. I suppose my reluctance comes from the fact that the libraries were designed specifically for VB and they come with a lot of strange baggage which was designed specifically to make VB6 programmers feel at home programming for .NET. One risk may be that it might encourage other team members to use some of this which would unecessarliy distort their C# from the most natural patterns. – Adam Ralph Jan 09 '11 at 12:56
  • @Hans Passant: I wasn't making any reference to the language the library was written in as I realise this is irrelevant. I would also have guessed that it was written in C#. Moreover, I don't care if any of the libraries I use were written in C++/CLI, C#, VB.NET, F# or any other weird and wonderful .NET language (although I might draw the line at J# just out of principle ;-) ) – Adam Ralph Jan 09 '11 at 13:07
  • 3
    @AdamRalph: Any particular examples of that "strange baggage" that spring to mind? Or are you saying that purely hypothetically? And yeah, it might mess with the minds of some of your C# buddies who have been writing an epic amount of code to do something that you can do in one line with the right `using` statement, but I doubt there'll be any serious damage. – Cody Gray - on strike Jan 09 '11 at 13:21
  • Sorry, my comment was directed at Danny, I realize I mixed it up. – Hans Passant Jan 09 '11 at 13:40
  • @Cody Gray - I absolutely agree that there may be many legitimate use cases for the VB library in C#. An example of 'strange baggage' is `DateAndTime.Year(DateTime)`, which seems to exists only to lower the barrier of entry for VB6 programmers. If a relative novice is tasked to make a changes to code which uses `Microsoft.VisualBasic`, they may stumble across this and use `DateAndTime.Year(myDate)` instead of `myDate.Year`. I realise this is a trivial example and it's likely the benefits will often outweigh this minor risk. I'm not trying to make a statement here, just expressing a concern. – Adam Ralph Jan 09 '11 at 20:49
  • @AdamRalph: It seems more likely that `DateAndTime.Year(myDate)` just calls `myDate.Year` (or if you're really lucky, compiles down to the same IL). But I understood your point, I was just wondering if there was anything in particular you have in mind. Your feeling isn't uncommon; in fact, it's probably the norm. I just think it's largely unfounded. There's an unnecessary amount of disdain for VB 6, and too much of that has carried over to VB.NET. – Cody Gray - on strike Jan 10 '11 at 07:55
  • 1
    @Cody Gray: agreed, the example is trivial as you illustrate. It's the extra code 'noise' introduced by calling such an unusual (from a C# POV) method that I'd be keen to avoid. In a well organised team such things would anyway be picked up in code review and can be easily avoided. BTW - I'm not trying to attack VB6/VB.NET. I described such methods as 'strange' only because, from a .NET POV, there's no reason for `DateAndTime.Year()` to exist, given that `DateTime` has a `Year` property. It only exists to make VB.NET appear more like VB6. As a former VB6 programmer, I can appreciate this ;-) – Adam Ralph Jan 10 '11 at 10:34
  • The problem for me with this answer is that DateDiff only compares month parts, not taking into account the day of the month: 'Larger Intervals. If Interval is set to DateInterval.Year, the return value is calculated purely from the year parts of Date1 and Date2. Similarly, the return value for DateInterval.Month is calculated purely from the year and month parts of the arguments, and for DateInterval.Quarter from the quarters containing the two dates.' - from: https://msdn.microsoft.com/en-us/library/b5xbyt6f(v=vs.90).aspx – Ian Grainger Jan 18 '16 at 11:11
11

Use Noda Time:

LocalDate start = new LocalDate(2013, 1, 5);
LocalDate end = new LocalDate(2014, 6, 1);
Period period = Period.Between(start, end, PeriodUnits.Months);
Console.WriteLine(period.Months); // 16

(example source)

Community
  • 1
  • 1
Edward Brey
  • 40,302
  • 20
  • 199
  • 253
10

To get difference in months (both start and end inclusive), irrespective of dates:

DateTime start = new DateTime(2013, 1, 1);
DateTime end = new DateTime(2014, 2, 1);
var diffMonths = (end.Month + end.Year * 12) - (start.Month + start.Year * 12);
Chirag
  • 4,046
  • 1
  • 33
  • 24
  • 5
    Imagine `start` and `end` are identical. Then you get a result of 1. How is that right? Why do you add 1 to the result? Who is up-voting this answer :-/ ? – paul Dec 18 '14 at 06:35
  • For identical dates, it will give output as 1. Basically, it will count all the months inclusive start and end months. – Chirag Dec 18 '14 at 11:29
  • 3
    doesn't sound like the difference between two items to me. What is the difference between 2 and 2? Is it really 1? I would suggest the difference is 0. – paul Dec 18 '14 at 11:57
  • I can't wrap my head around how this works, but I put it in excel and it does. I added the +1 at the end because for my purposes, 8/2023 - 8/2023 = 1 month. His solution does say "start and end inclusive". – flashsplat Aug 11 '23 at 01:36
8

I just needed something simple to cater for e.g. employment dates where only the month/year is entered, so wanted distinct years and months worked in. This is what I use, here for usefullness only

public static YearsMonths YearMonthDiff(DateTime startDate, DateTime endDate) {
    int monthDiff = ((endDate.Year * 12) + endDate.Month) - ((startDate.Year * 12) + startDate.Month) + 1;
    int years = (int)Math.Floor((decimal) (monthDiff / 12));
    int months = monthDiff % 12;
    return new YearsMonths {
        TotalMonths = monthDiff,
            Years = years,
            Months = months
    };
}

.NET Fiddle

jenson-button-event
  • 18,101
  • 11
  • 89
  • 155
4

You can use the DateDiff class of the Time Period Library for .NET:

// ----------------------------------------------------------------------
public void DateDiffSample()
{
  DateTime date1 = new DateTime( 2009, 11, 8, 7, 13, 59 );
  DateTime date2 = new DateTime( 2011, 3, 20, 19, 55, 28 );
  DateDiff dateDiff = new DateDiff( date1, date2 );

  // differences
  Console.WriteLine( "DateDiff.Months: {0}", dateDiff.Months );
  // > DateDiff.Months: 16

  // elapsed
  Console.WriteLine( "DateDiff.ElapsedMonths: {0}", dateDiff.ElapsedMonths );
  // > DateDiff.ElapsedMonths: 4

  // description
  Console.WriteLine( "DateDiff.GetDescription(6): {0}", dateDiff.GetDescription( 6 ) );
  // > DateDiff.GetDescription(6): 1 Year 4 Months 12 Days 12 Hours 41 Mins 29 Secs
} // DateDiffSample
4

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.

Morgs
  • 1,558
  • 3
  • 19
  • 34
4

You can use Noda Time https://nodatime.org/

LocalDate start = new LocalDate(2010, 1, 5);
LocalDate end = new LocalDate(2012, 6, 1);
Period period = Period.Between(start, end, PeriodUnits.Months);
Console.WriteLine(period.Months);
Shah Zain
  • 388
  • 4
  • 10
3

My understanding of the total months difference between 2 dates has an integral and a fractional part (the date matters).

The integral part is the full months difference.

The fractional part, for me, is the difference of the % of the day (to the full days of month) between the starting and ending months.

public static class DateTimeExtensions
{
    public static double TotalMonthsDifference(this DateTime from, DateTime to)
    {
        //Compute full months difference between dates
        var fullMonthsDiff = (to.Year - from.Year)*12 + to.Month - from.Month;

        //Compute difference between the % of day to full days of each month
        var fractionMonthsDiff = ((double)(to.Day-1) / (DateTime.DaysInMonth(to.Year, to.Month)-1)) -
            ((double)(from.Day-1)/ (DateTime.DaysInMonth(from.Year, from.Month)-1));

        return fullMonthsDiff + fractionMonthsDiff;
    }
}

With this extension, those are the results:

2/29/2000 TotalMonthsDifference 2/28/2001 => 12
2/28/2000 TotalMonthsDifference 2/28/2001 => 12.035714285714286
01/01/2000 TotalMonthsDifference 01/16/2000 => 0.5
01/31/2000 TotalMonthsDifference 01/01/2000 => -1.0
01/31/2000 TotalMonthsDifference 02/29/2000 => 1.0
01/31/2000 TotalMonthsDifference 02/28/2000 => 0.9642857142857143
01/31/2001 TotalMonthsDifference 02/28/2001 => 1.0
George Mavritsakis
  • 6,829
  • 2
  • 35
  • 42
2

This worked for what I needed it for. The day of month didn't matter in my case because it always happens to be the last day of the month.

public static int MonthDiff(DateTime d1, DateTime d2){
    int retVal = 0;

    if (d1.Month<d2.Month)
    {
        retVal = (d1.Month + 12) - d2.Month;
        retVal += ((d1.Year - 1) - d2.Year)*12;
    }
    else
    {
        retVal = d1.Month - d2.Month;
        retVal += (d1.Year - d2.Year)*12;
    }
    //// Calculate the number of years represented and multiply by 12
    //// Substract the month number from the total
    //// Substract the difference of the second month and 12 from the total
    //retVal = (d1.Year - d2.Year) * 12;
    //retVal = retVal - d1.Month;
    //retVal = retVal - (12 - d2.Month);

    return retVal;
}
Elmer
  • 139
  • 1
  • 4
2

There's 3 cases: same year, previous year and other years.

If the day of the month does not matter...

public int GetTotalNumberOfMonths(DateTime start, DateTime end)
{
    // work with dates in the right order
    if (start > end)
    {
        var swapper = start;
        start = end;
        end = swapper;
    }

    switch (end.Year - start.Year)
    {
        case 0: // Same year
            return end.Month - start.Month;

        case 1: // last year
            return (12 - start.Month) + end.Month;

        default:
            return 12 * (3 - (end.Year - start.Year)) + (12 - start.Month) + end.Month;
    }
}
Patrice Calvé
  • 674
  • 6
  • 12
  • If the day of the month does not matter, So the default section should be this: `12 * (end.Year - start.Year) - start.Month + end.Month;` – Ali Tabandeh Jul 06 '23 at 20:13
2

The most precise way is this that return difference in months by fraction :

private double ReturnDiffereceBetweenTwoDatesInMonths(DateTime startDateTime, DateTime endDateTime)
{
    double result = 0;
    double days = 0;
    DateTime currentDateTime = startDateTime;
    while (endDateTime > currentDateTime.AddMonths(1))
    {
        result ++;

        currentDateTime = currentDateTime.AddMonths(1);
    }

    if (endDateTime > currentDateTime)
    {
        days = endDateTime.Subtract(currentDateTime).TotalDays;

    }
    return result + days/endDateTime.GetMonthDays;
}
2

Here is a simple solution that works at least for me. It's probably not the fastest though because it uses the cool DateTime's AddMonth feature in a loop:

public static int GetMonthsDiff(DateTime start, DateTime end)
{
    if (start > end)
        return GetMonthsDiff(end, start);

    int months = 0;
    do
    {
        start = start.AddMonths(1);
        if (start > end)
            return months;

        months++;
    }
    while (true);
}
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
2

This simple static function calculates the fraction of months between two Datetimes, e.g.

  • 1.1. to 31.1. = 1.0
  • 1.4. to 15.4. = 0.5
  • 16.4. to 30.4. = 0.5
  • 1.3. to 1.4. = 1 + 1/30

The function assumes that the first date is smaller than the second date. To deal with negative time intervals one can modify the function easily by introducing a sign and a variable swap at the beginning.

public static double GetDeltaMonths(DateTime t0, DateTime t1)
{
     DateTime t = t0;
     double months = 0;
     while(t<=t1)
     {
         int daysInMonth = DateTime.DaysInMonth(t.Year, t.Month);
         DateTime endOfMonth = new DateTime(t.Year, t.Month, daysInMonth);
         int cutDay = endOfMonth <= t1 ? daysInMonth : t1.Day;
         months += (cutDay - t.Day + 1) / (double) daysInMonth;
         t = new DateTime(t.Year, t.Month, 1).AddMonths(1);
     }
     return Math.Round(months,2);
 }
Michael
  • 21
  • 2
2

one line solution

For first, check if both dates are in the current year, if not get months of whole years and then add months from the start and end year.

DateTime dateFrom = new DateTime(2019, 2, 1);
DateTime dateTo = new DateTime(2021, 5, 25);

With the first month

var monthCount = dateFrom.Year != dateTo.Year ? ((dateTo.Year - dateFrom.Year - 1) * 12) + (13 - dateFrom.Month + dateTo.Month) : dateTo.Month - dateFrom.Month + 1;

result = 28

Without first month

monthCount = dateFrom.Year != dateTo.Year ? ((dateTo.Year - dateFrom.Year - 1) * 12) + (12 - dateFrom.Month + dateTo.Month) : dateTo.Month - dateFrom.Month;

result = 27

2

I was working on a project that only dealt in years and months.

/// <summary>
/// Get the total months between two date.  This will count whole months and not care about the day.
/// </summary>
/// <param name="firstDate">First date.</param>
/// <param name="lastDate">Last date.</param>
/// <returns>Number of month apart.</returns>
private static int GetTotalMonths(DateOnly firstDate, DateOnly lastDate)
{
    int yearsApart = lastDate.Year - firstDate.Year;
    int monthsApart = lastDate.Month - firstDate.Month;
    return (yearsApart * 12) + monthsApart;
}

private static int GetTotalMonths(DateTime firstDate, DateTime lastDate)
{
    return GetTotalMonths(DateOnly.FromDateTime(firstDate), DateOnly.FromDateTime(lastDate));
}
p.campbell
  • 98,673
  • 67
  • 256
  • 322
1

This is from my own library, will return the difference of months between two dates.

public static int MonthDiff(DateTime d1, DateTime d2)
{
    int retVal = 0;

    // Calculate the number of years represented and multiply by 12
    // Substract the month number from the total
    // Substract the difference of the second month and 12 from the total
    retVal = (d1.Year - d2.Year) * 12;
    retVal = retVal - d1.Month;
    retVal = retVal - (12 - d2.Month);

    return retVal;
}
Matthias
  • 7,432
  • 6
  • 55
  • 88
Wayne
  • 19
  • 1
1

You can have a function something like this.

For Example, from 2012/12/27 to 2012/12/29 becomes 3 days. Likewise, from 2012/12/15 to 2013/01/15 becomes 2 months, because up to 2013/01/14 it's 1 month. from 15th it's 2nd month started.

You can remove the "=" in the second if condition, if you do not want to include both days in the calculation. i.e, from 2012/12/15 to 2013/01/15 is 1 month.

public int GetMonths(DateTime startDate, DateTime endDate)
{
    if (startDate > endDate)
    {
        throw new Exception("Start Date is greater than the End Date");
    }

    int months = ((endDate.Year * 12) + endDate.Month) - ((startDate.Year * 12) + startDate.Month);

    if (endDate.Day >= startDate.Day)
    {
        months++;
    }

    return months;
}
Firnas
  • 1,665
  • 4
  • 21
  • 31
1
public static int PayableMonthsInDuration(DateTime StartDate, DateTime EndDate)
{
    int sy = StartDate.Year; int sm = StartDate.Month; int count = 0;
    do
    {
        count++;if ((sy == EndDate.Year) && (sm >= EndDate.Month)) { break; }
        sm++;if (sm == 13) { sm = 1; sy++; }
    } while ((EndDate.Year >= sy) || (EndDate.Month >= sm));
    return (count);
}

This solution is for Rental/subscription calculation, where difference doesn't means to be subtraction, it's meant to be the span in within those two dates.

Sukanta
  • 103
  • 1
  • 1
  • 4
1

I wrote a function to accomplish this, because the others ways weren't working for me.

public string getEndDate (DateTime startDate,decimal monthCount)
{
    int y = startDate.Year;
    int m = startDate.Month;

    for (decimal  i = monthCount; i > 1; i--)
    {
        m++;
        if (m == 12)
        { y++;
            m = 1;
        }
    }
    return string.Format("{0}-{1}-{2}", y.ToString(), m.ToString(), startDate.Day.ToString());
}
R.Akhlaghi
  • 729
  • 1
  • 12
  • 23
  • Please answer in English (vs any invented language ...) – kleopatra Sep 30 '13 at 07:36
  • Why not just do startDate.AddMonths(monthCount).ToShortDateString()? This doesn't answer the original question that was asked anyway! – TabbyCool Apr 15 '14 at 15:44
  • oh, sorry @TabbyCool , this code works good in my program! programmers rule says: first code works and then optimization! tanx for ur comment :) – R.Akhlaghi Apr 19 '14 at 05:29
1

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, but here:

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
}
GreatNate
  • 191
  • 8
1

you can use the following extension: Code

public static class Ext
{
    #region Public Methods

    public static int GetAge(this DateTime @this)
    {
        var today = DateTime.Today;
        return ((((today.Year - @this.Year) * 100) + (today.Month - @this.Month)) * 100 + today.Day - @this.Day) / 10000;
    }

    public static int DiffMonths(this DateTime @from, DateTime @to)
    {
        return (((((@to.Year - @from.Year) * 12) + (@to.Month - @from.Month)) * 100 + @to.Day - @from.Day) / 100);
    }

    public static int DiffYears(this DateTime @from, DateTime @to)
    {
        return ((((@to.Year - @from.Year) * 100) + (@to.Month - @from.Month)) * 100 + @to.Day - @from.Day) / 10000;
    }

    #endregion Public Methods
}

Implementation !

int Age;
int years;
int Months;
//Replace your own date
var d1 = new DateTime(2000, 10, 22);
var d2 = new DateTime(2003, 10, 20);
//Age
Age = d1.GetAge();
Age = d2.GetAge();
//positive
years = d1.DiffYears(d2);
Months = d1.DiffMonths(d2);
//negative
years = d2.DiffYears(d1);
Months = d2.DiffMonths(d1);
//Or
Months = Ext.DiffMonths(d1, d2);
years = Ext.DiffYears(d1, d2); 
Waleed A.K.
  • 1,596
  • 13
  • 13
1

Here's a much more concise solution using VB.Net DateDiff for Year, Month, Day only. You can load the DateDiff library in C# as well.

date1 must be <= date2

VB.NET

Dim date1 = Now.AddDays(-2000)
Dim date2 = Now
Dim diffYears = DateDiff(DateInterval.Year, date1, date2) - If(date1.DayOfYear > date2.DayOfYear, 1, 0)
Dim diffMonths = DateDiff(DateInterval.Month, date1, date2) - diffYears * 12 - If(date1.Day > date2.Day, 1, 0)
Dim diffDays = If(date2.Day >= date1.Day, date2.Day - date1.Day, date2.Day + (Date.DaysInMonth(date1.Year, date1.Month) - date1.Day))

C#

DateTime date1 = Now.AddDays(-2000);
DateTime date2 = Now;
int diffYears = DateDiff(DateInterval.Year, date1, date2) - date1.DayOfYear > date2.DayOfYear ? 1 : 0;
int diffMonths = DateDiff(DateInterval.Month, date1, date2) - diffYears * 12 - date1.Day > date2.Day ? 1 : 0;
int diffDays = date2.Day >= date1.Day ? date2.Day - date1.Day : date2.Day + (System.DateTime.DaysInMonth(date1.Year, date1.Month) - date1.Day);
Brent
  • 1,378
  • 2
  • 16
  • 30
1

This is in response to Kirk Woll's answer. I don't have enough reputation points to reply to a comment yet...

I liked Kirk's solution and was going to shamelessly rip it off and use it in my code, but when I looked through it I realized it's way too complicated. Unnecessary switching and looping, and a public constructor that is pointless to use.

Here's my rewrite:

public class DateTimeSpan {
    private DateTime _date1;
    private DateTime _date2;
    private int _years;
    private int _months;
    private int _days;
    private int _hours;
    private int _minutes;
    private int _seconds;
    private int _milliseconds;

    public int Years { get { return _years; } }
    public int Months { get { return _months; } }
    public int Days { get { return _days; } }
    public int Hours { get { return _hours; } }
    public int Minutes { get { return _minutes; } }
    public int Seconds { get { return _seconds; } }
    public int Milliseconds { get { return _milliseconds; } }

    public DateTimeSpan(DateTime date1, DateTime date2) {
        _date1 = (date1 > date2) ? date1 : date2;
        _date2 = (date2 < date1) ? date2 : date1;

        _years = _date1.Year - _date2.Year;
        _months = (_years * 12) + _date1.Month - _date2.Month;
        TimeSpan t = (_date2 - _date1);
        _days = t.Days;
        _hours = t.Hours;
        _minutes = t.Minutes;
        _seconds = t.Seconds;
        _milliseconds = t.Milliseconds;

    }

    public static DateTimeSpan CompareDates(DateTime date1, DateTime date2) {
        return new DateTimeSpan(date1, date2);
    }
}

Usage1, pretty much the same:

void Main()
{
    DateTime compareTo = DateTime.Parse("8/13/2010 8:33:21 AM");
    DateTime now = DateTime.Parse("2/9/2012 10:10:11 AM");
    var dateSpan = new DateTimeSpan(compareTo, now);
    Console.WriteLine("Years: " + dateSpan.Years);
    Console.WriteLine("Months: " + dateSpan.Months);
    Console.WriteLine("Days: " + dateSpan.Days);
    Console.WriteLine("Hours: " + dateSpan.Hours);
    Console.WriteLine("Minutes: " + dateSpan.Minutes);
    Console.WriteLine("Seconds: " + dateSpan.Seconds);
    Console.WriteLine("Milliseconds: " + dateSpan.Milliseconds);
}

Usage2, similar:

void Main()
{
    DateTime compareTo = DateTime.Parse("8/13/2010 8:33:21 AM");
    DateTime now = DateTime.Parse("2/9/2012 10:10:11 AM");
    Console.WriteLine("Years: " + DateTimeSpan.CompareDates(compareTo, now).Years);
    Console.WriteLine("Months: " + DateTimeSpan.CompareDates(compareTo, now).Months);
    Console.WriteLine("Days: " + DateTimeSpan.CompareDates(compareTo, now).Days);
    Console.WriteLine("Hours: " + DateTimeSpan.CompareDates(compareTo, now).Hours);
    Console.WriteLine("Minutes: " + DateTimeSpan.CompareDates(compareTo, now).Minutes);
    Console.WriteLine("Seconds: " + DateTimeSpan.CompareDates(compareTo, now).Seconds);
    Console.WriteLine("Milliseconds: " + DateTimeSpan.CompareDates(compareTo, now).Milliseconds);
}
John A
  • 75
  • 4
1

In my case it is required to calculate the complete month from the start date to the day prior to this day in the next month or from start to end of month.


Ex: from 1/1/2018 to 31/1/2018 is a complete month
Ex2: from 5/1/2018 to 4/2/2018 is a complete month

so based on this here is my solution:

public static DateTime GetMonthEnd(DateTime StartDate, int MonthsCount = 1)
{
    return StartDate.AddMonths(MonthsCount).AddDays(-1);
}
public static Tuple<int, int> CalcPeriod(DateTime StartDate, DateTime EndDate)
{
    int MonthsCount = 0;
    Tuple<int, int> Period;
    while (true)
    {
        if (GetMonthEnd(StartDate) > EndDate)
            break;
        else
        {
            MonthsCount += 1;
            StartDate = StartDate.AddMonths(1);
        }
    }
    int RemainingDays = (EndDate - StartDate).Days + 1;
    Period = new Tuple<int, int>(MonthsCount, RemainingDays);
    return Period;
}

Usage:

Tuple<int, int> Period = CalcPeriod(FromDate, ToDate);

Note: in my case it was required to calculate the remaining days after the complete months so if it's not your case you could ignore the days result or even you could change the method return from tuple to integer.

Ahmed
  • 559
  • 1
  • 5
  • 14
1

Based on the excellent DateTimeSpan work done above, I've normalized the code a bit; this seems to work pretty well:

public class DateTimeSpan
{
  private DateTimeSpan() { }

  private DateTimeSpan(int years, int months, int days, int hours, int minutes, int seconds, int milliseconds)
  {
    Years = years;
    Months = months;
    Days = days;
    Hours = hours;
    Minutes = minutes;
    Seconds = seconds;
    Milliseconds = milliseconds;
  }

  public int Years { get; private set; } = 0;
  public int Months { get; private set; } = 0;
  public int Days { get; private set; } = 0;
  public int Hours { get; private set; } = 0;
  public int Minutes { get; private set; } = 0;
  public int Seconds { get; private set; } = 0;
  public int Milliseconds { get; private set; } = 0;

  public static DateTimeSpan CompareDates(DateTime StartDate, DateTime EndDate)
  {
    if (StartDate.Equals(EndDate)) return new DateTimeSpan();
    DateTimeSpan R = new DateTimeSpan();
    bool Later;
    if (Later = StartDate > EndDate)
    {
      DateTime D = StartDate;
      StartDate = EndDate;
      EndDate = D;
    }

    // Calculate Date Stuff
    for (DateTime D = StartDate.AddYears(1); D < EndDate; D = D.AddYears(1), R.Years++) ;
    if (R.Years > 0) StartDate = StartDate.AddYears(R.Years);
    for (DateTime D = StartDate.AddMonths(1); D < EndDate; D = D.AddMonths(1), R.Months++) ;
    if (R.Months > 0) StartDate = StartDate.AddMonths(R.Months);
    for (DateTime D = StartDate.AddDays(1); D < EndDate; D = D.AddDays(1), R.Days++) ;
    if (R.Days > 0) StartDate = StartDate.AddDays(R.Days);

    // Calculate Time Stuff
    TimeSpan T1 = EndDate - StartDate;
    R.Hours = T1.Hours;
    R.Minutes = T1.Minutes;
    R.Seconds = T1.Seconds;
    R.Milliseconds = T1.Milliseconds;

    // Return answer. Negate values if the Start Date was later than the End Date
    if (Later)
      return new DateTimeSpan(-R.Years, -R.Months, -R.Days, -R.Hours, -R.Minutes, -R.Seconds, -R.Milliseconds);
    return R;
  }
}
Dan Sutton
  • 21
  • 3
  • When comparing with `CompareDates(x, y)` where `x={01/02/2019 00:00:00}` and `y={01/05/2020 00:00:00}` then `Months` gives me `2` – Bassie Feb 19 '19 at 11:16
1

Insane method that counts all days, so super precise

helper class :

public class DaysInMonth
{
    public int Days { get; set; }
    public int Month { get; set; }
    public int Year { get; set; }
    public bool Full { get; set; }
}

function:

    public static List<DaysInMonth> MonthsDelta(DateTime start, DateTime end)
    {
        
        var dates = Enumerable.Range(0, 1 + end.Subtract(start).Days)
          .Select(offset => start.AddDays(offset))
          .ToArray();

        DateTime? prev = null;
        int days = 0;

        List < DaysInMonth > list = new List<DaysInMonth>();

        foreach (DateTime date in dates)
        {
            if (prev != null)
            {
                if(date.Month!=prev.GetValueOrDefault().Month)
                {
                    DaysInMonth daysInMonth = new DaysInMonth();
                    daysInMonth.Days = days;
                    daysInMonth.Month = prev.GetValueOrDefault().Month;
                    daysInMonth.Year = prev.GetValueOrDefault().Year;
                    daysInMonth.Full = DateTime.DaysInMonth(daysInMonth.Year, daysInMonth.Month) == daysInMonth.Days;
                    list.Add(daysInMonth);
                    days = 0;
                }
            }
            days++;
            prev = date;
        }

        //------------------ add last
        if (days > 0)
        {
            DaysInMonth daysInMonth = new DaysInMonth();
            daysInMonth.Days = days;
            daysInMonth.Month = prev.GetValueOrDefault().Month;
            daysInMonth.Year = prev.GetValueOrDefault().Year;
            daysInMonth.Full = DateTime.DaysInMonth(daysInMonth.Year, daysInMonth.Month) == daysInMonth.Days;
            list.Add(daysInMonth);
        }

        return list;
    }
phil123456
  • 1,192
  • 2
  • 10
  • 26
1
Public Class ClassDateOperation
    Private prop_DifferenceInDay As Integer
    Private prop_DifferenceInMonth As Integer
    Private prop_DifferenceInYear As Integer


    Public Function DayMonthYearFromTwoDate(ByVal DateStart As Date, ByVal DateEnd As Date) As ClassDateOperation
        Dim differenceInDay As Integer
        Dim differenceInMonth As Integer
        Dim differenceInYear As Integer
        Dim myDate As Date

        DateEnd = DateEnd.AddDays(1)

        differenceInYear = DateEnd.Year - DateStart.Year

        If DateStart.Month <= DateEnd.Month Then
            differenceInMonth = DateEnd.Month - DateStart.Month
        Else
            differenceInYear -= 1
            differenceInMonth = (12 - DateStart.Month) + DateEnd.Month
        End If


        If DateStart.Day <= DateEnd.Day Then
            differenceInDay = DateEnd.Day - DateStart.Day
        Else

            myDate = CDate("01/" & DateStart.AddMonths(1).Month & "/" & DateStart.Year).AddDays(-1)
            If differenceInMonth <> 0 Then
                differenceInMonth -= 1
            Else
                differenceInMonth = 11
                differenceInYear -= 1
            End If

            differenceInDay = myDate.Day - DateStart.Day + DateEnd.Day

        End If

        prop_DifferenceInDay = differenceInDay
        prop_DifferenceInMonth = differenceInMonth
        prop_DifferenceInYear = differenceInYear

        Return Me
    End Function

    Public ReadOnly Property DifferenceInDay() As Integer
        Get
            Return prop_DifferenceInDay
        End Get
    End Property

    Public ReadOnly Property DifferenceInMonth As Integer
        Get
            Return prop_DifferenceInMonth
        End Get
    End Property

    Public ReadOnly Property DifferenceInYear As Integer
        Get
            Return prop_DifferenceInYear
        End Get
    End Property

End Class
Edwin de Koning
  • 14,209
  • 7
  • 56
  • 74
1

Someone must have done it))

The extension method returns the number of full months between the given dates. No matter in what order the dates are received, a natural number will always be returned.No approximate calculations as in the "correct" answer.

    /// <summary>
    /// Returns the difference between dates in months.
    /// </summary>
    /// <param name="current">First considered date.</param>
    /// <param name="another">Second considered date.</param>
    /// <returns>The number of full months between the given dates.</returns>
    public static int DifferenceInMonths(this DateTime current, DateTime another)
    {
        DateTime previous, next;
        if (current > another)
        {
            previous = another;
            next     = current;
        }
        else
        {
            previous = current;
            next     = another;
        }

        return
            (next.Year - previous.Year) * 12     // multiply the difference in years by 12 months
          + next.Month - previous.Month          // add difference in months
          + (previous.Day <= next.Day ? 0 : -1); // if the day of the next date has not reached the day of the previous one, then the last month has not yet ended
    }

But if you still want to get the fractional parts of the months, you just need to add one more term to the return:

+ (next.Day - previous.Day) / DateTime.DaysInMonth(previous.Year, previous.Month)

EgoPingvina
  • 734
  • 1
  • 10
  • 33
0

Expanded Kirks struct with ToString(format) and Duration(long ms)

 public struct DateTimeSpan
{
    private readonly int years;
    private readonly int months;
    private readonly int days;
    private readonly int hours;
    private readonly int minutes;
    private readonly int seconds;
    private readonly int milliseconds;

    public DateTimeSpan(int years, int months, int days, int hours, int minutes, int seconds, int milliseconds)
    {
        this.years = years;
        this.months = months;
        this.days = days;
        this.hours = hours;
        this.minutes = minutes;
        this.seconds = seconds;
        this.milliseconds = milliseconds;
    }

    public int Years { get { return years; } }
    public int Months { get { return months; } }
    public int Days { get { return days; } }
    public int Hours { get { return hours; } }
    public int Minutes { get { return minutes; } }
    public int Seconds { get { return seconds; } }
    public int Milliseconds { get { return milliseconds; } }

    enum Phase { Years, Months, Days, Done }


    public string ToString(string format)
    {
        format = format.Replace("YYYY", Years.ToString());
        format = format.Replace("MM", Months.ToString());
        format = format.Replace("DD", Days.ToString());
        format = format.Replace("hh", Hours.ToString());
        format = format.Replace("mm", Minutes.ToString());
        format = format.Replace("ss", Seconds.ToString());
        format = format.Replace("ms", Milliseconds.ToString());
        return format;
    }


    public static DateTimeSpan Duration(long ms)
    {
        DateTime dt = new DateTime();
        return CompareDates(dt, dt.AddMilliseconds(ms));
    }


    public static DateTimeSpan CompareDates(DateTime date1, DateTime date2)
    {
        if (date2 < date1)
        {
            var sub = date1;
            date1 = date2;
            date2 = sub;
        }

        DateTime current = date1;
        int years = 0;
        int months = 0;
        int days = 0;

        Phase phase = Phase.Years;
        DateTimeSpan span = new DateTimeSpan();

        while (phase != Phase.Done)
        {
            switch (phase)
            {
                case Phase.Years:
                    if (current.AddYears(years + 1) > date2)
                    {
                        phase = Phase.Months;
                        current = current.AddYears(years);
                    }
                    else
                    {
                        years++;
                    }
                    break;
                case Phase.Months:
                    if (current.AddMonths(months + 1) > date2)
                    {
                        phase = Phase.Days;
                        current = current.AddMonths(months);
                    }
                    else
                    {
                        months++;
                    }
                    break;
                case Phase.Days:
                    if (current.AddDays(days + 1) > date2)
                    {
                        current = current.AddDays(days);
                        var timespan = date2 - current;
                        span = new DateTimeSpan(years, months, days, timespan.Hours, timespan.Minutes, timespan.Seconds, timespan.Milliseconds);
                        phase = Phase.Done;
                    }
                    else
                    {
                        days++;
                    }
                    break;
            }
        }

        return span;
    }
}
Ivan
  • 111
  • 1
  • 4
0
  var dt1 = (DateTime.Now.Year * 12) + DateTime.Now.Month;
  var dt2 = (DateTime.Now.AddMonths(-13).Year * 12) + DateTime.Now.AddMonths(-13).Month;
  Console.WriteLine(dt1);
  Console.WriteLine(dt2);
  Console.WriteLine((dt1 - dt2));
Ryan Bennett
  • 3,404
  • 19
  • 32
Paul
  • 1
0

Here's how we approach this:

public static int MonthDiff(DateTime date1, DateTime date2)
{
    if (date1.Month < date2.Month)
    {
        return (date2.Year - date1.Year) * 12 + date2.Month - date1.Month;
    }
    else
    {
        return (date2.Year - date1.Year - 1) * 12 + date2.Month - date1.Month + 12;
    }
}
Konstantin Chernov
  • 1,899
  • 2
  • 21
  • 37
0
int nMonths = 0;
if (FDate.ToDateTime().Year == TDate.ToDateTime().Year)
     nMonths = TDate.ToDateTime().Month - FDate.ToDateTime().Month;                         
else
nMonths = (12 - FDate.Month) + TDate.Month;                          
Thewads
  • 4,953
  • 11
  • 55
  • 71
0
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {

        label3.Text = new DateDifference(Convert.ToDateTime("2018-09-13"), Convert.ToDateTime("2018-11-15")).ToString();
        label2.Text = new DateDifference(Convert.ToDateTime("2018-10-12"), Convert.ToDateTime("2018-11-15")).ToString();

        DateDifference oDateDifference = new DateDifference(Convert.ToDateTime("2018-11-12"));
       label1.Text  =   oDateDifference.ToString();

    }
}




public class DateDifference
{
    public DateTime start { get; set; }
    public DateTime currentDAte { get; set; }
    public DateTime origstart { get; set; }
    public DateTime origCurrentDAte { get; set; }

    int days { get; set; }
    int months { get; set; }
    int years { get; set; }

    public DateDifference(DateTime postedDate, DateTime currentDAte)
    {
        this.start = this.removeTime(postedDate);
        this.currentDAte = this.removeTime(currentDAte);
        this.origstart = postedDate;
        this.origCurrentDAte = currentDAte;

    }

    public DateDifference(DateTime postedDate)
    {
        DateTime currentDate_ = DateTime.Now;
        this.start = this.removeTime(postedDate);
        this.currentDAte = this.removeTime(currentDate_);
        this.origstart = postedDate;
        this.origCurrentDAte = currentDate_;
        if (start > this.currentDAte)
        {
            throw new Exception("Current date is greater than date posted");
        }
        this.compute();
    }

    void compute()
    {
        while (this.start.Year <= this.currentDAte.Year)
        {
            if (this.start.Year <= this.currentDAte.Year && (this.start.AddMonths(1) <= this.currentDAte))
            {
                ++this.months;
                this.start = this.start.AddMonths(1);
            }

            if ((this.start.Year == this.currentDAte.Year) && (this.start >= this.currentDAte.AddMonths(-1) && this.start <= this.currentDAte))
            {
                break;
            }
        }

        while (this.start.DayOfYear < this.currentDAte.DayOfYear)
        {
            ++this.days;
            this.start = start.AddDays(1);
        }

        if (this.months > 11)
        {
            while (this.months > 11)
            {
                ++this.years;
                this.months = months - 12;
            }
        }

    }


    public override string ToString()
    {
        if (this.start > this.currentDAte)
        {
            throw new Exception("Current date is greater than date posted");
        }
        String ret = this.ComposeTostring();
        this.reset();
        return ret;
    }

    private String ComposeTostring()
    {
        this.compute();
        if (this.years > 0)
        {
            if (this.months > 0)
            {
                if (this.days > 0)
                {
                    return String.Format("{0} year{1}, {2} month{3} && {4} Day{5} ago", this.years, plural(this.years), this.months, plural(this.months), this.days, plural(this.days));
                }
                return String.Format("{0} year{1}, {2} month{3} ago", this.years, plural(this.years), this.months, plural(this.months));
            }
            else
            {
                if (this.days > 0)
                {
                    return String.Format("{0} year{1},{2} day{3} ago", this.years, plural(this.years), this.days, plural(this.days));
                }

                return String.Format("{0} year{1} ago", this.years, plural(this.years));

            }
        }

        if (this.months > 0)
        {
            if (this.days > 0)
            {
                return String.Format("{0} month{1}, {2} day{3} ago", this.months, plural(this.months), this.days, plural(this.days));
            }
            else
            {
                return String.Format("{0} month{1} ago", this.months, plural(this.months));
            }
        }

        if ((this.origCurrentDAte - this.origstart).Days > 0)
        {
            int daysDiff = (this.origCurrentDAte - this.origstart).Days;
            this.origstart = this.origstart.AddDays(daysDiff);
            int HoursDiff = (this.origCurrentDAte - this.origstart).Hours;
            return String.Format("{0} day{1}, {2} hour{3} ago", daysDiff, plural(daysDiff), HoursDiff, plural(HoursDiff));

        }
        else if ((this.origCurrentDAte - this.origstart).Hours > 0)
        {
            int HoursDiff = (this.origCurrentDAte - this.origstart).Hours;
            this.origstart = this.origstart.AddHours(HoursDiff);
            int MinDiff = (this.origCurrentDAte - this.origstart).Minutes;
            return String.Format("{0} hour{1}, {2} minute{3} ago", HoursDiff, plural(HoursDiff), MinDiff, plural(MinDiff));
        }
        else if ((this.origCurrentDAte - this.origstart).Minutes > 0)
        {

            int MinDiff = (this.origCurrentDAte - this.origstart).Minutes;
            this.origstart = this.origstart.AddMinutes(MinDiff);
            int SecDiff = (this.origCurrentDAte - this.origstart).Seconds;
            return String.Format("{0} minute{1}, {2} second{3} ago", MinDiff, plural(MinDiff), SecDiff, plural(SecDiff));
        }
        else if ((this.origCurrentDAte - this.origstart).Seconds > 0)
        {
            int sec = (this.origCurrentDAte - this.origstart).Seconds;
            return String.Format("{0} second{1}", sec, plural(sec));
        }

        return "";
    }

    String plural(int val)
    {
        return (val > 1 ? "s" : String.Empty);
    }

    DateTime removeTime(DateTime dtime)
    {
        dtime = dtime.AddHours(-dtime.Hour);
        dtime = dtime.AddMinutes(-dtime.Minute);
        dtime = dtime.AddSeconds(-dtime.Second);
        return dtime;
    }

    public void reset()
    {

        this.days = 0;
        this.months = 0;
        this.years = 0;
        this.start = DateTime.MinValue;
        this.currentDAte = DateTime.MinValue;
        this.origstart = DateTime.MinValue;
        this.origCurrentDAte = DateTime.MinValue;
    }
}
0

Simple and fast solution to count total months between 2 dates. If you want to get only different months, not counting the one that is in From date - just remove +1 from code.

public static int GetTotalMonths(DateTime From, DateTime Till)
        {
            int MonthDiff = 0;

            for (int i = 0; i < 12; i++)
            {
                if (From.AddMonths(i).Month == Till.Month)
                {
                    MonthDiff = i + 1;
                    break;
                }
            }

            return MonthDiff;
        }
Tommix
  • 443
  • 4
  • 15
0

It seems that the DateTimeSpan solution pleases a lot of people. I don't know. Let's consider the:

BeginDate = 1972/2/29 EndDate = 1972/4/28.

The DateTimeSpan based answer is:

1 year(s), 2 month(s) and 0 day(s)

I implemented a method and based on that the answer is:

1 year(s), 1 month(s) and 28 day(s)

Clearly there are not 2 full months there. I would say that because we are at the end of the month of the begin date what's left is actually the full month of March plus the number of days passed in the month of the end date (April), so 1 month and 28 days.

If you read so far and you are intrigued I posted the method below. I am explaining in the comments the assumptions I make because how many months, the concept of months is such a moving target. Test it many times and see if the answers make sense. I usually choose test dates in adjacent years and once I verify an answer I move a day or two back and forth. So far it looks good, I'm sure you'll find some bugs :D. The code might look a bit rough but I hope it is clear enough:

static void Main(string[] args) {
        DateTime EndDate = new DateTime(1973, 4, 28);
        DateTime BeginDate = new DateTime(1972, 2, 29);
        int years, months, days;
        GetYearsMonthsDays(EndDate, BeginDate, out years, out months, out days);
        Console.WriteLine($"{years} year(s), {months} month(s) and {days} day(s)");
    }

    /// <summary>
    /// Calculates how many years, months and days are between two dates.
    /// </summary>
    /// <remarks>
    /// The fundamental idea here is that most of the time all of us agree
    /// that a month has passed today since the same day of the previous month.
    /// A particular case is when both days are the last days of their respective months 
    /// when again we can say one month has passed.
    /// In the following cases the idea of a month is a moving target.
    /// - When only the beginning date is the last day of the month then we're left just with 
    /// a number of days from the next month equal to the day of the month that end date represent
    /// - When only the end date is the last day of its respective month we clearly have a 
    /// whole month plus a few days after the the day of the beginning date until the end of its
    /// respective months
    /// In all the other cases we'll check
    /// - beginingDay > endDay -> less then a month just daysToEndofBeginingMonth + dayofTheEndMonth
    /// - beginingDay < endDay -> full month + (endDay - beginingDay)
    /// - beginingDay == endDay -> one full month 0 days
    /// 
    /// </remarks>
    /// 
    private static void GetYearsMonthsDays(DateTime EndDate, DateTime BeginDate, out int years, out int months, out int days ) {
        var beginMonthDays = DateTime.DaysInMonth(BeginDate.Year, BeginDate.Month);
        var endMonthDays = DateTime.DaysInMonth(EndDate.Year, EndDate.Month);
        // get the full years
        years = EndDate.Year - BeginDate.Year - 1;
        // how many full months in the first year
        var firstYearMonths = 12 - BeginDate.Month;
        // how many full months in the last year
        var endYearMonths = EndDate.Month - 1;
        // full months
        months = firstYearMonths + endYearMonths;           
        days = 0;
        // Particular end of month cases
        if(beginMonthDays == BeginDate.Day && endMonthDays == EndDate.Day) {
            months++;
        }
        else if(beginMonthDays == BeginDate.Day) {
            days += EndDate.Day;
        }
        else if(endMonthDays == EndDate.Day) {
            days += beginMonthDays - BeginDate.Day;
        }
        // For all the other cases
        else if(EndDate.Day > BeginDate.Day) {
            months++;
            days += EndDate.Day - BeginDate.Day;
        }
        else if(EndDate.Day < BeginDate.Day) {                
            days += beginMonthDays - BeginDate.Day;
            days += EndDate.Day;
        }
        else {
            months++;
        }
        if(months >= 12) {
            years++;
            months = months - 12;
        }
    }
Mircea Ion
  • 658
  • 5
  • 20
0

Apart from all given answers I find this piece of code very straightforward. AS DateTime.MinValue is 1/1/1, we have to subtract 1 from month, years and days.

var timespan = endDate.Subtract(startDate);
var tempdate = DateTime.MinValue + timespan;

var totalMonths = (tempdate.Year - 1) * 12 + tempdate.Month - 1;

var totalDays = tempdate.Day - 1;
if (totalDays > 0)
{
    totalMonths = totalMonths + 1;
}
Gambitier
  • 504
  • 6
  • 18
0

In case you just care about the month and year and want to touch both dates (for example you want to go thru JAN/2021 to AGO/2022) you can use this:

int numberOfMonths= (Year2 > Year1 ? ( Year2 - Year1 - 1) * 12 + (12 - Month1) + Month2 + 1 : Month2 - Month1 + 1); 

Example:

Year1/Month1: 2021/10   
Year2/Month2: 2022/08   
numberOfMonths = 11;

Or same year:

Year1/Month1: 2021/10   
Year2/Month2: 2021/12   
numberOfMonths = 3;

If you just want to touch one of them remove both + 1.

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Miguel
  • 222
  • 1
  • 4
  • I use a variation of this. Get the months for 2 dates and remove a month if the day is less than the day to check. int ageInMonths = (DateTime.Today.Year * 12) + DateTime.Today.Month - ((dob.Year * 12) + dob.Month) - (DateTime.Today.Day < dob.Day ? 1 : 0); I was expecting to find build in function like TotalDays – Mal Aug 29 '23 at 07:14
-1

To be able to calculate the difference between 2 dates in months is a perfectly logical thing to do, and is needed in many business applications. The several coders here who have provided comments such as - what's the difference in months between "May 1,2010" and "June 16,2010, what's the difference in months between 31 December 2010 and 1 Jan 2011? -- have failed to understand the very basics of business applications.

Here is the answer to the above 2 comments - The number of months between 1-may-2010 and 16-jun-2010 is 1 month, the number of months between 31-dec-2010 and 1-jan-2011 is 0. It would be very foolish to calculate them as 1.5 months and 1 second, as the coders above have suggested.

People who have worked on credit card, mortgage processing, tax processing, rent processing, monthly interest calculations and a vast variety of other business solutions would agree.

Problem is that such a function is not included in C# or VB.NET for that matter. Datediff only takes into account years or the month component, so is actually useless.

Here are some real-life examples of where you need to and correctly can calculate months:

You lived in a short-term rental from 18-feb to 23-aug. How many months did you stay there? The answer is a simple - 6 months

You have a bank acount where interest is calculated and paid at the end of every month. You deposit money on 10-jun and take it out 29-oct (same year). How many months do you get interest for? Very simple answer- 4 months (again the extra days do not matter)

In business applications, most of the time, when you need to calculate months, it is because you need to know 'full' months based on how humans calculate time; not based on some abstract/irrelevant thoughts.

Tom
  • 23
  • 1
  • 6
    This is one of the reasons why accounting is not mathematics. In accounting the result depends on the way you calculate it.. I know your points and I know the "common business view" on this, but this explanation is plainly wrong. Between 2012.11.30 and 2012.12.01 there is either 0, or 1/30, or 1/31, or 1 or 2 months, **depending on what did you ask for**. Were the dates exlusive or inclusive? Did you ask for number of months crossed, touched, or passed? Did you want round-up, round-down, or exact? – quetzalcoatl Apr 25 '13 at 15:18
  • 3
    Now explain it to a business guy or an accountant and they'll give you a puzzled look. It's always "so obvious to them that they of course meant X and Y and Z, how could you thought differently?" Now get several business-people and try to get them to agree on the topic. Accountants are more likely to agree, because at some point they will use maths to check with what options they might accidentially sum up the same period twice, etc. Even your examples of calculations are disputable and region-dependent, or plainly invalid as they assume extra business rules like ignoring extra days. – quetzalcoatl Apr 25 '13 at 15:25
  • 2
    -1 You are assuming that all software is a "business application". The purpose of the code in question is not mentioned. You also assume all "business applications" have the same rules, which is definitely not true. – Jesse Webb Mar 28 '14 at 22:32
-1

My problem was solved with this solution:

static void Main(string[] args)
        {
            var date1 = new DateTime(2018, 12, 05);
            var date2 = new DateTime(2019, 03, 01);

            int CountNumberOfMonths() => (date2.Month - date1.Month) + 12 * (date2.Year - date1.Year);

            var numberOfMonths = CountNumberOfMonths();

            Console.WriteLine("Number of months between {0} and {1}: {2} months.", date1.ToString(), date2.ToString(), numberOfMonths.ToString());

            Console.ReadKey();

            //
            // *** Console Output:
            // Number of months between 05/12/2018 00:00:00 and 01/03/2019 00:00:00: 3 months.
            //

        }
Eduardo Pelais
  • 1,627
  • 15
  • 21
-2

LINQ Solution,

DateTime ToDate = DateTime.Today;
DateTime FromDate = ToDate.Date.AddYears(-1).AddDays(1);

int monthCount = Enumerable.Range(0, 1 + ToDate.Subtract(FromDate).Days)
                    .Select(x => FromDate.AddDays(x))
                    .ToList<DateTime>()
                    .GroupBy(z => new { z.Year, z.Month })
                    .Count();
-2

Simple fix. Works 100%

        var exactmonth = (date1.Year - date2.Year) * 12 + date1.Month - 
        date2.Month +  (date1.Day >= date2.Day ? 0 : -1);
        Console.WriteLine(exactmonth);
  • 2
    This is an exact copy of DrunkCoder's code from this comment 5 years ago. https://stackoverflow.com/questions/4638993/difference-in-months-between-two-dates#comment18480449_4639057 – JeffC Jun 01 '18 at 13:47