Count the number of months between one date and another

Asked

Viewed 5,490 times

12

I would like your help to basically count the amount of months between a date and another.

I’ll give you an example but what I want is basically just an accountant.

Ex: quantity of months between 05/02/2018 to date (21/03/2019)

  • if the date is 28/02/2019, whether the result is 1 month or 0?

  • in which case it would be 0

  • Quantity of months between 02/05/2018 to date (03/21/2019). what is the result? 13 or 12

5 answers

18


First we need to understand two important concepts:

  • one date is a specific point of the calendar. Ex: today is 21/03/2019 (March 21, 2019)
  • one duration That’s a lot of time. Ex: I lived in City X for 3 years and 5 months (I didn’t say when I started living, it’s just the amount of time, with no relationship to calendars)

Both may even use the same words (days, months, years etc), but they are different concepts.

One of the answers is getting a duration (the TimeSpan) and using it to create another date (DateTime), which makes no sense: the DateTime represents a date, and the TimeSpan represents a duration. Dot.

Of course the concepts are related: if I have two dates (instances of DateTime) and calculate the difference between them, the result will be the amount of time between those dates (ie a TimeSpan).


Finally, to calculate the difference of months between two dates, will depend on your criterion to consider what is a "month".

The most direct method for calculating the difference between dates is:

DateTime date = new DateTime(2018, 2, 5);
TimeSpan difference = DateTime.Now - date;
Console.WriteLine(difference.TotalDays);

TotalDays returns the difference in days, including decimals (since it takes into account the current time as well). Running now, I got 409.804892438586. If you want, you can use difference.Days, that returns the value as an integer (409).

But 409 days is how many months?

"Ah, just divide by 30, that’s 13 months"

It actually depends. Months have varying sizes, can be 28, 29, 30 or 31 days, and the bill is not always that simple.

For example, between 01/01/2019 and 01/02/2019 there are 31 days. Dividing by 30 and rounding, gives 1 month.

But between 01/02/2019 and 01/03/2019 there are 28 days. Dividing by 30, gives 0,93: if round down, gives zero months. But between February 1 and March 1 the difference is not one month? Then we should round up in this case?

But if it was between 01/01/2019 and 29/01/2019, the difference is also 28 days. Only between January 1st and January 29th it hasn’t been 1 month, so I can’t round up in this case. "Ah, so I just jump up if I’m not in the same month".

Then you see that between 01/01/2019 and 27/02/2019 the difference is 57 days, divided by 30 is 1,9. If you round it up, it’s 2, but between January 1st and February 27th it hasn’t been two months. And now?

Dividing by 30 (or any other arbitrary "mean" value) will always have this problem. This happens because months have varied sizes and the exact amount of months equivalent to days will depend on the dates involved.

Arithmetic of dates is not anything obvious, and is often counter-intuitive. Not even in mathematics, where 10 units are always a dozen, 10 tens are a hundred, etc. With dates, we can have "a dozen that does not have 10 units" (like a month that may not have exactly 30 days, or a year that may have 366 days). So there is no to correct rule for this, it is up to you to choose a way to calculate these differences.


If you want, you can make a calculation considering only the month and year:

static int ajustaMesAno(DateTime d) {
    return d.Year * 12 + d.Month;
}

DateTime date = new DateTime(2018, 2, 5);
int mesesDiff = ajustaMesAno(DateTime.Now) - ajustaMesAno(date);
Console.WriteLine(mesesDiff); 

whereas DateTime.Now returns the date of 03/21/2019, the code above calculates the difference in months between 02/05/2018 and 03/21/2019, and the result is 13.

But what if the starting date is 02/22/2018? You will consider that the difference is 12 months (because only on the day 22/03/2019 will complete the thirteenth month) or 13 months (because it only matters the month, regardless of the day)?

If you want to consider that it is 12 months because only after the day 22/03 completes the month, just include the adjustment:

DateTime inicio = new DateTime(2018, 2, 22);
DateTime fim = new DateTime(2019, 3, 21);
int mesesDiff = ajustaMesAno(fim) - ajustaMesAno(inicio);
if (inicio.Day > fim.Day) {
    mesesDiff--;
}
Console.WriteLine(mesesDiff); // 12

In the example above I used the final date as 21/03/2019 for educational purposes (so the result is 12), but you can exchange for DateTime.Now to use the current date.


Don’t confuse DateTime with TimeSpan

Just to explain why one of the answers suggested is not the right.

First she calculates the difference between the dates and gets the TimeSpan. So far OK:

DateTime inicio = new DateTime(2018, 03, 21);
DateTime fim = new DateTime(2019, 03, 21);
TimeSpan ts = fim.Subtract(inicio); // 365.00:00:00

