Странное значение при расчете разницы дат

Я использовал следующий код для вычисления разницы между двумя датами, и я получил необычную ошибку:

если я пройду следующие даты:

d1 = 12/12/2017 d2 = 31/07/2022

возвращается: 24055..

разве результат не должен возвращаться как количество месяцев?

 public static int CalcDateDiff( java.util.Date  date1,  java.util.Date  date2) {
    if(date1 == null || date2 ==null )
    {
        if(date2 == null)
        {
              Calendar d1 = Calendar.getInstance();
                d1.setTime(date1);
                final Calendar d2 = Calendar.getInstance();
                int diff = (d2.get(Calendar.YEAR) - d1.get(Calendar.YEAR)) * 12 + d2.get(Calendar.MONTH) - d1.get(Calendar.MONTH);

                return diff;
        }
        else
            return -1;
    }
    else
    {

            Calendar d1 = Calendar.getInstance();
            d1.setTime(date1);
            final Calendar d2 = Calendar.getInstance();
            d2.setTime(date2);
            int diff = (d2.get(Calendar.YEAR) - d1.get(Calendar.YEAR)) * 12 + d2.get(Calendar.MONTH) - d1.get(Calendar.MONTH);

            return diff;
        }
}

Заранее спасибо!

2 ответа

Лучшее решение:

Если у вас уже есть java.sql.Date экземпляры тогда

final Date d1 = new java.sql.Date(2017 - 1900, 12, 8);
final Date d2 = new java.sql.Date(2022 - 1900, 07, 31);

Я знаю, что я использую устаревший конструктор в java.sql.Date, это просто для удобства, чтобы получить то, что мне нужно в наименьшем количестве строк кода. Никогда не используйте этот конструктор в производственном коде!

Самый простой и простой способ самодокументирования, чтобы получить то, что вы хотите:

final long monthsBetween ChronoUnit.MONTHS.between(d1.toLocalDate(),d2.toLocalDate()) + 1;

Потому что с этим методом вам не придется вертеть с TimeZone информация, потому что все это гарантированно будет правильным для случаев LocalDate создано таким образом.

Только по какой-то причине java.sql.Date только имеет .toLocalDate() это хорошо для вас, потому что это то, что вы получаете из базы данных.

public LocalDate toLocalDate() Преобразует этот объект Date в LocalDate. При преобразовании создается LocalDate, представляющий то же значение даты, что и эта Date в местном часовом поясе.

Возвращает: объект LocalDate, представляющий одно и то же значение даты. С: 1.8


Комментарии по исправлению к вашему коду:

Правильная формула:

(y2 - y1) * 12 + (m2 - m1) + 1

Кроме того, ваш код слишком сложен и использует очень старые классы.

Решение Java 8 и ваше исправленное решение, совместимое с Java 7:

public class Q47717075
{
    /*
    https://stackru.com/questions/1086396/java-date-month-difference
     */
    public static void main(@Nonnull final String[] args)
    {
        final Date d1 = new java.sql.Date(2017 - 1900, 12, 8);
        final Date d2 = new java.sql.Date(2022 - 1900, 07, 31);
        System.out.println(CalcDateDiff(d1, d2));
        /* Obtains an instance of Instant from a text string such as 2007-12-03T10:15:30.00Z. */
        System.out.println(intervalInMonths(LocalDate.of(2017,12,8), LocalDate.of(2022,7,31)));
        System.out.println(ChronoUnit.MONTHS.between(d1.toLocalDate(),d2.toLocalDate()) + 1);
    }


    /*
       Alternate Java 8 version
     */
    public static int intervalInMonths(@Nonnull final LocalDate i1, @Nonnull final LocalDate i2)
    {
        final Period p = Period.between(i1, i2);
        return (p.getYears() * 12) + p.getMonths() + 1;
    }

    /**
     * Your versin corrected
     */
    public static int CalcDateDiff(@Nonnull final Date date1, @Nonnull final Date date2)
    {
        final Calendar d1 = Calendar.getInstance();
        d1.setTime(date1);
        final Calendar d2 = Calendar.getInstance();
        d2.setTime(date2);

        return ((d2.get(YEAR) - d1.get(YEAR)) * 12 + (d2.get(MONTH) - d1.get(MONTH)) + 1);
    }
}

Правильный вывод:

56
56
56

ТЛ; др

ChronoUnit.MONTHS.between(                                           // Use enum method to calculate elapsed time.
    date1.toInstant().atZone( ZoneId.of( "Africa/Casablanca " ) ) ,  // Convert from legacy `Date` class to modern `java.time.Instant` class. Adjust from UTC to a specific time zone. 
    date2.toInstant().atZone( ZoneId.of( "Africa/Casablanca " ) ) 
)                                                                    // Return a number of months elapsed. 

подробности

Ответ Роберсона хорош. Вот альтернатива.

Ваше использование хлопотноDate класс сейчас устарел. Этот унаследованный класс заменяется Instant в пакете java.time Оба представляют момент, точку на временной шкале в UTC. Современный класс имеет разрешение наносекунд, а не миллисекунд.

Преобразование с использованием новых методов, добавленных к старым классам.

Instant instant = myUtilDate.toInstant() ;

Чтобы определить количество месяцев, нам нужны даты. Для определения даты требуется часовой пояс. В любой момент времени дата меняется по всему земному шару в зависимости от зоны.

ZoneId z = ZoneId.of( "Pacific/Auckland " ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

ChronoUnit enum предлагает between метод расчета прошедшего времени.

long months = ChronoUnit.MONTHS.between( zdt1 , zdt2 ) ;
Другие вопросы по тегам