java.time
Other answers use the nasty old Calendar class, which is now deprecated, being superseded by java.time classes.
MONTHS_BETWEEN
doc says :
MONTHS_BETWEEN returns the number of months between dates date1 and date2. If date1 is later than date2, the result is positive. If date1 is earlier than date2, the result is negative. If date1 and date2 are the same days of the month or the last two days of the months, then the result is always an integer. Otherwise, Oracle Database calculates the fractional part of the result based on the 31-day month and considers the difference in the time components date1 and date2.
LocalDate
The LocalDate class represents a value only for a date with no time and no time zone.
LocalDate from the database using JDBC 4.2 and later. The java.sql.Date class is now deprecated and can be avoided.
LocalDate start = myResultSet.getObject( β¦ , LocalDate.class ) ; // Retrieve a `LocalDate` from database using JDBC 4.2 and later.
For our demonstration here, let's simulate the dates received.
LocalDate start = LocalDate.of( 2018 , Month.JANUARY , 23 ); LocalDate stop = start.plusDays( 101 );
Period
Calculate elapsed time as a period of time not tied to the timeline, Period .
Period p = Period.between( start , stop );
Extract the total number of months .
long months = p.toTotalMonths() ;
Extract the number of days, part , days remaining after calculating the months.
int days = p.getDays() ;
BigDecimal
Use BigDecimal for accuracy. The double and double types use technology technology with floating point , which allows you to quickly evaluate the effectiveness of the execution.
Convert our values ββfrom primitives to BigDecimal .
BigDecimal bdDays = new BigDecimal( days ); BigDecimal bdMaximumDaysInMonth = new BigDecimal( 31 );
Divide to get our fractional month. MathContext provides a limit to the resolution of a fractional number plus a rounding mode to get there. Here we use the constant MathContext.DECIMAL32 , because I assume that the Oracle function uses 32-bit math. Rounding mode RoundingMode.HALF_EVEN, defaults to IEEE 754 , and is also known as Rounding Bankers , which is more mathematically fair than school rounding, usually taught to children.
BigDecimal fractionalMonth = bdDays.divide( bdMaximumDaysInMonth , MathContext.DECIMAL32 );
Add this share to our number of whole months to get the full result.
BigDecimal bd = new BigDecimal( months ).add( fractionalMonth );
To more closely mimic the behavior of an Oracle function, you can convert to double .
double d = bd.round( MathContext.DECIMAL32 ).doubleValue();
Oracle has not documented the details of its calculations. Thus, you may need to do a trial evaluation to see if this code matches the results of your Oracle function.
Dump for the console.
System.out.println( "From: " + start + " to: " + stop + " = " + bd + " months, using BigDecimal. As a double: " + d );
See this code run on IdeOne.com .
From: 2018-01-23 to: 2018-05-04 = 3.3548387 months using BigDecimal. In double: 3.354839
Caution: While I was answering the question of how I was asked, I should note: keeping track of elapsed time as a particle, as shown here, is unreasonable. Use the java.time Period and Duration classes instead. For textual representation, use the standard ISO 8601 format : PnYnMnDTnHnMnS. For example, Period in our example above: P3M11D for three months and eleven days.
About java.time
The java.time framework is built into Java 8 and later. These classes supersede the nasty old legacy datetime classes such as java.util.Date , Calendar and SimpleDateFormat .
The Joda-Time project, now in maintenance mode , is advised to switch to the java.time classes.
To learn more, see the Oracle Tutorial . And search for qaru for many examples and explanations. JSR 310 specification .
Where to get java.time classes?
The ThreeTen-Extra project extends java.time with additional classes. This project is a proof of possible future additions to java.time. Here you can find useful classes such as Interval , YearWeek , YearQuarter and more .