broot
broot

Reputation: 28452

ChronoUnit.MONTHS: inconsistency between addTo() and between()

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:

Upvotes: 3

Views: 124

Answers (1)

Arvind Kumar Avinash
Arvind Kumar Avinash

Reputation: 79580

It's not a bug. It is documented as follows:

The calculation returns a whole number, representing the number of complete units between the two temporals. For example, the amount in hours between the times 11:30 and 13:29 will only be one hour as it is one minute short of two hours.

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.

var d1 = OffsetDateTime.parse("2023-01-31T10:00:00Z");
var d2 = ChronoUnit.MONTHS.addTo(d1, 1);
System.out.println(ChronoUnit.MONTHS.between(d1, d2)); // 0

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.

d2 = ChronoUnit.HOURS.addTo(d2, 23); // 2023-03-01T09:00Z

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.

Upvotes: 0

Related Questions