Java. Dates difference in months by GregorianCalendar not work correct

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP



Java. Dates difference in months by GregorianCalendar not work correct



java 6.0



Suppose today is: 8 Aug 2018



Here method return difference of 2 dates in months .


public static Integer getDiffMonths(Date dateFrom, Date dateTo)
Calendar cal1 = new GregorianCalendar();
cal1.setTime(dateFrom);
Calendar cal2 = new GregorianCalendar();
cal2.setTime(dateTo);
return differenceInMonths(cal1, cal2);


private static Integer differenceInMonths(Calendar dateFrom, Calendar dateTo)
int month1 = dateFrom.get(Calendar.YEAR) * 12 + dateFrom.get(Calendar.MONTH);
int month2 = dateTo.get(Calendar.YEAR) * 12 + dateTo.get(Calendar.MONTH);
int diff = month2 - month1;
return diff;



Here results of 4 test cases:



1.


currentDate = Aug 08 2018
DateTo = Aug 13 2018
diffMonth = 0



2.


currentDate = Aug 08 2018
DateTo = Oct 14 2018
diffMonth = 2



3.


currentDate = Aug 08 2018
DateTo = Jan 03 2019
diffMonth = 5



Error: difference must be 4 months



4.


currentDate = Aug 08 2018
DateTo = Aug 03 2019
diffMonth = 12



Error: difference must be 11 months



As you can see test cases #1 and #2 are correct, but test cases #3 and #4 are incorrect.



Why?





OK, you might get extended support for Java 6 until december 2018.
– user9455968
Aug 8 at 13:54





This is a source code of old project, that was write on java 6.0
– Alexei
Aug 8 at 13:54





FYI, the troublesome old date-time classes such as java.util.Date, java.util.Calendar, and java.text.SimpleDateFormat are now legacy, supplanted by the java.time classes. Much of the java.time functionality is back-ported to Java 6 & Java 7 in the ThreeTen-Backport project. Further adapted for earlier Android in the ThreeTenABP project. See How to use ThreeTenABP….
– Basil Bourque
Aug 11 at 18:49



java.util.Date


java.util.Calendar


java.text.SimpleDateFormat




4 Answers
4



You completely ignore the day when calculating the difference. So Jan 1 is the same as Jan 31.



The first two cases happen to have greater day in the second date, the last two a smaller day. But this information is ignored.





But why is the day relevant, if he fetches the year and month explicitly? So January 1st is Month = 0, but January 31st is also Month = 0?
– Korashen
Aug 8 at 13:56





@Korashen if day is ignored, the last two examples are correct. It is 12 months from Jan to Jan next year.
– Henry
Aug 8 at 13:57






@Korashen Because if he is going from January 31st to January 1st, he wants it to return 11 months because 12 full months have not passed.
– gkgkgkgk
Aug 8 at 13:57





Ah, so this is fuzzy logic! Cause the code is working perfectly fine.
– Korashen
Aug 8 at 14:03





@Henry I add correct code
– Alexei
Aug 8 at 15:17



Examples 3 and 4 are working as expected. Aug -> Jan is 5 months, Aug -> Aug is 12 months. When you are subtracting the month values, it is strictly dealing with month values, not the days of the month. If you want to account for the day of that month, add this:


if(dateFrom.get(Calendar.DAY_OF_MONTH) > dateTo.get(Calendar.DAY_OF_MONTH) && diff != 0)
diffMonth -=1;





Not work. If currentDate = Aug 08 2018, DateTo = Aug 13 2018 then diffMonth = -1. But must be 0
– Alexei
Aug 8 at 14:39






@Alexei sorry, I had the comparison backwards. I fixed my answer. If currentDate is more than DateTo, then subtract 1.
– gkgkgkgk
Aug 8 at 14:48





But what about this test case: currentDate = Aug 08 2018, DateTo = Aug 01 2018 -> diffMonth = -1 . It's not correct. Must be 0
– Alexei
Aug 8 at 14:57






@Alexei you are right. This is a very specific test case that can be dealt with by adding an extra condition.
– gkgkgkgk
Aug 8 at 15:10