If you print the TimeSpan, will be shown "365.00:00:00" (a duration of 365 days, zero hours, zero minutes and zero seconds, that is, exact 365 days).

After - and this is the wrong part - it uses the value of Ticks to create another DateTime (added a WriteLine to see the result):

DateTime periodo = new DateTime(ts.Ticks);
Console.WriteLine(periodo); // 1/1/0002 12:00:00 AM

Only that Ticks returns the amount of ticks. According to the documentation, one tick corresponds to a range of 100 nanoseconds. In the above example, the value of Ticks is 315360000000000 (more than 300 trillion ticks, because this is the amount of 100 nanosecond intervals that exist in 365 days).

The builder of DateTime which receives the value of ticks creates a date (not a duration) corresponding to the quantity of ticks since January 1, year 1, at midnight (not the year 2001 nor 1901, is the year 1). In this case, the resulting date above is 1 January of year 2 (as it is the date corresponding to "315360000000000 ticks after 1 January of year 1 at midnight").

This is not a duration (not the difference between the two dates). If you use the properties of this date, you will get the values relative to the date. For example, d.Month returns 1, but by chance the difference between the dates is 1 month? d.Year is 2, the difference is two years? No, because the variable periodo is a DateTime (a date), not a duration.

Finally, the reply still prints the date in this way:

Console.WriteLine("Anos: {0}  Meses: {1}  Dias: {2}", periodo.Year - 1, periodo.Month, periodo.Day);

The result is:

Years: 1 Months: 1 Days: 1

Would it be a "duration" of 1 year, 1 month and 1 day? No, if you fix well the difference between the dates used in the example is exactly 1 year. The result is wrong and this is because the concepts of date and duration have been confused. The date of January 1 of year 2 was created and the values of the day, month and year were printed as if they were durations.

You want the difference between the dates (ie a duration), and this has already been obtained when you created the TimeSpan. In this case there is no reason to create another date (other DateTime) from the value of TimeSpan.

  • 1

    I understand, very good explanation made clear all the difference between Datetime and Timespan and even gave an even more complete explanation that I wanted because I would probably round to 30 even, but I will opt for this adjustment. But there is still a problem that is the same as I had commented above I’m getting the value of the date of a Datetimepicker already tried both to grab the property .Text as the property .Value and both the code -- Datetime date = new Datetime(dtpVigencia.Text); ends up resulting in Argument 1: cannot Convert from 'string' to 'long'

  • @Phpatrick O Value of DateTimePicker is already a DateTime, can do DateTime date = dtpVigencia.Value straightforward.

  • @Phpatrick The builder of DateTime gets a long (a numeric type), so the error message (this does not mean that the value is "too long", but the wrong type). Another thing, I saw that you canceled the answer. If anything was missing, let me know that I add...

7

Try to use it this way.

DateTime inicio = new DateTime(2018, 03, 21);
DateTime fim = new DateTime(2019, 03, 21);

TimeSpan ts = fim.Subtract(inicio);
DateTime periodo = new DateTime(ts.Ticks);

Console.WriteLine("Anos: {0}  Meses: {1}  Dias: {2}", periodo.Year - 1, periodo.Month, periodo.Day);
  • 2

    Your code returns Anos: 1 Meses: 1 Dias: 1 (would be 1 year, 1 month and 1 day? ) but the difference between 21/03/2018 and 21/03/2019 is 1 year only...

6

Use only mathematics. As in the example below:

DateTime inicio = new DateTime(1980, 01, 28);
DateTime fim = new DateTime(2019, 03, 21);

var i = Math.Truncate(fim.Subtract(inicio).Days / (365.25 / 12));

If you want to check if it’s correct, enter on this website and enter the dates.

Reference: Difference in months between two Dates.

  • 1

    Has a corner case: between 01/02/2019 and 01/03/2019 gives zero (but the difference between them is not 1 month?) Debatable, but still, a corner case... :-)

4

Other option:

DateTime data = DateTime.Parse("5 / 02 / 2018");
DateTime hoje = DateTime.Today;

int meses = ((hoje.Year - data.Year) * 12) + hoje.Month - data.Month;

meses += data.Day > hoje.Day ? -1 : 0;

4

    static int GetQtdMoth(DateTime inicio, DateTime fim)
    {
        int years = fim.Year - inicio.Year;
        if (years < 0)
            return 0;
        if (years == 0)
            return fim.Month - inicio.Month;
        int meses = 12 - inicio.Month;
        return (years - 1) * 12 + meses + fim.Month;
    }

Browser other questions tagged

You are not signed in. Login or sign up in order to post.