PlusDays: примеры (JAVA)

Метод plusDays в java.time: назначение и особенности
Раздел: Время и дата (Date & Time API)
plusDays(long daysToAdd): LocalDate

Краткое описание

Метод plusDays добавляет к дате или к типу даты-времени указанное количество дней и возвращает новый объект с результатом. В Java реализация встречается в нескольких классах и интерфейсах пакета java.time: в LocalDate, LocalDateTime, ZonedDateTime, OffsetDateTime и у некоторых других типов. Также близкая по смыслу операция доступна через Period (для представления интервала) и через универсальные методы с указанием единицы времени (plus(long, TemporalUnit)).

Метод неизменяемый: исходный объект не меняет состояние, возвращается новый экземпляр с изменённой датой.

Аргументы и возвращаемые значения

  • Аргумент: как правило один числовой параметр - количество дней. В LocalDate и подобных это long daysToAdd. В Period методы прибавления принимают int или используются фабричные методы.
  • Значения: возвращается новый экземпляр того же типа (например, LocalDate для LocalDate.plusDays), где к исходной дате добавлено указанное количество дней. При отрицательном аргументе происходит вычитание дней.

Поведение и ограничения

  • Обработка смены месяца и года автоматическая: при переходе через границы месяцев и года результат корректно сдвигается.
  • Высокосные года учитываются: добавление 1 дня к 2020-02-28 даёт 2020-02-29.
  • Для типов с часовым поясом (ZonedDateTime) при переходе через летнее/зимнее время может измениться смещение (offset). Добавление суток понимается как сдвиг календарной даты, а не как фиксированные 24 часа.
  • При превышении допустимого диапазона дат метод выбрасывает исключение (как правило DateTimeException или ArithmeticException при переполнении арифметики).

Исключения

  • DateTimeException при выходе за поддерживаемый диапазон дат.
  • ArithmeticException при переполнении арифметики при больших значениях.

Примеры использования

Короткие примеры для различных типов. Каждый пример содержит код и результат.

LocalDate: базовая операция и высокосный год

java.time.LocalDate d = java.time.LocalDate.of(2020, 2, 28);
java.time.LocalDate r = d.plusDays(1);
System.out.println(r);
2020-02-29

LocalDate с отрицательным значением

java.time.LocalDate d2 = java.time.LocalDate.of(2021, 3, 1);
System.out.println(d2.plusDays(-1));
2021-02-28

LocalDateTime: время сохраняется

java.time.LocalDateTime dt = java.time.LocalDateTime.of(2021, 12, 31, 23, 0);
systemOut(dt.plusDays(1));
// утилита вывода
static void systemOut(java.time.LocalDateTime x){ System.out.println(x); }
2022-01-01T23:00

ZonedDateTime и переход на летнее время

java.time.ZonedDateTime z = java.time.ZonedDateTime.of(
    2021, 3, 27, 12, 0, 0, 0, java.time.ZoneId.of("Europe/Berlin")
);
System.out.println(z);
System.out.println(z.plusDays(1));
2021-03-27T12:00+01:00[Europe/Berlin]
2021-03-28T12:00+02:00[Europe/Berlin]

Period: создание интервала и прибавление дней к интервалу

java.time.Period p = java.time.Period.ofMonths(1);
java.time.Period p2 = p.plusDays(10);
System.out.println(p2);
P1M10D

LocalDate с Period

java.time.LocalDate d3 = java.time.LocalDate.of(2021, 1, 31);
java.time.LocalDate res = d3.plus(java.time.Period.ofMonths(1)).plusDays(1);
System.out.println(res);
2021-03-01

Похожие методы в Java

  • plus(long, TemporalUnit) - универсальный метод для добавления любой единицы времени, например date.plus(5, java.time.temporal.ChronoUnit.DAYS). Предпочтение можно отдать при генерализации кода или когда единица времени определяется динамически.
  • plusWeeks/plusMonths/plusYears - более семантически точные методы для добавления недель, месяцев или лет. При добавлении недель используются недели как группы по 7 дней.
  • Calendar.add (старый API) - добавление дней через Calendar.add(Calendar.DAY_OF_MONTH, n). Старый API мутирует объект и требует осторожности при многопоточной работе.
  • Instant/Duration - для представлений в виде наносекунд/секунд. Если важны точные 24-часовые интервалы, лучше использовать Duration.ofHours(24) и instant.plus(duration) вместо добавления календарных дней.

