Prototype code used to compute fractional month difference between two dates.
String[][] tests = {
{"2024-04-14", "2024-04-04"}, //<1 month (10 days or 0.333)
{"2024-04-04", "2024-04-14"}, //<1 month (10 days or 0.333)
{"2024-03-04", "2024-04-04"}, //1 month
{"2024-03-04", "2025-04-04"}, //13 months
{"2024-06-04", "2024-07-04"}, //1 month
{"2024-11-28", "2025-02-28"}, //3 months
{"2024-02-28", "2025-02-28"}, //12 months
{"2024-02-29", "2025-02-28"}, //12 months (leap year)
{"2024-11-30", "2025-02-28"}, //3 months
{"2024-01-01", "2024-01-31"}, //<1 month
{"2024-01-01", "2025-01-01"}, //12 months
{"2024-01-01", "2025-12-01"}, //23 months
{"2024-11-30", "2024-12-31"}, //1 month
{"2024-09-30", "2024-12-31"}, //3 months
{"2024-06-30", "2024-12-31"}, //6 months
{"2024-03-01", "2024-04-15"}, //1+ months
{"2024-03-31", "2024-04-01"}, //<1 month (1 day)
{"2024-03-31", "2024-04-05"}, //<1 month (5 days)
{"2024-03-31", "2024-04-15"}, //0.5 months (15 days)
{"2024-03-31", "2024-04-30"}, //1 month
{"2024-03-31", "2024-05-01"}, //1+ month
{"2024-11-28", "2025-02-27"}, //<3 months
{"2024-11-28", "2025-02-28"}, //3 months
{"2024-11-29", "2025-02-28"}, //<3 months
{"2024-11-30", "2025-02-28"}, //3 months
{"2024-12-30", "2025-02-28"}, //<2 months
{"2024-11-15", "2024-11-30"}, //0.5 months
{"2024-11-30", "2024-11-15"}, //-0.5 months
{"2024-11-15", "2025-11-30"}, //12.5 months
{"2023-01-28", "2023-02-27"}, //<1 month
{"2023-02-27", "2023-03-26"}, //<1 month
{"2023-02-27", "2023-03-27"}, //1 month
{"2023-02-27", "2023-03-28"}, //1+ month
{"2023-02-27", "2023-03-29"}, //1+ month
{"2023-02-27", "2023-03-30"}, //1+ month
{"2023-02-27", "2023-03-31"}, //1+ month
{"2023-02-28", "2023-03-30"}, //1+ month
{"2023-02-28", "2023-03-31"}, //1 month
{"2023-01-27", "2023-02-28"}, //>1 month
{"2023-01-28", "2023-02-28"}, //1 month
{"2023-01-29", "2023-02-28"}, //<1 month
};
private static double getMonthsBetween(LocalDate start, LocalDate end) {
//Check if the start date is after the end date. Swap dates as needed
boolean negate = false;
if (start.isAfter(end)){
negate = true;
LocalDate t = start;
start = end;
end = t;
}
//Check if start/end dates fall on the last of the month
boolean startIsLastDayInMonth = start.getDayOfMonth() == start.lengthOfMonth();
boolean endIsLastDayInMonth = end.getDayOfMonth() == end.lengthOfMonth();
//Calulate months between the 2 dates using Java's built-in ChronoUnit
//Note that the ChronoUnit "rounds down" the interval between the dates.
long m = ChronoUnit.MONTHS.between(start, end);
//When the 2 dates fall on the same day in the month, and the dates aren't
//on the last day of the month, simply return the value returned by the
//ChronoUnit class
int startDay = start.getDayOfMonth();
int endDay = end.getDayOfMonth();
if (startDay==endDay){
if (startIsLastDayInMonth && !endIsLastDayInMonth ||
!startIsLastDayInMonth && endIsLastDayInMonth){
//Example: 2024-11-28 2025-02-28
}
else{
return m;
}
}
//If we're still here, compute fractions
double fraction = 0.0;
if (m==0 && start.getMonthValue()==end.getMonthValue()){
//Simply compare the days of the month
fraction = (end.getDayOfMonth()-start.getDayOfMonth())/(double)end.lengthOfMonth();
}
else{
//Create new end date using the original end date. Adjust the day
//of the month to match the start date. The new date will be either
//before or after the original end date.
int maxDays = LocalDate.of(end.getYear(), end.getMonthValue(), 1).lengthOfMonth();
LocalDate e2 = LocalDate.of(end.getYear(), end.getMonthValue(), Math.min(start.getDayOfMonth(), maxDays));
if (start.getDayOfMonth()>maxDays){
//Create new date a few days after the end of the month
LocalDate d = e2.plusDays(start.getDayOfMonth()-maxDays);
//Calulate months between the start date and the new date
m = ChronoUnit.MONTHS.between(start, d);
//Calculate fraction
if (startIsLastDayInMonth && endIsLastDayInMonth){}
else{
if (!startIsLastDayInMonth){
fraction = -((start.lengthOfMonth()-start.getDayOfMonth())/(double)start.lengthOfMonth());
}
else{
fraction = -(1-((end.getDayOfMonth())/(double)maxDays));
}
}
}
else{
//Calulate months between the start date and the new end date
m = ChronoUnit.MONTHS.between(start, e2);
//Calculate fraction
if (e2.isAfter(end)){
//subtract from e2
int n = e2.getDayOfMonth()-end.getDayOfMonth();
double f = (double)n/(double)end.lengthOfMonth();
if (m==0){
fraction = 1-f;
}
else{
fraction = -f;
}
}
else if (e2.isBefore(end)){
//add from e2
fraction = (end.getDayOfMonth()-start.getDayOfMonth())/(double)end.lengthOfMonth();
}
}
}
//Add months and fractions
double diff = fraction+(double)m;
//When the 2 dates fall on the the last day of the month, round up
if (startIsLastDayInMonth && endIsLastDayInMonth){
diff = Math.round(diff);
}
//Return diff
return negate ? -diff : diff;
}