Добавить год в календарь Java не работает

Пожалуйста, просветите меня в этом:

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

public int getMaxYears() {
  int max = 0;
  Calendar ten_year_later = Calendar.getInstance();
  ten_year_later.setTime(new Date());
  ten_year_later.add(Calendar.YEAR, 10);
  Calendar expiration = Calendar.getInstance();
  expiration.setTime(expiration_date);
  max = (int) (ten_year_later.getTimeInMillis() - expiration.getTimeInMillis())/(365 * 24 * 60 * 60 * 1000);
  return max;
}

Когда я отлаживаю это, календарь всегда остается в текущем году.

Кто угодно ?


person codea    schedule 07.10.2010    source источник
comment
Какой календарь? ten_year_later или срок действия?   -  person Paul Tomblin    schedule 07.10.2010
comment
@Paul Tomblin: ten_year_later ... Я отредактирую вопрос.   -  person codea    schedule 07.10.2010
comment
Также: год не 365 дней, он должен учитывать високосные годы. Для грубых расчетов подойдет 365,25, но для реальных расчетов нужно что-то, что знает правила... вроде Календаря.   -  person Anon    schedule 07.10.2010
comment
@Анон: Я знаю. Спасибо, но вопрос не в этом.   -  person codea    schedule 07.10.2010
comment
Я бы предложил использовать библиотеку JodaTime для всех ваших потребностей в расчете даты.   -  person Erick Robertson    schedule 07.10.2010
comment
Возможно, но это всего лишь одна из нескольких логических ошибок в коде. Суть моего комментария в том, что у вас есть инструмент для манипулирования датами, но вы пытаетесь манипулировать ими вне этого инструмента. Смотрите мой ответ, если вам нужны подробные инструкции.   -  person Anon    schedule 07.10.2010
comment
Ну спасибо всем! Моя проблема была не с календарем, а с максимальным расчетом. Используя длинный тип, как ответ Stacker, решите мою проблему.   -  person codea    schedule 07.10.2010


Ответы (7)


У вас проблема с преобразованием int/long: 365 * 24 * 60 * 60 * 1000, что оценивается как 31536000000 и, следовательно, превышает Integer.MAX_VALUE 2147483647. Это работает:

public static void main(String[] args) {
          Calendar ten_year_later = Calendar.getInstance();
          System.out.println( ten_year_later.getTime() );
          ten_year_later.setTime(new Date()); 
          ten_year_later.add(Calendar.YEAR, 10);
          System.out.println( ten_year_later.getTime() );
          Calendar expiration = Calendar.getInstance(); 
          expiration.setTime(expiration.getTime()); 
          long max = (ten_year_later.getTimeInMillis() - expiration.getTimeInMillis())/(365 * 24 * 60 * 60 * 1000L); 
          System.out.println( "max " + max );
        } 
person stacker    schedule 07.10.2010
comment
+1: я думаю, что это лучше для случая, когда ОП хотел бы округлить количество лет в меньшую сторону, а не в большую. ОП должен только помнить, что в году не обязательно 365 дней. - person BalusC; 07.10.2010

Ваш расчет max неверен. int не может хранить год в миллисекундах.

Скорее замените его на

max = ten_year_later.get(Calendar.YEAR) - expiration.get(Calendar.YEAR);

Или лучше используйте JodaTime:

DateTime tenYearsLater = new DateTime().plusYears(10);
DateTime expiration = new DateTime(expiration_date.getTime());
Period period = new Period(expiration, tenYearsLater);
return period.getYears();
person BalusC    schedule 07.10.2010
comment
Так не пойдет! 01.01.2020 - 31.12.2010 = 10. Должно получиться 9 + 1 день, значит 9 лет; - person codea; 07.10.2010
comment
@elblanco: см. мой комментарий к ответу укладчика. - person BalusC; 07.10.2010
comment
+1 Я полностью согласен с тем, что это должно быть рассчитано, как вы указали. - person stacker; 07.10.2010

Вот простой пример того, что должно работать.

Calendar cal = new GregorianCalendar();
cal.setTime(new Date());
cal.add(Calendar.YEAR, yearsToAdd);
Date retDate = cal.getTime();

Просто не забудьте использовать long, чтобы получить время в миллисекундах!