Аналоги в других языках и отличия

  • JavaScript (классический Date)
  • const d = new Date('2021-03-27T12:00:00Z');
    d.setUTCDate(d.getUTCDate() + 1);
    console.log(d.toISOString());
    2021-03-28T12:00:00.000Z

    Отличие: объект Date является мутабельным, операции часто изменяют исходный объект. Также нужно учитывать часовой пояс и методы UTC vs локальные.

  • JavaScript (Temporal API, современно)
  • // если доступен
    const pd = new Temporal.PlainDate(2021, 3, 27);
    console.log(pd.plus({ days: 1 }).toString());
    2021-03-28
  • Python
  • from datetime import date, timedelta
    print(date(2020,2,28) + timedelta(days=1))
    2020-02-29

    Отличие: в Python операция с timedelta похожа по семантике; объекты дат также неизменяемы и возвращается новый объект.

  • PHP
  • $d = new DateTime('2021-03-27');
    $d->modify('+1 day');
    echo $d->format('Y-m-d');
    2021-03-28

    Отличие: DateTime в PHP по умолчанию мутирует объект при вызове modify, но можно клонировать.

  • SQL
  • -- MySQL
    SELECT DATE_ADD('2021-03-27', INTERVAL 1 DAY);
    -- SQL Server
    SELECT DATEADD(day, 1, '2021-03-27');
    2021-03-28
    2021-03-28
  • C#
  • var d = new DateTime(2021,3,27);
    Console.WriteLine(d.AddDays(1).ToString("yyyy-MM-dd"));
    2021-03-28

    Отличие: API C# схож по семантике с Java Time - объекты DateTime неизменяемы, возвращается новый объект.

  • Go
  • t := time.Date(2021, 3, 27, 12, 0, 0, 0, time.UTC)
    fmt.Println(t.AddDate(0, 0, 1))
    2021-03-28 12:00:00 +0000 UTC
  • Kotlin
  • val d = java.time.LocalDate.of(2021,3,27)
    println(d.plusDays(1))
    2021-03-28

    Отличие: Kotlin использует тот же Java Time API на JVM, методы идентичны.

  • Lua
  • -- пример с использованием os.time/os.date
    local t = {year=2021, month=3, day=27, hour=12}
    local sec = os.time(t) + 24*60*60
    print(os.date('%Y-%m-%d', sec))
    2021-03-28

    Отличие: в Lua нет встроенного богатого календарного API, используется смещение в секундах; это не всегда равно добавлению календарного дня при переходе через летнее время.

Типичные ошибки и примеры

  • Ожидание мутации объекта: java.time объекты неизменяемы, поэтому вызов d.plusDays(1) без присвоения не изменит исходную переменную.
    java.time.LocalDate d = java.time.LocalDate.of(2021, 1, 1);
    d.plusDays(1);
    System.out.println(d);
    2021-01-01

    Правильное использование: d = d.plusDays(1) или сохранять результат в другую переменную.

  • Путаница между «днём календаря» и «24 часами»:
    java.time.ZonedDateTime z = java.time.ZonedDateTime.of(
      2021,3,27,12,0,0,0, java.time.ZoneId.of("Europe/Berlin")
    );
    System.out.println(z.plusDays(1));
    System.out.println(z.plus(java.time.Duration.ofHours(24)));
    2021-03-28T12:00+02:00[Europe/Berlin]
    2021-03-28T13:00+02:00[Europe/Berlin]

    Добавление календарного дня сохраняет локальное время, а добавление Duration в секундах/часах учитывает переходы и может дать другой результат по времени.

  • Выход за диапазон дат:
    try {
      java.time.LocalDate dmax = java.time.LocalDate.MAX;
      System.out.println(dmax.plusDays(1));
    } catch (Exception e) {
      e.printStackTrace();
    }
    java.time.DateTimeException: Invalid date after addition
        at ...

    При операции, приводящей к дате вне допустимого диапазона, генерируется исключение. Следует предусмотреть обработку или валидацию входных значений.

  • Передача null: если метод вызывается у null-ссылки, произойдёт NullPointerException. Например, LocalDate d = null; d.plusDays(1); вызовет NPE.

