• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TAKETODAY / today-infrastructure / 18154768944

01 Oct 2025 07:26AM UTC coverage: 81.882% (-0.005%) from 81.887%
18154768944

push

github

web-flow
Merge pull request #290 from TAKETODAY/dev/jspecify

jspecify

59788 of 78013 branches covered (76.64%)

Branch coverage included in aggregate %.

141239 of 167496 relevant lines covered (84.32%)

3.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.18
today-context/src/main/java/infra/scheduling/support/CronTrigger.java
1
/*
2
 * Copyright 2017 - 2025 the original author or authors.
3
 *
4
 * This program is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see [https://www.gnu.org/licenses/]
16
 */
17

18
package infra.scheduling.support;
19

20
import org.jspecify.annotations.Nullable;
21

22
import java.time.Instant;
23
import java.time.ZoneId;
24
import java.time.ZonedDateTime;
25
import java.util.TimeZone;
26

27
import infra.lang.Assert;
28
import infra.scheduling.Trigger;
29
import infra.scheduling.TriggerContext;
30

31
/**
32
 * {@link Trigger} implementation for cron expressions. Wraps a
33
 * {@link CronExpression} which parses according to common crontab conventions.
34
 *
35
 * <p>Supports a Quartz day-of-month/week field with an L/# expression. Follows
36
 * common cron conventions in every other respect, including 0-6 for SUN-SAT
37
 * (plus 7 for SUN as well). Note that Quartz deviates from the day-of-week
38
 * convention in cron through 1-7 for SUN-SAT whereas Infra strictly follows
39
 * cron even in combination with the optional Quartz-specific L/# expressions.
40
 *
41
 * @author Juergen Hoeller
42
 * @author Arjen Poutsma
43
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
44
 * @see CronExpression
45
 * @since 4.0
46
 */
47
public class CronTrigger implements Trigger {
48

49
  private final CronExpression expression;
50

51
  private final ZoneId zoneId;
52

53
  /**
54
   * Build a {@code CronTrigger} from the pattern provided in the default time zone.
55
   *
56
   * @param expression a space-separated list of time fields, following cron
57
   * expression conventions
58
   */
59
  public CronTrigger(String expression) {
60
    this(expression, ZoneId.systemDefault());
4✔
61
  }
1✔
62

63
  /**
64
   * Build a {@code CronTrigger} from the pattern provided in the given time zone.
65
   *
66
   * @param expression a space-separated list of time fields, following cron
67
   * expression conventions
68
   * @param timeZone a time zone in which the trigger times will be generated
69
   */
70
  public CronTrigger(String expression, TimeZone timeZone) {
71
    this(expression, timeZone.toZoneId());
5✔
72
  }
1✔
73

74
  /**
75
   * Build a {@code CronTrigger} from the pattern provided in the given time zone.
76
   *
77
   * @param expression a space-separated list of time fields, following cron
78
   * expression conventions
79
   * @param zoneId a time zone in which the trigger times will be generated
80
   * @see CronExpression#parse(String)
81
   * @since 4.0
82
   */
83
  public CronTrigger(String expression, ZoneId zoneId) {
2✔
84
    Assert.hasLength(expression, "Expression must not be empty");
3✔
85
    Assert.notNull(zoneId, "ZoneId is required");
3✔
86

87
    this.expression = CronExpression.parse(expression);
4✔
88
    this.zoneId = zoneId;
3✔
89
  }
1✔
90

91
  /**
92
   * Return the cron pattern that this trigger has been built with.
93
   */
94
  public String getExpression() {
95
    return this.expression.toString();
4✔
96
  }
97

98
  /**
99
   * Determine the next execution time according to the given trigger context.
100
   * <p>Next execution times are calculated based on the
101
   * {@linkplain TriggerContext#lastCompletion completion time} of the
102
   * previous execution; therefore, overlapping executions won't occur.
103
   */
104
  @Nullable
105
  @Override
106
  public Instant nextExecution(TriggerContext triggerContext) {
107
    Instant instant = triggerContext.lastCompletion();
3✔
108
    if (instant != null) {
2✔
109
      Instant scheduled = triggerContext.lastScheduledExecution();
3✔
110
      if (scheduled != null && instant.isBefore(scheduled)) {
6✔
111
        // Previous task apparently executed too early...
112
        // Let's simply use the last calculated execution time then,
113
        // in order to prevent accidental re-fires in the same second.
114
        instant = scheduled;
2✔
115
      }
116
    }
1✔
117
    else {
118
      instant = triggerContext.getClock().instant();
4✔
119
    }
120
    ZonedDateTime dateTime = ZonedDateTime.ofInstant(instant, this.zoneId);
5✔
121
    ZonedDateTime next = this.expression.next(dateTime);
6✔
122
    return (next != null ? next.toInstant() : null);
7✔
123
  }
124

125
  @Override
126
  public boolean equals(@Nullable Object other) {
127
    return (this == other || (other instanceof CronTrigger &&
12!
128
            this.expression.equals(((CronTrigger) other).expression)));
4!
129
  }
130

131
  @Override
132
  public int hashCode() {
133
    return this.expression.hashCode();
×
134
  }
135

136
  @Override
137
  public String toString() {
138
    return this.expression.toString();
×
139
  }
140

141
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc