Поведение календаря не совместимо с LocalDate? - PullRequest
0 голосов
/ 29 января 2020

Привет, друзья! У меня проблемы с попыткой перенести поведение из календаря в localdate.

payDate.set(Calendar.DAY_OF_MONTH,payDay)

Предположим, что у payDate была текущая дата, 2020-01-29

по деловым причинам payDay может иметь значение 0, поэтому, когда предыдущая строка кода выполняется с предыдущим сценарием, в результате payDate обновляет дату до 2019-12-31, то есть сказать дату назад к последнему дню прошлого месяца.

Я не уверен, техническая причина этого, если кто-то может объяснить мне это, я буду так благодарен, я попытался проверить java do c, но это не помогло.

Так что мне нужно повторить это поведение с библиотекой LocalDate java. С моей точки зрения; аналог метода set из Calendar со значением DAY_OF_MONTH в LocalDate:

payDate.withDayOfMonth(payDay)

Но когда представлен нижеприведенный сценарий и payDay равен 0, я получаю ошибка:

java.time.DateTimeException: Invalid value for DayOfMonth (valid values 1 - 28/31): 0

Также у меня были некоторые идеи о том, как можно получить тот же результат календаря в localDate при включении правила (если payDay равен 0, вернуться к последнему дню предыдущего месяца), но слишком многословны.

Если вы знаете подобное поведение в LocalDate, пожалуйста, помогите мне. Спасибо.

Ответы [ 3 ]

4 голосов
/ 29 января 2020

TL; DR: Использование payDate = payDate.plusDays(payDay - payDate.getDayOfMonth());


Поведение Calendar, которое вы описываете, задокументировано в javado c:

Лентность

Calendar имеет два режима для интерпретации полей календаря: снисходительный и без снисходительны . Когда Calendar находится в мягком режиме, он принимает более широкий диапазон значений календарного поля, чем он производит. Когда Calendar пересчитывает значения полей календаря для возврата на get(), все поля календаря нормализуются. Например, снисходительный GregorianCalendar интерпретирует MONTH == JANUARY, DAY_OF_MONTH == 32 как 1 февраля.

Когда Calendar находится в не снисходительном режиме, он генерирует исключение, если в его календаре есть несоответствия поля. Например, GregorianCalendar всегда выдает DAY_OF_MONTH значения между 1 и продолжительностью месяца. Не снисходительный GregorianCalendar выдает исключение при вычислении его значений поля времени или календаря, если было установлено какое-либо значение поля вне диапазона.

Чтобы показать эффект этого, попробуйте установить дата от Calendar до 70 января 2020 года:

Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(2020, Calendar.JANUARY, 70);
System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(cal.getTime()));

Вывод

2020-03-10

Вы бы получили тот же результат, если бы сделали:

cal.set(2020, Calendar.JANUARY, 1);
cal.add(Calendar.DAY_OF_MONTH, 69);

LocalDate всегда не снижен , поэтому вы не можете установить значение дня месяца равным из-диапазона. Однако вы можете получить тот же результат, что и Calendar, изменив операцию «добавить» вместо «установить».

Итак, если у вас есть конкретная дата, например, указанная дата 2020-01-29 в вопросе, и вы хотите "установить" значение дня месяца равным 70 или 0, с тем же снисходительным переполнением logi c, как Calendar, сделайте это:

LocalDate date = LocalDate.parse("2020-01-29");
date = date.plusDays(70 - date.getDayOfMonth());
System.out.println(date);
LocalDate date = LocalDate.parse("2020-01-29");
date = date.plusDays(0 - date.getDayOfMonth());
System.out.println(date);

Выход

2020-03-10
2019-12-31

Как видите, date.plusDays(dayToSet - date.getDayOfMonth()) даст вам желаемый результат.

2 голосов
/ 29 января 2020

Вот как я бы go об этом:

    LocalDate payDate = LocalDate.now(); // or whatever
    int payDay = 0;
    if (payDay == 0) {
        // simulate `GregorianCalendar` behaviour: day 0 is the day before day 1
        payDate = payDate.withDayOfMonth(1).minusDays(1);
    } else {
        payDate = payDate.withDayOfMonth(payDay);
    }
    System.out.println(payDate);

Когда я только что запустил сниппет, на выходе была дата, которую вы уже упомянули:

2019- 12-31

Если бы мы хотели, чтобы оно было короче, мы могли бы использовать payDate.withDayOfMonth(1).minusDays(1).plusDays(payDay) или трюк из ответа Андреаса, и нам не понадобился бы оператор if. Я бы не стал, однако. (1) Труднее читать. (2) Он не дает валидации payDay, которая предоставляется бесплатно в приведенном выше фрагменте.

Смущающее поведение Calendar происходит из-за отсутствия проверки диапазона аргумента set(). Таким образом, день 0 месяца - это день, предшествующий дню 1 месяца. День -1 будет днем ​​раньше и так далее. Это фрагмент кода из документации (или должен был быть, по крайней мере):

Когда Календарь находится в мягком режиме, он принимает более широкий диапазон значений поля календаря, чем создает. Когда календарь пересчитывает значения полей календаря для возврата на get(), все поля календаря нормализуются. Например, кредитор GregorianCalendar интерпретирует MONTH == JANUARY, DAY_OF_MONTH == 32 как 1 февраля.

Вы можете прочитать его с помощью этого фрагмента из документации метода setLenient:

По умолчанию снисходительно.

Ссылки

1 голос
/ 29 января 2020

Вы не сможете просто вызвать один метод для достижения тех же результатов. Если вы уверены , что установка DAY_OF_MONTH на 0 должна привести к его откату на один месяц (это тот тип вещей, который я бы проверил у бизнес-аналитика или владельца продукта для проверки работоспособности), тогда вы ' Вам придется сделать что-то вроде этого:

    int payDay = 0;
    LocalDate payDate = LocalDate.of(2020, Month.JANUARY, 29);

    if(payDay == 0) {
        payDate = payDate.minusMonths(1);
        payDay = payDate.lengthOfMonth();
    }

    payDate = payDate.withDayOfMonth(payDay);

Другой подход:

   int payDay = 0;
   LocalDate payDate = LocalDate.of(2020, Month.JANUARY, 29);
   if(payDay == 0) {
      payDate = payDate.withDayOfMonth(1).minusDays(1);
   } else {
      payDate = payDate.withDayOfMonth(payDay);
   }
...