Изменения и история

Метод plusDays введён в рамках Java 8 как часть нового пакета java.time. С тех пор явных изменений в семантике метода не произошло: он остаётся неизменяемым, корректно обрабатывает переходы календаря и летнее/зимнее время для типов с часовыми поясами. Дополнительные утилиты и методы вокруг времени постепенно добавлялись в последующих версиях Java, но поведение plusDays совместимо назад.

Расширенные и редкие варианты использования

Несколько продвинутых сценариев с объяснениями.

Комбинация Period и Duration для сложных сдвигов

Пример java
java.time.ZonedDateTime start = java.time.ZonedDateTime.of(
  2021, 10, 30, 1, 30, 0, 0, java.time.ZoneId.of("Europe/Berlin")
);
// добавить 1 календарный день и 36 часов
java.time.ZonedDateTime r = start.plus(java.time.Period.ofDays(1)).plus(java.time.Duration.ofHours(36));
System.out.println(start);
System.out.println(r);
2021-10-30T01:30+02:00[Europe/Berlin]
2021-11-01T13:30+01:00[Europe/Berlin]

Пояснение: при комбинировании календарных и фиксированных интервалов учитываются переходы смещений и суммируются шаги отдельно.

Использование с нестандартной хронологией (не-ISO)

Пример java
java.time.chrono.HijrahDate hd = java.time.chrono.HijrahChronology.INSTANCE.dateNow();
java.time.chrono.HijrahDate hd2 = hd.plus(java.time.temporal.ChronoUnit.DAYS.getDuration().toDays(), java.time.temporal.ChronoUnit.DAYS);
// рекомендуется использовать типизированный plus с ChronoUnit
System.out.println(hd);
System.out.println(hd.plus(10, java.time.temporal.ChronoUnit.DAYS));
(пример вывода зависит от текущей даты в хиджре)
(дата +10 дней в хиджрском календаре)

Пояснение: в нестандартных календарях добавление дней ведёт себя согласно правилам этой хроники, поэтому результаты отличаются от эквивалентных ISO-операций.

Атомарная проверка валидности перед добавлением

Пример java
java.time.LocalDate safeAdd(java.time.LocalDate d, long days){
  // проверка переполнения по диапазону
  long maxAdd = java.time.LocalDate.MAX.toEpochDay() - d.toEpochDay();
  long minAdd = java.time.LocalDate.MIN.toEpochDay() - d.toEpochDay();
  if (days > maxAdd || days < minAdd) throw new IllegalArgumentException("out of range");
  return d.plusDays(days);
}
// использование
System.out.println(safeAdd(java.time.LocalDate.of(9999,12,31), -1));
9999-12-30

Пояснение: вычисление в днях через toEpochDay() позволяет заранее проверить выход за диапазон.

Отличие при добавлении 24 часов и одного календарного дня

Пример java
java.time.ZonedDateTime z0 = java.time.ZonedDateTime.of(2021,3,27,0,30,0,0, java.time.ZoneId.of("Europe/Berlin"));
System.out.println(z0.plusDays(1));
System.out.println(z0.plus(java.time.Duration.ofHours(24)));
2021-03-28T00:30+02:00[Europe/Berlin]
2021-03-28T01:30+02:00[Europe/Berlin]

Пояснение: переход на летнее время сдвинул часы, поэтому фиксированное количество часов даёт другой локальный момент по сравнению с календарной операцией.

Обработка больших интервалов и поточность

Пример java
// пример, когда требуется прибавлять много дней в цикле, лучше использовать арифметику с epoch
java.time.LocalDate base = java.time.LocalDate.of(2000,1,1);
long daysToAdd = 10_000_000L;
java.time.LocalDate result = java.time.LocalDate.ofEpochDay(base.toEpochDay() + daysToAdd);
System.out.println(result);
(результат в зависимости от вычисления, возможно выброс исключения при выходе за диапазон)

Пояснение: при экстремально больших значениях стоит учитывать производительность и возможность переполнения; работа через epoch дни даёт контроль и возможность ранней проверки.

джава plusDays function comments

En
PlusDays Returns a copy of this LocalDate with the specified number of days added