FYI, the troublesome old date-time classes such as java.util.Date, java.util.Calendar, and java.text.SimpleDateFormat are now legacy, supplanted by the java.time classes. Much of the java.time functionality is back-ported to Java 6 & Java 7 in the ThreeTen-Backport project. Further adapted for earlier Android in the ThreeTenABP project. See How to use ThreeTenABP….
– Basil Bourque
Aug 11 at 18:49



java.util.Date


java.util.Calendar


java.text.SimpleDateFormat



This is one of the (many) cases where java.time, the modern Java date and time API, excels:


public static long differenceInMonths(LocalDate dateFrom, LocalDate dateTo)
return ChronoUnit.MONTHS.between(dateFrom, dateTo);



Yes, I seriously suggest that you use this in your old Java 6 code. It requires a little more explanation, of course. Before I get to that, allow me to demonstrate that the one-liner above gives you the results you expect:


LocalDate currentDate = LocalDate.of(2018, Month.AUGUST, 8);
System.out.println("1. Months until Aug 13 2018: "
+ differenceInMonths(currentDate, LocalDate.of(2018, Month.AUGUST, 13)));
System.out.println("2. Months until Oct 14 2018: "
+ differenceInMonths(currentDate, LocalDate.of(2018, Month.OCTOBER, 14)));
System.out.println("3. Months until Jan 03 2019: "
+ differenceInMonths(currentDate, LocalDate.of(2019, Month.JANUARY, 3)));
System.out.println("4. Months until Aug 03 2019: "
+ differenceInMonths(currentDate, LocalDate.of(2019, Month.AUGUST, 3)));



Output:


1. Months until Aug 13 2018: 0
2. Months until Oct 14 2018: 2
3. Months until Jan 03 2019: 4
4. Months until Aug 03 2019: 11


Date



As I understand, you have an old method taking two old-fashioned Date arguments and you are asking why it gives incorrect results. So I guess you will also want to fix it to give the results that are considered correct by your users (as your own answer suggests). So rewrite your method to use my method above:


Date


public static Integer getDiffMonths(Date dateFrom, Date dateTo)



Even though the conversions from Date to LocalDate are at least as wordy as the conversions to Calendar in your own code, you may consider it worth is when the difference calculation itself is so simple and clear.


Date


LocalDate


Calendar



What went wrong in your old code has been explained nicely by others, there is no reason for me to repeat.



java.time works nicely on Java 6.


java.time


org.threeten.bp



The code above uses ThreeTen Backport and imports the date and time classes from org.threeten.bp with subpackages:


org.threeten.bp


import org.threeten.bp.DateTimeUtils;
import org.threeten.bp.LocalDate;
import org.threeten.bp.Month;
import org.threeten.bp.ZoneId;
import org.threeten.bp.temporal.ChronoUnit;



If you want to use the same code on Java 8 or later and use the built-in java.time, the conversion happens a little differently, for example:


LocalDate localDateFrom = dateFrom.toInstant()
.atZone(zone)
.toLocalDate();


java.time


java.time


java.time



Here correct code:


public static Integer getDiffMonths(Date dateFrom, Date dateTo)
Calendar cal1 = new GregorianCalendar();
cal1.setTime(dateFrom);
Calendar cal2 = new GregorianCalendar();
cal2.setTime(dateTo);
return differenceInMonths(cal1, cal2);


private static Integer differenceInMonths(Calendar dateFrom, Calendar dateTo)
if (dateFrom == null





FYI, the troublesome old date-time classes such as java.util.Date, java.util.Calendar, and java.text.SimpleDateFormat are now legacy, supplanted by the java.time classes. Much of the java.time functionality is back-ported to Java 6 & Java 7 in the ThreeTen-Backport project. Further adapted for earlier Android in the ThreeTenABP project. See How to use ThreeTenABP….
– Basil Bourque
Aug 11 at 18:48



java.util.Date


java.util.Calendar


java.text.SimpleDateFormat






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Firebase Auth - with Email and Password - Check user already registered

Dynamically update html content plain JS

How to determine optimal route across keyboard