Hi buddies I'm in a trouble trying to migrate a behavior from calendar to localdate.
payDate.set(Calendar.DAY_OF_MONTH,payDay)
Lets imagine that payDate
had the current date, 2020-01-29
for business reasons payDay
can had the value of 0
, so, when the previous code line is executed with the previous scenario, the result is that payDate
update the date to 2019-12-31,
that is to say the the date back to the last day of the past month.
I'm not sure, the technical reason of this, if someone can explain to me this I'll be so thankful, I tried checking the java doc but it was not helpful.
So I need to replicate that behavior with LocalDate java library. From my point of view; the similar of set
method from Calendar
with the value of DAY_OF_MONTH
in LocalDate is:
payDate.withDayOfMonth(payDay)
But when the below scenario is presented and payDay
is equal to 0
I get an error:
java.time.DateTimeException: Invalid value for DayOfMonth (valid values 1 - 28/31): 0
Also I had some ideas about how can I get the same result of calendar in localDate when the rule comes on (if payDay is 0, return to the last day of previous month), but are too verbose.
If you know a similar behavior on LocalDate please help me. Thanks.
TL;DR: Use payDate = payDate.plusDays(payDay - payDate.getDayOfMonth());
The behavior of Calendar
you're describing is documented in the javadoc:
Leniency
Calendar
has two modes for interpreting the calendar fields, lenient and non-lenient. When aCalendar
is in lenient mode, it accepts a wider range of calendar field values than it produces. When aCalendar
recomputes calendar field values for return byget()
, all of the calendar fields are normalized. For example, a lenientGregorianCalendar
interpretsMONTH == JANUARY
,DAY_OF_MONTH == 32
as February 1.When a
Calendar
is in non-lenient mode, it throws an exception if there is any inconsistency in its calendar fields. For example, aGregorianCalendar
always producesDAY_OF_MONTH
values between 1 and the length of the month. A non-lenientGregorianCalendar
throws an exception upon calculating its time or calendar field values if any out-of-range field value has been set.
To show the effect of this, try setting the date of a Calendar
to January 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()));
Output
2020-03-10
You would get the same result if you did:
cal.set(2020, Calendar.JANUARY, 1);
cal.add(Calendar.DAY_OF_MONTH, 69);
LocalDate
is always non-lenient, so you can't set the day-of-month value to a value that is out-of-range. You can however get the same result as what Calendar
does, by changing the operation to "add" instead of "set".
So, if you have a particular date, e.g. the 2020-01-29
date mentioned in the question, and you want to "set" the day-of-month value to 70 or 0, with same lenient overflow logic as Calendar
has, do this:
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);
Output
2020-03-10
2019-12-31
As you can see, date.plusDays(dayToSet - date.getDayOfMonth())
will give you the desired result.