java.time
В других ответах используется проблемный старый класс Calendar
, который теперь унаследован и заменен классами java.time .
MONTHS_BETWEEN
Документ говорит :
MONTHS_BETWEEN возвращает количество месяцев между датами date1 и date2. Если дата1 позже даты2, то результат положительный. Если date1 раньше, чем date2, то результат отрицательный. Если date1 и date2 являются либо одними и теми же днями месяца, либо обоими последними днями месяцев, то результатом всегда будет целое число. В противном случае Oracle Database рассчитывает дробную часть результата на основе 31-дневного месяца и учитывает разницу во временных компонентах date1 и date2.
LocalDate
Класс LocalDate
представляет значение только для даты без времени суток и без часового пояса.
Получить LocalDate
из базы данных, используя JDBC 4.2 и более поздние версии. Класс java.sql.Date
теперь унаследован и его можно избежать.
LocalDate start = myResultSet.getObject( … , LocalDate.class ) ; // Retrieve a `LocalDate` from database using JDBC 4.2 and later.
Для нашей демонстрации давайте смоделируем эти найденные даты.
LocalDate start = LocalDate.of( 2018 , Month.JANUARY , 23 );
LocalDate stop = start.plusDays( 101 );
Period
Рассчитать прошедшее время как промежуток времени, не привязанный к временной шкале, a Period
.
Period p = Period.between( start , stop );
Извлечь общее количество месяцев .
long months = p.toTotalMonths() ;
Извлеките количество дней, составляющих , дни, оставшиеся после расчета месяцев.
int days = p.getDays() ;
BigDecimal
Для точности используйте BigDecimal
. В типах double
и Double
используется технология с плавающей запятой , снижающая точность для быстрого выполнения.
Преобразовать наши значения из примитивов в BigDecimal
.
BigDecimal bdDays = new BigDecimal( days );
BigDecimal bdMaximumDaysInMonth = new BigDecimal( 31 );
Разделите, чтобы получить наш дробный месяц. MathContext
предоставляет ограничение для разрешения дробного числа, а также режим округления, чтобы туда попасть. Здесь мы используем константу MathContext.DECIMAL32
, потому что я предполагаю, что функция Oracle использует 32-битную математику. Режим округления - RoundingMode.HALF_EVEN
, по умолчанию задан IEEE 754 , а также известен как «Банковское округление» , что математически более справедливо, чем «округление в школе» ”Обычно учат детей.
BigDecimal fractionalMonth = bdDays.divide( bdMaximumDaysInMonth , MathContext.DECIMAL32 );
Добавьте эту дробь к числу целых месяцев, чтобы получить полный результат.
BigDecimal bd = new BigDecimal( months ).add( fractionalMonth );
Чтобы более близко имитировать поведение функции Oracle, вы можете преобразовать в double
.
double d = bd.round( MathContext.DECIMAL32 ).doubleValue();
Oracle не документировал кровавые подробности их расчета. Поэтому вам может потребоваться провести эксперименты с пробами и ошибками, чтобы проверить, соответствует ли этот код вашим функциям Oracle.
Дамп на консоль.
System.out.println( "From: " + start + " to: " + stop + " = " + bd + " months, using BigDecimal. As a double: " + d );
См. Этот код, запущенный на IdeOne.com .
С: 2018-01-23 до: 2018-05-04 = 3,3548387 месяцев с использованием BigDecimal. Как двойной: 3.354839
Предостережение: Пока я отвечал на Вопрос в соответствии с просьбой, я должен отметить: Отслеживание прошедшего времени в виде дроби, как видно здесь, неразумно . Вместо этого используйте классы java.time Period
и Duration
. Для текстового представления используйте стандартный формат ISO 8601 : PnYnMnDTnHnMnS. Например, Period
видно в нашем примере выше: P3M11D
в течение трех месяцев и одиннадцати дней.
О java.time
Фреймворк java.time встроен в Java 8 и более поздние версии. Эти классы вытесняют проблемные старые устаревшие классы даты и времени, такие как java.util.Date
, Calendar
, & SimpleDateFormat
.
Проект Joda-Time , теперь в режиме обслуживания , рекомендует выполнить переход на классы java.time .
Чтобы узнать больше, см. Oracle Tutorial . И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310 .
Где получить классы java.time?
ThreeTen-Extra Проект расширяет java.time дополнительными классами.Этот проект является полигоном для возможных будущих дополнений к java.time.Здесь вы можете найти несколько полезных классов, таких как Interval
, YearWeek
, YearQuarter
и more .