Months within period with Joda-Time

Goal

To calculate the months within a given period

Description

This recipe tells you how to use Joda-Time to get the months that exist between two different dates (start and end).

How to

Implement a static function that gets two dates: start and end and returns a List of YearMonth objects. This function does nor care if there is exactly 1 month between the two dates. Instead, all we want is to return the months that goes from the first date to the last date. So, for instance, if the first date is 2014-01-31 and the second date is 2014-02-01, the result we want is 2014-01 (January of 2014) and 2014-02 (February of 2014).

...
public static List getYearMonths(final Date startDate, final Date endDate) {
  final List result = new ArrayList();

  final DateMidnight start = new DateMidnight(startDate.getTime()).withDayOfMonth(1);
  final DateMidnight end = new DateMidnight(endDate.getTime()).
                           plusMonths(1).withDayOfMonth(1).minusDays(1);
  if (start.isAfter(end)) {
    throw new IllegalArgumentException("start must be before end");
  }

  DateMidnight current = new DateMidnight(start.getMillis());
  do {
    final YearMonth yearMonth = new YearMonth(current.getYear(), current.getMonthOfYear());
    result.add(yearMonth);
    current = current.plusMonths(1);
  } while (current.isBefore(end));

  return result;
}
...

The previous function uses the first day of the first date’s month and the last day of the second date’s month to make sure the calculations will return what we want (notice that, in case you are using a specific timezone, then your new DateMidnight() dates should take that into account as well, through calling Joda’s DateTimeZone.forID()). The following test checks the validation of the previous method:

...
@Test
public void testGetYearMonths() throws ParseException {
  final SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");
  Assert.assertEquals(
         Arrays. asList(new YearMonth(2010, 1)), 
         DateUtil.getYearMonths(f.parse("2010-01-01"), f.parse("2010-01-31")));

  Assert.assertEquals(
         Arrays. asList(new YearMonth(2010, 1), new YearMonth(2010, 2)),
         DateUtil.getYearMonths(f.parse("2010-01-01"), f.parse("2010-02-01")));

  Assert.assertEquals(
         Arrays. asList(new YearMonth(2010, 1)), 
         DateUtil.getYearMonths(f.parse("2010-01-01"), f.parse("2010-01-01")));

  Assert.assertEquals(
         Arrays. asList(new YearMonth(2010, 1)),
         DateUtil.getYearMonths(f.parse("2010-01-01"), f.parse("2010-01-02")));
	
  Assert.assertEquals(
         Arrays. asList(new YearMonth(2010, 1), new YearMonth(2010, 2)),
         DateUtil.getYearMonths(f.parse("2010-01-31"), f.parse("2010-02-01")));

  Assert.assertEquals(
         Arrays. asList(new YearMonth(2010, 1), new YearMonth(2010, 2)),
         DateUtil.getYearMonths(f.parse("2010-01-31"), f.parse("2010-02-28")));

  Assert.assertEquals(
         Arrays. asList(new YearMonth(2010, 1), new YearMonth(2010, 2), 
                                   new YearMonth(2010, 3)),
         DateUtil.getYearMonths(f.parse("2010-01-31"), f.parse("2010-03-01")));

  try {
    DateUtil.getYearMonths(f.parse("2010-01-01"), f.parse("2009-01-02"));
    Assert.fail("It should throw an exception");
  } catch (final IllegalArgumentException e) {
    Assert.assertTrue(true);
  } catch (final Exception e) {
    Assert.fail("It should throw an IllegalArgumentException");	
  }
}
...

Explanations

The trick of this recipe is to “move” the start date to the first day of that date’s month and “move” the end date to the last day of that date’s month

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s