From 6303fd0ebbae184d3005eb19c17627003a571135 Mon Sep 17 00:00:00 2001 From: zhaomeng Date: Thu, 4 Jun 2026 10:16:06 +0800 Subject: [PATCH 1/2] Fix CronTrigger equals/hashCode to include zoneId CronTrigger carries an optional ZoneId since 5.3 that affects nextExecution, but equals and hashCode only considered the cron expression. Include zoneId in both methods. Signed-off-by: zhaomeng --- .../scheduling/support/CronTrigger.java | 6 ++++-- .../scheduling/support/CronTriggerTests.java | 11 +++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/support/CronTrigger.java b/spring-context/src/main/java/org/springframework/scheduling/support/CronTrigger.java index 5a055ce66ff6..84e34181a499 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/support/CronTrigger.java +++ b/spring-context/src/main/java/org/springframework/scheduling/support/CronTrigger.java @@ -26,6 +26,7 @@ import org.springframework.scheduling.Trigger; import org.springframework.scheduling.TriggerContext; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * {@link Trigger} implementation for cron expressions. Wraps a @@ -145,12 +146,13 @@ Instant determineInitialTimestamp(TriggerContext triggerContext) { @Override public boolean equals(@Nullable Object other) { return (this == other || (other instanceof CronTrigger that && - this.expression.equals(that.expression))); + this.expression.equals(that.expression) && + ObjectUtils.nullSafeEquals(this.zoneId, that.zoneId))); } @Override public int hashCode() { - return this.expression.hashCode(); + return ObjectUtils.nullSafeHash(this.expression, this.zoneId); } @Override diff --git a/spring-context/src/test/java/org/springframework/scheduling/support/CronTriggerTests.java b/spring-context/src/test/java/org/springframework/scheduling/support/CronTriggerTests.java index 2de519954daa..1fb4495a3371 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/support/CronTriggerTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/support/CronTriggerTests.java @@ -16,6 +16,7 @@ package org.springframework.scheduling.support; +import java.time.ZoneId; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; @@ -746,6 +747,16 @@ void daylightSavingMissingHour() { assertThat(nextExecutionTime).isEqualTo(this.calendar.getTime()); } + @Test + void equalsAndHashCodeConsidersZoneId() { + String cron = "0 0 9 * * *"; + CronTrigger amsterdam = new CronTrigger(cron, ZoneId.of("Europe/Amsterdam")); + CronTrigger newYork = new CronTrigger(cron, ZoneId.of("America/New_York")); + + assertThat(amsterdam).isNotEqualTo(newYork); + assertThat(amsterdam).doesNotHaveSameHashCodeAs(newYork); + } + private static void roundup(Calendar calendar) { calendar.add(Calendar.SECOND, 1); From 45a013e14f1310754422854d0b925d778a71e686 Mon Sep 17 00:00:00 2001 From: zhaomeng Date: Thu, 4 Jun 2026 17:13:30 +0800 Subject: [PATCH 2/2] Fix CronTrigger equals/hashCode to include zoneId Ensure CronTrigger instances with the same cron expression but different time zones are no longer considered equal. Signed-off-by: zhaomeng Co-authored-by: Cursor --- .../org/springframework/scheduling/support/CronTrigger.java | 6 +++--- .../scheduling/support/CronTriggerTests.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/support/CronTrigger.java b/spring-context/src/main/java/org/springframework/scheduling/support/CronTrigger.java index 84e34181a499..e596379f9608 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/support/CronTrigger.java +++ b/spring-context/src/main/java/org/springframework/scheduling/support/CronTrigger.java @@ -19,6 +19,7 @@ import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.util.Objects; import java.util.TimeZone; import org.jspecify.annotations.Nullable; @@ -26,7 +27,6 @@ import org.springframework.scheduling.Trigger; import org.springframework.scheduling.TriggerContext; import org.springframework.util.Assert; -import org.springframework.util.ObjectUtils; /** * {@link Trigger} implementation for cron expressions. Wraps a @@ -147,12 +147,12 @@ Instant determineInitialTimestamp(TriggerContext triggerContext) { public boolean equals(@Nullable Object other) { return (this == other || (other instanceof CronTrigger that && this.expression.equals(that.expression) && - ObjectUtils.nullSafeEquals(this.zoneId, that.zoneId))); + Objects.equals(this.zoneId, that.zoneId))); } @Override public int hashCode() { - return ObjectUtils.nullSafeHash(this.expression, this.zoneId); + return Objects.hash(this.expression, this.zoneId); } @Override diff --git a/spring-context/src/test/java/org/springframework/scheduling/support/CronTriggerTests.java b/spring-context/src/test/java/org/springframework/scheduling/support/CronTriggerTests.java index 1fb4495a3371..e773770b2f0c 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/support/CronTriggerTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/support/CronTriggerTests.java @@ -748,7 +748,7 @@ void daylightSavingMissingHour() { } @Test - void equalsAndHashCodeConsidersZoneId() { + void equalsAndHashCodeConsiderZoneId() { String cron = "0 0 9 * * *"; CronTrigger amsterdam = new CronTrigger(cron, ZoneId.of("Europe/Amsterdam")); CronTrigger newYork = new CronTrigger(cron, ZoneId.of("America/New_York"));