person Bryan James    schedule 07.10.2010
comment
Это единственное рабочее решение для меня из всех этих ответов. Спасибо Брайан! - person rilar; 05.08.2014

Календарь ленив, поэтому он может не пересчитывать все остальные поля, пока вы их не попросите. Это сбило меня с толку в отладчике раньше. Что произойдет, если вы System.out.println(ten_year_later);?

person Paul Tomblin    schedule 07.10.2010
comment
getTimeInMillis() уже должен был обновить поля - как и любой другой get метод Calendar. - person BalusC; 07.10.2010
comment
Даже если я вызову ten_year_later.getTimeInMillis()? max всегда равно 0, потому что expire_date после сегодняшнего дня. - person codea; 07.10.2010
comment
Нет, я говорил о том, что вы видите в отладчике перед вызовом getTimeInMillis(). - person Paul Tomblin; 07.10.2010

Я отметил в комментарии, что у вас неправильный расчет количества милли в год (не говоря уже о проблеме int/long).

Поскольку у вас есть два календаря, в каждом из которых может храниться год, почему бы вам не написать свой код так (не скомпилированный, поэтому может содержать опечатки):

Calendar cal1 = Calendar.newInstance();   // this will use current time
cal1.add(Calendar.YEAR, 10);
Calendar cal2 = Calendar.newInstance();
cal2.setDate(expiration);
return cal1.get(Calendar.YEAR) - cal2.get(Calendar.YEAR);

Предположим, что вы действительно этого хотите...

person Anon    schedule 07.10.2010

tl;dr

Используйте современные классы java.time. Можно сделать в один лайнер (не то, чтобы я рекомендовал это).

Period
.between(
    ( ( GregorianCalendar) myCalendarExpiration ).toZonedDateTime().toLocalDate() ,
    ZonedDateTime.now( ZoneId.of( "Asia/Kolkata" ) ).plusYears( 10 ).toLocalDate() 
)
.getYears()

java.время

Современный подход использует классы java.time, которые заменили устаревшие классы даты и времени, такие как Calendar.

Обычной конкретной реализацией Calendar является GregorianCalendar. Теперь это заменено на ZonedDateTime. Вы можете конвертировать туда и обратно, вызывая новые методы в старых классах.

ZonedDateTime zdtExpiration = ( ( GregorianCalendar) myCal ).toZonedDateTime() ;

Получить текущий момент.

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdtNow  = ZonedDateTime.now( z ) ;

Добавьте десять лет.

ZonedDateTime zdtLater = zdtNow.plusYears( 10 ) ;

Рассчитайте время, прошедшее в годах между датами этих двух моментов.

Period p = Period.between(
    zdtExpiration.toLocalDate() , 
    zdtLater.toLocalDate()
) ;

Опросите количество полных лет.

int yearsElapsed = p.getYears() ;

О java.time

< Платформа em>java.time встроена в Java 8 и более поздние версии. Эти классы заменяют проблемные старые устаревшие классы даты и времени, такие как java.util.Date, Calendar и SimpleDateFormat.

Чтобы узнать больше, см. Учебное пособие по Oracle. И поищите множество примеров и пояснений в Stack Overflow. Спецификация: JSR 310.

Проект Joda-Time, теперь в режим обслуживания, советует перейти на java.time.

Вы можете обмениваться объектами java.time непосредственно с вашей базой данных. Используйте драйвер JDBC, совместимый с JDBC 4.2 или более поздней версии. Нет необходимости в строках, нет необходимости в java.sql.* классах.

Где получить классы java.time?

Проект ThreeTen-Extra расширяет java.time дополнительными классами. . Этот проект является испытательным полигоном для возможных дополнений к java.time в будущем. Здесь вы можете найти несколько полезных классов, таких как Interval, YearWeek, YearQuarter и подробнее.

person Basil Bourque    schedule 23.11.2019

Количество миллисекунд в году выходит далеко за пределы диапазона int, поэтому как целочисленное приведение ten_year_later.getTimeInMillis() - expiration.getTimeInMillis(), так и вычисление 365 * 24 * 60 * 60 * 1000 будут оцениваться как неверные значения.

ten_year_later должно быть правильным. Нет необходимости вызывать calculateFields, как писал Р. Бемроуз.

person jarnbjo    schedule 07.10.2010