-2

For a span of time running from one date to another date, how to get the number of calendar months containing one or more days of my span?

So for example:

  • 2016-01-23/2016-01-23 = 1 calendar month (January)
  • 2016-01-31/2016-02-01 = 2 calendar months (January, February)
  • 2016-01-23/2016-02-28 = 2 calendar months (January, February)
  • 2016-01-15/2016-03-15 = 3 calendar months (January, February, March)
  • 2016-01-15/2017-03-15 = 15 calendar months (Jan-Dec of 2016 plus January, February, March of 2017)

I do not define a month as “30 days”. I am asking about calendar months, January-December.

Similar to this Question but that asks about PHP/MySQL.

Community
  • 1
  • 1
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • 1
    If the dates are in different years, do you want the number of months to be able to go above 12? Or only the number of distinct month names? – Enwired Nov 15 '16 at 23:22
  • @Enwired I added a last example to show a total number of elapsed months that can go far beyond 12, *not* a count of distinct months in the calendar which would always be 12 or less. – Basil Bourque Nov 15 '16 at 23:27
  • Is this question so commonly asked that you think this example will help others? I doubt it, since you state that "no such question has been clearly posed for Java". So are you just looking for free rep or something? I have no problem with canonical questions, but I don't get the need for this one. – nhouser9 Nov 15 '16 at 23:32
  • @nhouser9 Please cite the policy statement of Stack Overflow that allows only questions that are commonly asked. Secondly, [this Question](http://stackoverflow.com/q/40613603/642706) today asked about counting months. In trying to deduce their intention I realized there are at least three definitions of month, one being complete calendar months elapsed, another is groups of 30 days, and the third being partial calendar months touched. I searched for that third one, found postings for other languages but not Java. If you find a dup, cite it and I'll close this question myself. – Basil Bourque Nov 16 '16 at 01:40
  • @BasilBourque Obviously you can ask a question that isn't commonly asked. But I don't see the need for a **canonical** question which isn't commonly (or ever) asked. Just my opinion. – nhouser9 Nov 16 '16 at 03:11

3 Answers3

3

Calculate the "epoch" month of both dates, then subtract them and add 1.

Using LocalDate like in the other answer, an epochMonth() helper method makes it easy:

private static int monthsTouched(LocalDate fromDate, LocalDate toDate) {
    return epochMonth(toDate) - epochMonth(fromDate) + 1;
}
private static int epochMonth(LocalDate date) {
    return date.getYear() * 12 + date.getMonthValue();
}

Like the results in the question, both dates are inclusive.

Note: Validation skipped for brevity, e.g. what is result if fromDate > toDate?

Test

public static void main(String[] args) {
    test("2016-01-23", "2016-01-23");
    test("2016-01-31", "2016-02-01");
    test("2016-01-23", "2016-02-28");
    test("2016-01-15", "2016-03-15");
    test("2016-01-15", "2017-03-15");
}
private static void test(String fromDate, String toDate) {
    System.out.println(monthsTouched(LocalDate.parse(fromDate), LocalDate.parse(toDate)));
}

Output (matches results from question)

1
2
2
3
15
Andreas
  • 154,647
  • 11
  • 152
  • 247
  • 2
    Good answer. Another solution using internally the same mechanism would be to use two instances of `YearMonth` and to determine the delta in months (plus one). – Meno Hochschild Nov 16 '16 at 11:11
1

Use ChronoField.PROLEPTIC_MONTH, which returns a count of months from year zero:

import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;

long monthsTouched = date2.getLong(PROLEPTIC_MONTH) - date1.getLong(PROLEPTIC_MONTH) + 1;
JodaStephen
  • 60,927
  • 15
  • 95
  • 117
0

Adjust the start and end dates

The key is to adjust your dates.

  • Move the starting date to the first of the month
  • Move the ending date to the first of the following month

We move the ending to the next month after because the Half-Open approach is commonly used when considering spans of time. Half-Open means the beginning is inclusive while the ending is exclusive. So lunch hour runs from 12:00 to 13:00 but does not include the 61st minute of 1 PM. A week runs from Monday to Monday, for seven days not including that second Monday.

So a span running from the first of January to the first of March is two months rather than three because we run up to, but do not include, that last date, the first of March.

java.time

The java.time classes built into Java 8 and later make easier work of this.

For date-only values, without a time-of-day and without a time zone, use the LocalDate class.

LocalDate start = LocalDate.parse( "2016-01-31" );
LocalDate stop = LocalDate.parse( "2016-02-01" );

To adjust, use a TemporalAdjuster. Implementations can be found in the TemporalAdjusters class (note the plural 's'). We need firstDayOfMonth and firstDayOfNextMonth.

LocalDate startAdjusted = start.with( TemporalAdjusters.firstDayOfMonth() );
LocalDate stopAdjusted = stop.with( TemporalAdjusters.firstDayOfNextMonth() );

Now use the ChronoUnit class to calculate elapsed whole months.

long calendarMonthsTouched = ChronoUnit.MONTHS.between( startAdjusted , stopAdjusted );

span: 2016-01-31/2016-02-01

calendarMonthsTouched: 2

See this code live in IdeOne.com.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • 1
    Hmm... Asking and immediately answering your own question. Are you trying to document how to do this. If so, wouldn't the [Documentation](http://stackoverflow.com/documentation) section be better? Isn't that why they created it? – Andreas Nov 15 '16 at 23:19
  • @Andreas Yes, I am answering my own Question as a way to document that knowledge in public so that others including myself can find it later… [as ***explicitly encouraged*** by Stack Overflow](http://stackoverflow.com/help/self-answer). – Basil Bourque Nov 15 '16 at 23:21
  • Sure, but I do think that encouragement predates the new Documentation section. Now, if you asked a question, and got one or more answers, but none of them are exactly right, but you find the real answer from them, answering your own question is definitely a good idea. – Andreas Nov 15 '16 at 23:37
  • 1
    @Andreas That may be your opinion regarding answering one's own Question, but it is *not* the policy of the Stack Overflow management. Have you not noticed the "Answer your own question" checkbox built into the Stack Overflow question submission page?!! If you wish to change the Stack Overflow policy, take it up with them, not me. – Basil Bourque Nov 16 '16 at 01:31