java.time
The java.util
Date-Time API and their formatting API, SimpleDateFormat
are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
Solution using java.time
, the modern API: Since you do not need time and timezone to consider in your requirement, LocalDate
fits best to your requirement.
Demo:
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
public class Main {
public static void main(String[] args) {
// Test
System.out.println(monthsBetween(LocalDate.of(2012, 1, 28), LocalDate.of(2012, 2, 1)));
System.out.println(monthsBetween(LocalDate.of(2012, 2, 27), LocalDate.of(2012, 2, 28)));
System.out.println(monthsBetween(LocalDate.of(2012, 3, 28), LocalDate.of(2012, 7, 1)));
}
static int monthsBetween(final LocalDate fromDate, final LocalDate toDate) {
return Math.toIntExact(
ChronoUnit.MONTHS.between(
fromDate.with(TemporalAdjusters.firstDayOfMonth()),
toDate.with(TemporalAdjusters.firstDayOfMonth())
)
);
}
}
Output:
1
0
4
ONLINE DEMO
In case you need to use java.util.Date
:
For any reason, if you need to do it for the objects of java.util.Date
, you can convert the objects of java.util.Date
to Instant
using Date#toInstant
which can be converted to other java.time
types as required.
Demo:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) throws ParseException {
// Test
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
sdf.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
System.out.println(monthsBetween(sdf.parse("2012-01-28"), sdf.parse("2012-02-01")));
System.out.println(monthsBetween(sdf.parse("2012-02-27"), sdf.parse("2012-02-28")));
System.out.println(monthsBetween(sdf.parse("2012-03-28"), sdf.parse("2012-07-01")));
}
static int monthsBetween(final Date fromDate, final Date toDate) {
ZonedDateTime zdtFrom = fromDate.toInstant().atZone(ZoneOffset.UTC);
ZonedDateTime zdtTo = toDate.toInstant().atZone(ZoneOffset.UTC);
return Math.toIntExact(
ChronoUnit.MONTHS.between(
zdtFrom.with(TemporalAdjusters.firstDayOfMonth()),
zdtTo.with(TemporalAdjusters.firstDayOfMonth())
)
);
}
}
Output:
1
0
4
ONLINE DEMO
Learn more about java.time
, the modern Date-Time API* from Trail: Date Time.
Some important notes:
- The constructor,
java.util.Date(int, int, int)
is deprecated.
java.util.Date
and java.util.Calendar
month is 0-based i.e. January is month#0. However, SimpleDateFormat
considers January as month#1.
- An
int
prefixed with 0 is considered as an octal number which can support digit only in the range of 0-7. You did not encounter a compilation error merely by chance because you have used months only up to 07. Had you used 08 or 09, for example, you would have encountered a compilation error.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.