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:00and2023-03-01 10:00there are0full months, while there is literally a full calendar month between these dates. - There are
5months between2023-05-31 11:00and2023-12-01 10:00, but if we add6months to the first date, we get...2023-11-30 11:00, so older date than the one that is5months 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.