Как вычесть X дней из даты, используя календарь Java?

180

Кто-нибудь знает простой способ использования календаря Java для вычитания X дней из даты?

Я не смог найти ни одной функции, которая позволяла бы мне напрямую вычитать X дней из даты в Java. Может ли кто-нибудь указать мне правильное направление?

fmsf
источник
К вашему сведению, проблемные старые классы даты и времени, такие как java.util.Calendarтеперь, унаследованы , заменены классами java.time . Смотрите Учебник по Oracle .
Базилик Бурк

Ответы:

309

Взято из документации здесь :

Добавляет или вычитает указанное количество времени к заданному календарному полю на основе правил календаря. Например, чтобы вычесть 5 дней из текущего времени календаря, вы можете добиться этого, позвонив:

Calendar calendar = Calendar.getInstance(); // this would default to now
calendar.add(Calendar.DAY_OF_MONTH, -5).
Энсон Смит
источник
1
Просто будьте осторожны, поскольку это не всегда катится так, как вы ожидаете.
Carson
1
Этот ответ имеет много положительных отзывов, но безопасно ли его использовать? или это лучше: stackoverflow.com/a/10796111/948268
Kuldeep Jain
44
Вы не будете использовать Calendar.DAY_OF_MONTHв этом случае, так как он не будет обрабатывать бросок между месяцами, как вы хотели бы. Используйте Calendar.DAY_OF_YEARвместо этого
алгоритмы
4
Это должно быть безопасно, очевидно, когда вы добавляете, не имеет значения, является ли это DAY_OF_MONTH или DAY_OF_YEAR stackoverflow.com/q/14065198/32453
rogerdpack
@carson, какие проблемы с этим подходом?
tatmanblue
38

Вы можете использовать addметод и передать ему отрицательное число. Тем не менее, вы также можете написать более простой метод, который не использует Calendarкласс, такой как следующий

public static void addDays(Date d, int days)
{
    d.setTime( d.getTime() + (long)days*1000*60*60*24 );
}

Это получает значение метки времени даты (миллисекунды с начала эпохи) и добавляет правильное количество миллисекунд. Вы можете передать отрицательное целое число для параметра days, чтобы выполнить вычитание. Это было бы проще, чем «правильное» календарное решение:

public static void addDays(Date d, int days)
{
    Calendar c = Calendar.getInstance();
    c.setTime(d);
    c.add(Calendar.DATE, days);
    d.setTime( c.getTime().getTime() );
}

Обратите внимание, что оба эти решения изменяют Dateобъект, переданный в качестве параметра, а не возвращают совершенно новый Date. Любая функция может быть легко изменена, чтобы сделать это другим способом при желании.

Эли Кортрайт
источник
3
Не верьте .setTime и .add являются статическими методами Календаря. Вы должны использовать переменную экземпляра c.
Эдвард
@ Эдвард: вы правы, спасибо за указание на мою ошибку, я исправил код, чтобы он теперь работал правильно.
Эли Кортрайт
3
Будьте осторожны с первым способом, например, при работе с високосными днями он может потерпеть неудачу: stackoverflow.com/a/1006388/32453
rogerdpack
К вашему сведению, проблемные старые классы даты и времени, такие как java.util.Calendarтеперь, унаследованы , заменены классами java.time . Смотрите Учебник по Oracle .
Василий Бурк
36

Ответ Ансона подойдет для простого случая, но если вы собираетесь делать более сложные вычисления даты, я бы порекомендовал проверить Joda Time . Это сделает вашу жизнь намного проще.

К вашему сведению в Joda Time вы могли бы сделать

DateTime dt = new DateTime();
DateTime fiveDaysEarlier = dt.minusDays(5);
Майк Дек
источник
1
@ ShahzadImam, проверьте DateTimeFormat . Это позволит вам преобразовывать экземпляры DateTime в строки, используя произвольные форматы. Это очень похоже на класс java.text.DateFormat.
Майк Дек
1
На Java 8 рассмотреть возможность использования java.time docs.oracle.com/javase/8/docs/api/java/time/...
toidiu
К вашему сведению, проект Joda-Time в настоящее время находится в режиме обслуживания , и команда советует перейти на классы java.time . Смотрите Учебник по Oracle .
Василий Бурк
19

ТЛ; др

LocalDate.now().minusDays( 10 )

Лучше указать часовой пояс.

LocalDate.now( ZoneId.of( "America/Montreal" ) ).minusDays( 10 )

подробности

Старые классы даты и времени, связанные с ранними версиями Java, такими как java.util.Date/ .Calendar, оказались проблематичными, запутанными и ошибочными. Избежать их.

java.time

Java 8 и более поздние версии заменяют эти старые классы новой структурой java.time. Смотрите учебник . Определено JSR 310 , вдохновлено Joda-Time и расширено проектом ThreeTen-Extra . Проект ThreeTen-Backport переносит классы на Java 6 и 7; проект ThreeTenABP для Android.

Вопрос неопределенный, неясно, запрашивает ли он только дату или дату-время.

LocalDate

Только для даты, без времени суток, используйте LocalDateкласс. Обратите внимание, что часовой пояс имеет решающее значение при определении даты, такой как «сегодня».

LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) );
LocalDate tenDaysAgo = today.minusDays( 10 );

ZonedDateTime

Если вы имели в виду дату и время, используйте Instantкласс, чтобы получить момент времени на UTC . Оттуда установите часовой пояс, чтобы получить ZonedDateTimeобъект.

Instant now = Instant.now();  // UTC.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
ZonedDateTime tenDaysAgo = zdt.minusDays( 10 );

Таблица типов даты и времени в Java, как современных, так и устаревших.


О java.time

