Странное значение при расчете разницы дат
Я использовал следующий код для вычисления разницы между двумя датами, и я получил необычную ошибку:
если я пройду следующие даты:
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 ) ;