Ответ 1
Это связано с тем, что 30 * 1000 * 60 * 60 * 24
переполняет Integer.MAX_VALUE
, а 20 * 1000 * 60 * 60 * 24
- нет.
почему, когда я добавляю 30 дней до сегодняшнего дня, я получил сегодня день - 30, и когда я добавляю 20, он добавляет
вот пример
import java.text.DateFormat;
import java.util.Date;
public class DatePlus
{
public static void main(String[] args)
{
Date now = new Date();
Date now1 = new Date();
Date now2 = new Date();
DateFormat currentDate = DateFormat.getDateInstance();
Date addedDate1 = addDays(now2, 20);
Date addedDate2 = addDays(now1, 30);
System.out.println(currentDate.format(now));
System.out.println(currentDate.format(addedDate1));
System.out.println(currentDate.format(addedDate2));
}
public static Date addDays(Date d, int days)
{
d.setTime(d.getTime() + days * 1000 * 60 * 60 * 24);
return d;
}
}
"это консоль"
Jul 30, 2012
Aug 19, 2012
Jul 10, 2012
Это связано с тем, что 30 * 1000 * 60 * 60 * 24
переполняет Integer.MAX_VALUE
, а 20 * 1000 * 60 * 60 * 24
- нет.
Используйте Календарь. http://docs.oracle.com/javase/6/docs/api/java/util/GregorianCalendar.html
Псевдокод:
Calendar c=new GregorianCalendar();
c.add(Calendar.DATE, 30);
Date d=c.getTime();
Date
не привязан к календарной системе, используемой людьми. Он просто представляет момент времени. Добавление 30 дней до Date
не имеет смысла, это похоже на добавление 20 к красному цвету.
Общий подход добавления 1000 * 60 * 60 * 24
неверен. Вы добавляете 86400 секунд, но один день не обязательно 86400 секунд. Это может быть на один час длиннее или короче из-за dst. Это может быть на одну секунду длиннее или короче из-за прыжковых секунд.
Что вам нужно сделать, это преобразовать Date
в Calendar
(который фактически представляет собой некоторую систему календаря, например GregorianCalendar
). Затем просто добавьте дни:
Calendar calendar = new GregorianCalendar(/* remember about timezone! */);
calendar.setTime(date);
calendar.add(Calendar.DATE, 30);
date = calendar.getTime();
Используйте DateUtils.addDays()
от Apache Commons Lang:
DateUtils.add(date, 30);
Это не нарушает написанное выше, оно преобразуется в Calendar
под.
Или вообще избегайте этого ада и идите за Joda Time.
days
- целое число.
30 * 1000 * 60 * 60 * 24 - 2 592 000 000, больше, чем 2,147,483,647 (наибольший int на Java). У вас переполнение буфера, и ваш результат представляет собой значительно меньшее отрицательное число (проверьте его в двоичном формате, преобразуйте обратно в int как 2 дополнения)
Легкое исправление заключается в том, чтобы вывести одно из значений так долго, чтобы результат выражения хранился как длинный, который может удерживать это значение без переполнения.
(long) days * 1000L * 60 * 60 * 24
Всегда рекомендуется использовать Календарь или, возможно, другой api (я слышал о JodaTime, но не использовал его), чтобы манипулировать датами.
В ваших расчетах встречается целочисленное переполнение. Значение дней * 1000 * 60 * 60 * 24 больше максимального значения, которое разрешено для подписанного int при днях = 30, но не тогда, когда дни = 20. Когда вы увеличиваете целое число со знаком, максимально возможное значение, вместо того, чтобы увеличиваться в стоимости, знак переворачивается, и он становится отрицательным! Вот почему ваша дата идет назад - вы добавляете к ней отрицательное число.
Чтобы решить эту проблему, вы можете использовать длинный тип данных, который имеет гораздо большее максимальное значение, чем целое число, например:
long secondsToAdd = days;
secondsToAdd *= (1000*60*60*24);
d.setTime(d.getTime() + secondsToAdd);
Как отмечали другие, конкретная проблема - это переполнение 32-разрядного целого числа.
Этот код также игнорирует критическую проблему часового пояса.
Большая проблема заключается в том, что вы пытаетесь выполнять собственные расчеты по дате, а не используя приличную библиотеку.
Классы java.time, встроенные в Java 8 и более поздние версии, вытесняют неприятные старые классы времени.
An Instant
- момент на временной шкале в UTC с разрешением наносекунд.
Instant now = Instant.now();
Примените часовой пояс, чтобы получить ZonedDateTime
.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
Теперь добавьте 30 дней. Пусть java.time обрабатывает аномалии, такие как переход на летнее время (DST).
ZonedDateTime zdtLater = zdt.plusDays( 30 );
Или, может быть, вы имели в виду месяц. Пусть java.time обрабатывает сведения о месяцах, имеющих разную длину.
ZonedDateTime zdtMonthLater = zdt.plusMonths( 1 );
См. этот вопрос для получения информации о преобразовании нового и старого типов. См. Новые методы, добавленные в старый класс, например:
java.util.Date utilDate = java.util.Date.from( zdt.toInstant() );
java.util.Calendar utilCalendar = java.util.GregorianCalendar.from( ZonedDateTime );