LocalDate.plusDays() is relatively slow for the common case. As this method is widely used (by minusDays, plusWeeks, minusWeeks and matching methods on other classes such as LocalDateTime and ZonedDateTime) this change will have wide ripple through benefits.
The common case for adding/subtracting days on a date is adding/subtracting 1 day. In the codebase I work on this accounts for about 70% of all uses of the method. Less than 3% was additions/subtractions of over 30 days. The current logic in plusDays() does not distinguish the common case.
Benchmarking has been performed here:
https://github.com/ThreeTen/threeten-bench/blob/master/src/main/java/org/threeten/LocalDateBenchmark.java (non-OpenJDK server but any necessary rights hereby granted to OpenJDK)
Based on trying 5 different approaches, propose adding option 3 to LocalDate in OpenJDK. The existing implementation of LocalDate.plusDays() would be replaced as follows:
public LocalDate plusDays(long daysToAdd) {
if (daysToAdd == 0) {
return this;
}
long dom = day + daysToAdd;
if (dom > 0 && dom <= 59) { // 59th Jan is 28th Feb, 59th Feb is 31st Mar
int monthLen = input.lengthOfMonth();
if (dom <= monthLen) {
return LocalDate.of(year, month, (int) dom);
} else if (month < 12) {
return LocalDate.of(year, month + 1, (int) (dom - monthLen));
} else {
return LocalDate.of(year + 1, 1, (int) (dom - monthLen));
}
}
long mjDay = Math.addExact(toEpochDay(), daysToAdd);
return LocalDate.ofEpochDay(mjDay);
}
This option is a reasonable balance of higher performance and simplicity, handling where the date moves to later in the current month or most dates in the next month. This also handles subtraction within the current month.
This code is roughly 3.7 times faster than the current code. In the non-optimized fallback case, there is very little negative impact (not measurable).
Given how core the method is and the widespread impact of this one change, backporting to JDK 8 appears worthwhile.
Existing test cases cover this method OK, however some additional testing would be worthwhile, particular to cover the additional edge cases.
The common case for adding/subtracting days on a date is adding/subtracting 1 day. In the codebase I work on this accounts for about 70% of all uses of the method. Less than 3% was additions/subtractions of over 30 days. The current logic in plusDays() does not distinguish the common case.
Benchmarking has been performed here:
https://github.com/ThreeTen/threeten-bench/blob/master/src/main/java/org/threeten/LocalDateBenchmark.java (non-OpenJDK server but any necessary rights hereby granted to OpenJDK)
Based on trying 5 different approaches, propose adding option 3 to LocalDate in OpenJDK. The existing implementation of LocalDate.plusDays() would be replaced as follows:
public LocalDate plusDays(long daysToAdd) {
if (daysToAdd == 0) {
return this;
}
long dom = day + daysToAdd;
if (dom > 0 && dom <= 59) { // 59th Jan is 28th Feb, 59th Feb is 31st Mar
int monthLen = input.lengthOfMonth();
if (dom <= monthLen) {
return LocalDate.of(year, month, (int) dom);
} else if (month < 12) {
return LocalDate.of(year, month + 1, (int) (dom - monthLen));
} else {
return LocalDate.of(year + 1, 1, (int) (dom - monthLen));
}
}
long mjDay = Math.addExact(toEpochDay(), daysToAdd);
return LocalDate.ofEpochDay(mjDay);
}
This option is a reasonable balance of higher performance and simplicity, handling where the date moves to later in the current month or most dates in the next month. This also handles subtraction within the current month.
This code is roughly 3.7 times faster than the current code. In the non-optimized fallback case, there is very little negative impact (not measurable).
Given how core the method is and the widespread impact of this one change, backporting to JDK 8 appears worthwhile.
Existing test cases cover this method OK, however some additional testing would be worthwhile, particular to cover the additional edge cases.