1

When using java.time in Scala I experienced a strange behavior. I want to calculate the number of months between two dates like this:

import java.time._

Period.between(LocalDate.parse("2015-03-31"), LocalDate.parse("2015-04-30")) 
// java.time.Period = P30D
// I would expect java.time.Period = P1M

Period.between(LocalDate.parse("2015-03-31"), LocalDate.parse("2015-05-01"))
// java.time.Period = P1M1D

Is this a bug or do I have got it all wrong?

org.joda.time works as I would expect it:

import org.joda.time.DateTime
import org.joda.time.Months

Months.monthsBetween( new DateTime().withDate(2015, 3, 31), new DateTime().withDate(2015, 4, 30))
//org.joda.time.Months = P1M

When adding months to a java.time.LocalDate it works fine:

java.time.LocalDate.parse("2015-03-31").plusMonths(1)
// java.time.LocalDate = 2015-04-30
4
  • 1
    Does it break a clearly stated specification? Commented Sep 14, 2016 at 11:27
  • @MarkoTopolnik: I don't know if it does but I would expect that a month can have different numbers of days. Commented Sep 14, 2016 at 11:33
  • If it breaks specification, it's a bug; if it doesn't, then it only broke your (reasonable) expectation. Commented Sep 14, 2016 at 11:35
  • I tried with 04-30 to 05-31, then it said 1M1D.. Makes no sense to me.. I have limited experience with the java time API, but from what I've seen, JodaTime is better. A shame really.. Commented Sep 14, 2016 at 11:36

3 Answers 3

5

This is not a bug, and it is behaving like expected (see also JDK-8152384 and JDK-8037392, which were closed as "Not An Issue"). Joda Time and the Java Time API have different behaviour regarding this. Quoting Stephen Colebourne from the previous bug report:

The OP appears to want a rule where the days are calculated based on the original month length, not the one that results once the month-year difference is applied. The OP is not wrong, its just that its not how we choose to make the calculation in java.time.

Indeed, from Period.between:

The period is calculated by removing complete months, then calculating the remaining number of days, adjusting to ensure that both have the same sign. [...] A month is considered to be complete if the end day-of-month is greater than or equal to the start day-of-month.

Between the 31st of March, and the 30th of April, no complete month has elapsed. As such, you have a period containing the number of days between the two dates, which is 30. To have the complete month of April elapsed, you need to add one day to the end date, and make it the 1st of June.

Joda has a different way of calculating the month period. From Months.monthsBetween:

This method calculates by adding months to the start date until the result is past the end date. As such, a period from the end of a "long" month to the end of a "short" month is counted as a whole month.

Joda explicitly takes the variable number of days in a month into account when calculating the number of months between the two dates. Java Time doesn't.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the links. The fact that the results for Period(startDate, endDate) and Period(endDate, startDate) could differ so much is counter intuitive and a bit scary to be honest :|
2

I agree that it is a bit unexpected, but it is the correct result if you take into account the javadoc. From the javadoc

The start date is included, but the end date is not. The period is calculated by removing complete months, then calculating the remaining number of days, adjusting to ensure that both have the same sign. The number of months is then split into years and months based on a 12 month year. A month is considered if the end day-of-month is greater than or equal to the start day-of-month. For example, from 2010-01-15 to 2011-03-18 is one year, two months and three days.

The difference comes from what a "complete month" means. In this case 1st April to 1st May (exclusive) is considered a complete month while 31st March to 30th April (exclusive) is not.

1 Comment

Please link to the Javadoc. I tried to look this up as well, but clearly wasn't looking at the right place.
1

I believe the Period.between is returning P30D in the first example because the second parameter is exclusive. This is according to https://docs.oracle.com/javase/8/docs/api/java/time/Period.html#between-java.time.LocalDate-java.time.LocalDate-

public static Period between(LocalDate startDateInclusive, LocalDate endDateExclusive)

5 Comments

I am aware of this. But why is Period.between(LocalDate.parse("2015-04-30"), LocalDate.parse("2015-05-30")) returning P1M?
@cperriard because that is one month apart. Why would you expect a period which is a day shorter to be the same?
@PeterLawrey because the month of April only has 30 days and because java.time.LocalDate.parse("2015-03-31").plusMonths(1) returns 2015-04-30.
@cperriard it only returns 2015-04-30 as there is not 31 days so it rounds down, now subtract 1 month and you will get 2015-03-30 not 31.
@cperriad it looks like some of the confusion comes from plusMonths. See the algorithm described here docs.oracle.com/javase/8/docs/api/java/time/… The other part of the confusion comes from the definition of a Period and ISO-8601 and what constitutes a 'month' (see here stackoverflow.com/questions/26265751/…)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.