views:

418

answers:

7

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

+14  A: 

You won't be able to get that from a TimeSpan, because a "month" is a variable unit of measure. You'll have to calculate it yourself, and you'll have to figure out how exactly you want it to work.

For example, should dates like July 5, 2009 and August 4, 2009 yield one month or zero months difference? If you say it should yield one, then what about July 31, 2009 and August 1, 2009? Is that a month? Is it simply the difference of the Month values for the dates, or is it more related to an actual span of time? The logic for determining all of these rules is non-trivial, so you'll have to determine your own and implement the appropriate algorithm.

If all you want is simply a difference in the months--completely disregarding the date values--then you can use this:

public static int MonthDifference(this DateTime lValue, DateTime rValue)
{
    return (lValue.Month - rValue.Month) + 12 * (lValue.Year - rValue.Year);
}

Note that this returns a relative difference, meaning that if rValue is greater than lValue, then the return value will be negative. If you want an absolute difference, you can use this:

public static int MonthDifference(this DateTime lValue, DateTime rValue)
{
    return Math.Abs((lValue.Month - rValue.Month) + 12 * (lValue.Year - rValue.Year));
}
Adam Robinson
+4  A: 

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.

Henk Holterman
A: 

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

Rik
+2  A: 

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;
}
Maximilian Mayerl
That's certainly one algoritm, but it could be vastly simplified to `return (dtOther.Month - dtThis.Month) + 12 * (dtOther.Year - dtThis.Year);`
Adam Robinson
Two problems: You are starting from 2 Dates, not a TimeSpan. Second, you calculate between the 1st of both months, that is a very questionable definition. Although it could be right sometimes.
Henk Holterman
@Henk: Yes, of course that's not always right, that's why I said that this is how I would do it, not how anybody should do it. The OP didn't specify how the result should be calculated.@Adam: Wow, I thought way too complicated yet again...that happens all too often to me. Thanks for the comment, you are obviously right, your version is much better. I will use this from now on.
Maximilian Mayerl
@Adam: why don't you submit this as an actual answer?! This is the most compact so far. Very slick.
Dinah
@Dinah: I didn't want to assume that's what you actually wanted. If it is, I have edited my previous answer to include this approach.
Adam Robinson
@Adam: awesome, thank you. Accepted :)
Dinah
+2  A: 

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;
JDunkerley
nice code..Any idea how to get the days also ?
Parhs
A: 

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.

Jesse O'Brien
+1  A: 

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"


Rubens Farias
I don't understand the * 100. Should it be * 12?
Ruffles
you're absolutely right; fixed, ty
Rubens Farias