Reputation: 347
I want the number of months between 2 java.util.Date
's, without the days in month counted.
So I just want the year and month in the comparison.
monthsBetween(new Date(2012,01,28), new Date(2012,02,01)) ---> 1
monthsBetween(new Date(2012,02,27), new Date(2012,02,28)) ---> 0
monthsBetween(new Date(2012,03,28), new Date(2012,07,01)) ---> 4
I have tried this (returns 0, expected 1), using Joda-time:
private static int monthsBetween(final Date fromDate, final Date toDate) {
DateTime date1 = new DateTime().withDate(2012, 1, 20);
DateTime date2 = new DateTime().withDate(2012, 2, 13);
PeriodType monthDay = PeriodType.yearDayTime().withDaysRemoved();
Period difference = new Period(date1, date2, monthDay);
int months = difference.getMonths();
return months;
}
And also this (same results), using Joda-time:
private static int monthsBetween(final Date fromDate, final Date toDate) {
return Months.monthsBetween(new DateTime(fromDate), new DateTime(toDate).getMonths();
}
How can I accomplish this?
Upvotes: 6
Views: 38116
Reputation: 78965
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
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
Learn more about java.time
, the modern Date-Time API* from Trail: Date Time.
Some important notes:
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.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.
Upvotes: 3
Reputation: 942
this will fetch you difference in months
(endCal.get(Calendar.YEAR)*12+endCal.get(Calendar.MONTH))-(startCal.get(Calendar.YEAR)*12+startCal.get(Calendar.MONTH))
Upvotes: 0
Reputation: 587
It will work for leap year also
public static int getNumberOfMonths(Date fromDate, Date toDate) {
int monthCount = 0;
Calendar cal = Calendar.getInstance();
cal.setTime(fromDate);
int c1date = cal.get(Calendar.DATE);
int c1month = cal.get(Calendar.MONTH);
int c1year = cal.get(Calendar.YEAR);
cal.setTime(toDate);
int c2date = cal.get(Calendar.DATE);
int c2month = cal.get(Calendar.MONTH);
int c2year = cal.get(Calendar.YEAR);
System.out.println(" c1date:"+c1date+" month:"+c1month+" year:"+c1year);
System.out.println(" c2date:"+c2date+" month:"+c2month+" year:"+c2year);
GregorianCalendar grCal = new GregorianCalendar();
boolean isLeapYear1 = grCal.isLeapYear(c1year);
boolean isLeapYear2 = grCal.isLeapYear(c2year);
monthCount = ((c2year - c1year) * 12) + (c2month - c1month);
if(isLeapYear2 && c2month == 1 && c2date == 29){
monthCount = monthCount+ ((c1date == 28)?0:1);
}else if(isLeapYear1 && c1month == 1 && c1date == 29){
monthCount = monthCount+ ((c2date == 28)?0:1);
}else{
monthCount = monthCount+ ((c2date >= c1date)?0:1);
}
return monthCount;
}
Upvotes: 0
Reputation: 1499770
You're asking for the number of whole months - which isn't the same as saying "ignore the day of month part".
To start with, I'd suggest using LocalDate
instead of DateTime
for the computations. Ideally, don't use java.util.Date
at all, and take your input as LocalDate
to start with (e.g. by parsing text straight to that, or wherever your data comes from.) Set the day of month to 1 in both dates, and then take the difference in months:
private static int monthsBetweenIgnoreDays(LocalDate start, LocalDate end) {
start = start.withDayOfMonth(1);
end = end.withDayOfMonth(1);
return Months.monthsBetween(start, end).getMonths();
}
Upvotes: 10
Reputation: 563
I would just get the year and month fields of Calendar instances, convert the years to months and get differences.
private static int monthsBetween(final Date s1, final Date s2) {
final Calendar d1 = Calendar.getInstance();
d1.setTime(s1);
final Calendar d2 = Calendar.getInstance();
d2.setTime(s2);
int diff = (d2.get(Calendar.YEAR) - d1.get(Calendar.YEAR)) * 12 + d2.get(Calendar.MONTH) - d1.get(Calendar.MONTH);
return diff;
}
Upvotes: 1
Reputation: 31
If you have the years and months in an int value:
months = (year2-year1)*12 + month2 - month1;
Being 12 months in a year.
Upvotes: 3
Reputation: 279
/**
* Gets number of months between two dates.
* <p>Months are calculated as following:</p>
* <p>After calculating number of months from years and months from two dates,
* if there are still any extra days, it will be considered as one more month.
* For ex, Months between 2012-01-01 and 2013-02-06 will be 14 as
* Total Months = Months from year difference are 12 + Difference between months in dates is 1
* + one month since day 06 in enddate is greater than day 01 in startDate.
* </p>
* @param startDate
* @param endDate
* @return
*/
public static int getMonthsBetweenDates(Date startDate, Date endDate)
{
if(startDate.getTime() > endDate.getTime())
{
Date temp = startDate;
startDate = endDate;
endDate = temp;
}
Calendar startCalendar = Calendar.getInstance();
startCalendar.setTime(startDate);
Calendar endCalendar = Calendar.getInstance();
endCalendar.setTime(endDate);
int yearDiff = endCalendar.get(Calendar.YEAR)- startCalendar.get(Calendar.YEAR);
int monthsBetween = endCalendar.get(Calendar.MONTH)-startCalendar.get(Calendar.MONTH) +12*yearDiff;
if(endCalendar.get(Calendar.DAY_OF_MONTH) >= startCalendar.get(Calendar.DAY_OF_MONTH))
monthsBetween = monthsBetween + 1;
return monthsBetween;
}
Upvotes: 1
Reputation: 135992
This version is JDK Calendar based:
public static void main(String[] args) throws Exception {
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");
Date d1 = f.parse("2012-01-01");
Date d2 = f.parse("2012-02-02");
int n = differenceInMonths(d1, d2);
System.out.println(n);
}
private static int differenceInMonths(Date d1, Date d2) {
Calendar c1 = Calendar.getInstance();
c1.setTime(d1);
Calendar c2 = Calendar.getInstance();
c2.setTime(d2);
int diff = 0;
if (c2.after(c1)) {
while (c2.after(c1)) {
c1.add(Calendar.MONTH, 1);
if (c2.after(c1)) {
diff++;
}
}
} else if (c2.before(c1)) {
while (c2.before(c1)) {
c1.add(Calendar.MONTH, -1);
if (c1.before(c2)) {
diff--;
}
}
}
return diff;
}
Upvotes: 5