3

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?

Kohei Nozaki
  • 1,154
  • 1
  • 13
  • 36
  • Have you consulted [the documentation](https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html)? – chrylis -cautiouslyoptimistic- Sep 12 '19 at 07:25
  • Yes but it’s unclear that if they are 100% compatible in terms of date related calculations – Kohei Nozaki Sep 12 '19 at 09:38
  • Just to be clear: You should ***never* be using `Calendar` or `Date`**. Those classes really are that bad — [JSR 310](https://jcp.org/en/jsr/detail?id=310) was adopted for good reason! To interoperate with legacy code not yet updated for *java.time*, you can easily convert back-and-forth. Look to new `to…`/`from…` methods added to the old classes. For `Calendar`, cast to `GregorianCalendar`, then call `toZonedDateTime`. For `java.util.Date`, call `toInstant`. And go the other direction: `GregorianCalendar.from( myZonedDateTime )` and `Date.from( myInstant )`. – Basil Bourque Sep 15 '19 at 18:51

1 Answers1

4

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.
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161