Search code examples
javalocaldatedatejava.util.calendar

Is there any different behavior between Calendar#add(Calendar.MONTH, months) and LocalDate#plusMonth(months)?


I'm working on some legacy code where java.util.Calendar is used for date related calculations (basically adding months). Now I want to replace java.util.Calendar by java.time.LocalDate in the code but I don't want to change the behavior. So, I've been looking for a source which clarifies that they yield same result for the same calculation for any case but I can't find any.

Especially I want to know if is there a date that makes them yield a different result between:

Calendar#add(Calendar.MONTH, months)

and

LocalDate#plusMonth(months)

I've tested some corner cases (e.g. a leap year related dates) and they seem to yield the same result but I can't be 100% sure with that. Isn't there any official information about that or some known difference between them?


Solution

  • TL;DR

    If:

    • You are sure that your Calendar is really a GregorianCalendar (by far the most commonly used subclass), and…
    • Your dates don’t go more than 100 years back, then…

    …you can safely use LocalDate#plusMonth(months) instead of Calendar#add(Calendar.MONTH, months).

    Details

    Congratulations on the decision to migrate from the old and poorly designed Calendar class to LocalDate from java.time, the modern Java date and time API. This will be an improvement to your code base.

    You are correct, the methods you mention are used for the same purpose and generally work the same. So when you migrate from Calendar to java.time, if you find that LocalDate is the right new class to use, then you will use LocalDate#plusMonth(months) where you used Calendar#add(Calendar.MONTH, months) before.

    Differences include:

    • The Calendar class is an abstract superclass for classes representing dates (and times) in many different calendar systems (Gregorian, Buddhist and more), a LocalDate is always in the proleptic Gregorian calendar. Since each calendar system has its own definition of what a month is, adding a number of months in a calendar other than the Gregorian calendar can give quite different results from what LocalDate.plusMonths gives you.
    • If your dates go back in history it will also make a minor difference that LocalDate uses the proleptic Gregorian calendar. This means that it doesn’t use the Julian calendar for dates where it was in use centuries ago.
    • While Calendar.add modifies the Calendar object that you call it on, LocalDate.plusMonths returns a new LocalDate object with the new date.
    • While for going backward in the calendar you need to pass a negative number of months to Calendar::add, LocalDate has a convenient minusMonths method that you will typically want to use instead of plusMonths (both work, though).
    • The range of dates that each class can represent is different. I don’t readily remember the minimum and maximum date for each. On Calendar/GregorianCalendar, see their various methods such as getGreatestMinimum​ & getLeastMaximum​. For LocalDate, see the constants: MAX & MIN.