In some cases, ChronoUnit.MONTHS
perform math on dates inconsistently depending if we add or subtract. Corner case is when we e.g. add 1 month to a date 2023-01-31
. I understand there is no single, correct answer if the result should be 2023-02-28
or 2023-03-01
. However, I would expect the same API to at least use the same assumptions, which is not the case here:
var d1 = OffsetDateTime.parse("2023-01-31T10:00:00Z");
// add 1 month
var d2 = ChronoUnit.MONTHS.addTo(d1, 1);
// add 23 hours
d2 = ChronoUnit.HOURS.addTo(d2, 23);
// subtract
System.out.println(ChronoUnit.MONTHS.between(d1, d2)); // 0
Apparently, addTo()
assumes 1 month from 2023-01-31 10:00
is 2023-02-28 10:00
, however, between()
assumes one full month ends at 2023-03-01 10:00
.
Is this a bug? Do I miss or misunderstand something?
BTW, d1.plusMonths(1)
works in the same way as addTo()
and this behavior is even clearly documented. between()
seems to be inconsistent with other operations.
EDIT:
I understand the date math is tricky, it isn't exactly "math", calendar dates don't flow linearly and we can't treat them as adding/subtracting just numbers. But still, I find some examples extremely counter-intuitive:
- Between
2023-01-31 11:00
and2023-03-01 10:00
there are0
full months, while there is literally a full calendar month between these dates. - There are
5
months between2023-05-31 11:00
and2023-12-01 10:00
, but if we add6
months to the first date, we get...2023-11-30 11:00
, so older date than the one that is5
months away.
It's not a bug. It is documented as follows:
In the following code, d2 becomes 2023-02-28T10:00Z but when it calculates the difference as per the above documentation, it would have counted the difference as 1 if a date such as 2023-02-31T10:00:00Z existed. If not, it counts the difference as 1 for the next day which is 2023-03-01T10:00:00Z.
When you add 23 hours in the next statement, it becomes 2023-03-01T09:00Z which is one hour less than 2023-03-01T10:00:00Z; therefore it does not reach the point to count the difference as 1 month.
Had you added 24 hours, it would have become 2023-03-01T10:00:00Z and given you the difference as 1.
This behaviour is consistent across all the time units.