Java.time каркас встроен в Java 8 и более поздних версий. Эти классы вытеснять неприятные старые устаревшие классы даты и времени , такие как java.util.Date, Calendar, и SimpleDateFormat.

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

Чтобы узнать больше, смотрите Oracle Tutorial . И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310 .

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

Где взять классы java.time?

  • Java SE 8 , Java SE 9 , Java SE 10 и более поздние версии
    • Встроенный.
    • Часть стандартного Java API со встроенной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и Java SE 7
    • Большая часть функциональности java.time перенесена на Java 6 и 7 в ThreeTen-Backport .
  • Android
    • Более поздние версии Android связывают реализации классов java.time.
    • Для более ранних версий Android (<26) проект ThreeTenABP адаптирует ThreeTen-Backport (упомянутый выше). Смотрите Как использовать ThreeTenABP… .

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

Базилик Бурк
источник
Это намного лучшее улучшение по сравнению с другими вариантами (оно также намного новее и использует новые методы). Если вы посещаете эту проблему сейчас, это путь.
Shourya Bansal
5

Вместо того, чтобы писать свои собственные, addDaysкак предложено Эли, я бы предпочел использовать DateUtilsиз Apache . Это удобно, особенно когда вам нужно использовать его в разных местах вашего проекта.

API говорит:

addDays(Date date, int amount)

Добавляет количество дней к дате, возвращая новый объект.

Обратите внимание, что он возвращает новый Dateобъект и не вносит изменений в сам предыдущий.

Рисав Карна
источник
2

Это может быть легко сделано следующим

Calendar calendar = Calendar.getInstance();
        // from current time
        long curTimeInMills = new Date().getTime();
        long timeInMills = curTimeInMills - 5 * (24*60*60*1000);    // `enter code here`subtract like 5 days
        calendar.setTimeInMillis(timeInMills);
        System.out.println(calendar.getTime());

        // from specific time like (08 05 2015)
        calendar.set(Calendar.DAY_OF_MONTH, 8);
        calendar.set(Calendar.MONTH, (5-1));
        calendar.set(Calendar.YEAR, 2015);
        timeInMills = calendar.getTimeInMillis() - 5 * (24*60*60*1000);
        calendar.setTimeInMillis(timeInMills);
        System.out.println(calendar.getTime());
Rab
источник
Этот ответ опрометчив, используя ужасные старые классы даты и времени, которые теперь унаследованы, вытеснены классами java.time. Кроме того, этот код игнорирует критическую проблему часового пояса, наивно предполагая 24-часовые дни. Другая проблема заключается в использовании класса «дата-время» для решения проблемы, связанной только с датой. Использование LocalDateгораздо проще и целесообразнее.
Василий Бурк
1

Кто-то так рекомендовал Joda Time - я использую этот класс CalendarDate http://calendardate.sourceforge.net

Это несколько конкурирующий проект с Joda Time, но гораздо более базовый только в 2 классах. Это очень удобно и отлично работает для того, что мне нужно, так как я не хотел использовать пакет больше, чем мой проект. В отличие от аналогов Java, его наименьшая единица - день, поэтому это действительно дата (без указания миллисекунд или чего-то еще). После создания даты все, что вы делаете для вычитания, это что-то вроде myDay.addDays (-5), чтобы вернуться на 5 дней назад. Вы можете использовать его, чтобы найти день недели и тому подобное. Другой пример:

CalendarDate someDay = new CalendarDate(2011, 10, 27);
CalendarDate someLaterDay = today.addDays(77);

И:

//print 4 previous days of the week and today
String dayLabel = "";
CalendarDate today = new CalendarDate(TimeZone.getDefault());
CalendarDateFormat cdf = new CalendarDateFormat("EEE");//day of the week like "Mon"
CalendarDate currDay = today.addDays(-4);
while(!currDay.isAfter(today)) {
    dayLabel = cdf.format(currDay);
    if (currDay.equals(today))
        dayLabel = "Today";//print "Today" instead of the weekday name
    System.out.println(dayLabel);
    currDay = currDay.addDays(1);//go to next day
}
mikato
источник
1

Я считаю, что с помощью java.time.Instant можно добиться чистого и приятного способа вычитания или сложения любой единицы времени (месяцы, дни, часы, минуты, секунды, ...). класса .

Пример для вычитания 5 дней из текущего времени и получения результата в виде даты:

new Date(Instant.now().minus(5, ChronoUnit.DAYS).toEpochMilli());

Еще один пример для вычитания 1 часа и добавления 15 минут:

Date.from(Instant.now().minus(Duration.ofHours(1)).plus(Duration.ofMinutes(15)));

Если вам нужно больше точности, Instance измеряет до наносекунд. Методы манипулирования наносекундной частью:

minusNano()
plusNano()
getNano()

Также имейте в виду, что Дата не так точна, как Мгновенная.

Йордан Боев
источник
1
Или, в зависимости от вкуса, немного короче:Date.from(Instant.now().minus(5, ChronoUnit.DAYS))
Оле В.В.
1
Ницца! Ты прав. Я фактически обновил ответ, чтобы использовать это, а также показать использование класса java.time.Duration, который в этом случае также является очень мощным.
Йордан Боев
0

Второе решение Эли Кортрайт неверно, оно должно быть:

Calendar c = Calendar.getInstance();
c.setTime(date);
c.add(Calendar.DATE, -days);
date.setTime(c.getTime().getTime());
user178973
источник
2
Имя функции в решении Илиева является addDays; его решение верное. Если вы хотите вычесть дни из даты, вы передаете отрицательное значение для days.
Томас Аптон
Ну, это то, что программист должен указать при создании функции, нет? : P
user178973