diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/HardcodedContracts.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/HardcodedContracts.java
index 498e956ca595..0aaf0d401c5c 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/HardcodedContracts.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/HardcodedContracts.java
@@ -21,8 +21,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.intellij.codeInspection.dataFlow.ContractReturnValue.*;
-import static com.intellij.codeInspection.dataFlow.MethodContract.singleConditionContract;
-import static com.intellij.codeInspection.dataFlow.MethodContract.trivialContract;
+import static com.intellij.codeInspection.dataFlow.MethodContract.*;
import static com.intellij.codeInspection.dataFlow.StandardMethodContract.ValueConstraint.*;
import static com.intellij.codeInspection.dataFlow.StandardMethodContract.createConstraintArray;
import static com.intellij.psi.CommonClassNames.*;
@@ -184,16 +183,31 @@ public final class HardcodedContracts {
.register(enumValues(), ContractProvider.of(StandardMethodContract.fromText("->new")))
.register(staticCall("java.lang.System", "arraycopy"), expression -> getArraycopyContract())
.register(anyOf(
- instanceCall("java.util.Date", "before", "after"),
- instanceCall("java.time.LocalDate", "isBefore", "isAfter"),
- instanceCall("java.time.LocalDateTime", "isBefore", "isAfter"),
- instanceCall("java.time.LocalTime", "isBefore", "isAfter"),
- instanceCall("java.time.ZonedDateTime", "isBefore", "isAfter"),
- instanceCall("java.time.Year", "isBefore", "isAfter"),
- instanceCall("java.time.YearMonth", "isBefore", "isAfter")
- ), ContractProvider.of(
- singleConditionContract(ContractValue.qualifier(), RelationType.EQ, ContractValue.argument(0), returnFalse())
- ))
+ instanceCall(JAVA_TIME_LOCAL_DATE, "isAfter"),
+ instanceCall(JAVA_TIME_LOCAL_TIME, "isAfter"),
+ instanceCall(JAVA_TIME_OFFSET_TIME, "isAfter"),
+ instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "isAfter"),
+ instanceCall(JAVA_TIME_ZONED_DATE_TIME, "isAfter"),
+ instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "isAfter"),
+ instanceCall("java.util.Date", "after"),
+ instanceCall("java.time.Year", "isAfter"),
+ instanceCall("java.time.YearMonth", "isAfter")),
+ ContractProvider.of(
+ singleConditionContract(ContractValue.qualifier(), RelationType.GT, ContractValue.argument(0), returnBoolean(true)),
+ trivialContract(returnBoolean(false))))
+ .register(anyOf(
+ instanceCall(JAVA_TIME_LOCAL_DATE, "isBefore"),
+ instanceCall(JAVA_TIME_LOCAL_TIME, "isBefore"),
+ instanceCall(JAVA_TIME_OFFSET_TIME, "isBefore"),
+ instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "isBefore"),
+ instanceCall(JAVA_TIME_ZONED_DATE_TIME, "isBefore"),
+ instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "isBefore"),
+ instanceCall("java.util.Date", "before"),
+ instanceCall("java.time.Year", "isBefore"),
+ instanceCall("java.time.YearMonth", "isBefore")),
+ ContractProvider.of(
+ singleConditionContract(ContractValue.qualifier(), RelationType.LT, ContractValue.argument(0), returnBoolean(true)),
+ trivialContract(returnBoolean(false))))
//for propagation CONSUMED_STREAM
.register(ConsumedStreamUtils.getSkipStreamMatchers(), ContractProvider.of(trivialContract(returnThis())))
.register(staticCall(JAVA_LANG_CHARACTER, "isSurrogate").parameterCount(1),
diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/DateTimeComparing.java b/java/java-tests/testData/inspection/dataFlow/fixture/DateTimeComparing.java
new file mode 100644
index 000000000000..c584d42bf546
--- /dev/null
+++ b/java/java-tests/testData/inspection/dataFlow/fixture/DateTimeComparing.java
@@ -0,0 +1,30 @@
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+
+public class DateTimeComparing {
+ public void testLocalDateTime() {
+ LocalDateTime now1 = LocalDateTime.now();
+ LocalDateTime now2 = LocalDateTime.now();
+ if (now1.isAfter(now2)) {
+ if (now1.isBefore(now2)) {
+
+ }
+ if (now2.isBefore(now1)) {
+ }
+ }
+ }
+
+ public void testLocalTime() {
+ LocalTime now1 = LocalTime.now();
+ LocalTime now2 = LocalTime.now();
+ if (now1.isAfter(now2)) {
+
+ if (now1.isBefore(now2)) {
+
+ }
+ if (now2.isBefore(now1)) {
+
+ }
+ }
+ }
+}
diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/HardcodedContractsTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/HardcodedContractsTest.java
index 08e25274a508..abad2c609fd6 100644
--- a/java/java-tests/testSrc/com/intellij/java/codeInspection/HardcodedContractsTest.java
+++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/HardcodedContractsTest.java
@@ -184,4 +184,29 @@ public class HardcodedContractsTest extends DataFlowInspectionTestCase {
}
public void testCharacterMethods() { checkHighlighting(); }
+
+ @SuppressWarnings("MethodOverloadsMethodOfSuperclass")
+ public void testDateTimeComparing() {
+ myFixture.addClass("""
+ package java.time.chrono;
+ public interface ChronoLocalDateTime { }""");
+ myFixture.addClass("""
+ package java.time;
+ import java.time.temporal.TemporalUnit;
+ public final class LocalTime {
+ public static LocalTime now() { return new LocalTime(); }
+ public boolean isBefore(LocalTime localTime) { return false; }
+ public boolean isAfter(LocalTime localTime) { return false; }
+ }""");
+ myFixture.addClass("""
+ package java.time;
+ import java.time.chrono.ChronoLocalDateTime;
+ import java.time.temporal.TemporalUnit;
+ public final class LocalDateTime implements ChronoLocalDateTime {
+ public static LocalDateTime now() { return new LocalDateTime(); }
+ public boolean isBefore(ChronoLocalDateTime localDateTime2) { return false; }
+ public boolean isAfter(ChronoLocalDateTime localDateTime2) { return false; }
+ }""");
+ checkHighlighting();
+ }
}