Cron Expression once each 2 days
Categories:
Scheduling Tasks Every Other Day with Cron Expressions

Learn how to craft cron expressions for Quartz Scheduler and other cron-compatible systems to execute tasks precisely once every two days, covering common pitfalls and best practices.
Scheduling recurring tasks is a fundamental requirement in many applications. Cron expressions provide a powerful and flexible way to define these schedules. While daily, weekly, or monthly schedules are straightforward, defining a task to run 'once every two days' can sometimes be tricky due to the nature of cron's time-based fields. This article will guide you through creating robust cron expressions for this specific requirement, focusing on Quartz Scheduler's syntax, which is widely adopted in Java environments.
Understanding Cron Expression Basics
A standard cron expression consists of six or seven fields, depending on the implementation. Each field represents a unit of time, from seconds to day of the week. Understanding these fields is crucial for crafting accurate schedules.
For Quartz Scheduler, the fields are typically:
- Seconds (0-59)
- Minutes (0-59)
- Hours (0-23)
- Day of Month (1-31)
- Month (1-12 or JAN-DEC)
- Day of Week (1-7 or SUN-SAT)
- Year (optional, empty or 1970-2099)
flowchart LR subgraph Cron Fields S[Seconds] --> M[Minutes] M --> H[Hours] H --> DOM["Day of Month"] DOM --> MO[Month] MO --> DOW["Day of Week"] DOW --> Y[Year (Optional)] end style S fill:#f9f,stroke:#333,stroke-width:2px style M fill:#bbf,stroke:#333,stroke-width:2px style H fill:#bfb,stroke:#333,stroke-width:2px style DOM fill:#fbb,stroke:#333,stroke-width:2px style MO fill:#fdf,stroke:#333,stroke-width:2px style DOW fill:#ddf,stroke:#333,stroke-width:2px style Y fill:#eee,stroke:#333,stroke-width:2px
Standard Cron Expression Field Order
Crafting the 'Every Other Day' Expression
The challenge with 'every other day' lies in the 'Day of Month' and 'Day of Week' fields. A simple /2
increment on the 'Day of Month' field (1/2
) would mean 'every 2nd day of the month starting on the 1st', which might not align with a continuous 'every other day' schedule across month boundaries. The most reliable way to achieve a true 'every other day' schedule is to use the 'Day of Month' field with a starting day and an increment, combined with careful consideration of the 'Day of Week' field to avoid conflicts.
Let's assume we want the task to run at 9:00 AM. A common approach is to pick a starting day and then increment. For example, to run every other day starting on the 1st of the month:
0 0 9 1/2 * ? *
This expression means:
0
: At second 00
: At minute 09
: At hour 9 (9 AM)1/2
: Every 2nd day of the month, starting on the 1st.*
: Every month?
: No specific day of the week (important to avoid conflicts with 'Day of Month')*
: Any year (optional, can be omitted in some implementations)
However, this expression will run on the 1st, 3rd, 5th, etc., of each month. If a month has 31 days, it will run on the 31st. The next month, it will run on the 1st, meaning it runs on two consecutive days (31st and 1st). To truly ensure 'every other day' without this overlap, you might need to manage two separate cron jobs or use a more advanced scheduling mechanism that tracks the last execution date.
1/2
for 'every other day' across month boundaries. If a job runs on the 31st of a month, and then on the 1st of the next month, it effectively runs on two consecutive days. For strict 'every other day' logic, consider external state management or two alternating cron jobs.Alternative: Using Two Alternating Cron Jobs
To achieve a strict 'every other day' schedule that correctly handles month transitions, a robust solution involves setting up two cron jobs that alternate. One job runs on odd-numbered days of the month, and the other on even-numbered days. You then ensure that only one of them executes based on a shared state or a more complex logic within your task.
Job 1 (Odd Days): Runs on the 1st, 3rd, 5th, etc., of the month.
0 0 9 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31 * ? *
Job 2 (Even Days): Runs on the 2nd, 4th, 6th, etc., of the month.
0 0 9 2,4,6,8,10,12,14,16,18,20,22,24,26,28,30 * ? *
Within your scheduled task, you would then implement logic to check if it's the 'correct' day for execution based on the last run date. This approach provides more control but adds complexity to your application logic.
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
public class EveryOtherDayScheduler {
public static void main(String[] args) throws Exception {
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
JobDetail job = JobBuilder.newJob(MyJob.class)
.withIdentity("everyOtherDayJob", "group1")
.build();
// Cron expression for every other day, starting on the 1st of the month at 9 AM
// This will run on 1st, 3rd, 5th... of each month.
// Be aware of month boundary issues (e.g., 31st then 1st).
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("everyOtherDayTrigger", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 9 1/2 * ? *"))
.build();
sched.scheduleJob(job, trigger);
sched.start();
// For demonstration, let it run for a while and then shut down
Thread.sleep(60L * 1000L); // Keep scheduler running for 1 minute
sched.shutdown(true);
}
}
// A simple job that prints a message
class MyJob implements org.quartz.Job {
public void execute(org.quartz.JobExecutionContext context) {
System.out.println("Job executed at: " + new java.util.Date());
}
}
Example of scheduling an 'every other day' job using Quartz Scheduler in Java.
1/2
for 'Day of Month', always set 'Day of Week' to ?
to avoid conflicts. If both are specified, the cron scheduler might prioritize one over the other or fail to schedule.Considerations for Robust 'Every Other Day' Scheduling
For mission-critical applications requiring a strict 'every other day' schedule without any consecutive runs, relying solely on a single cron expression like 1/2
can be problematic due to month-end transitions. Here are some advanced considerations:
- Application-Level Logic: Implement a check within your scheduled task. Before executing the core logic, verify if the last execution was indeed two days ago. If not, skip the current execution. This requires storing the last execution timestamp.
- External Scheduler Features: Some advanced schedulers (e.g., Airflow, Jenkins with specific plugins) offer more sophisticated scheduling options that can handle 'every N days' more natively, often tracking the last successful run.
- Two-Job Approach with State: As discussed, two cron jobs (one for odd days, one for even) can be used. The job itself would then check a shared state (e.g., a database entry) to determine if it's its turn to run and update the state after execution.
- Leap Years and Month Lengths: Standard cron expressions handle variable month lengths and leap years automatically for 'Day of Month' fields. However, the 'every other day' logic still needs to account for the sequence across these changes.
1. Define Execution Time
Determine the exact second, minute, and hour you want your task to run. For example, 0 0 9
for 9:00:00 AM.
2. Choose Day of Month Strategy
Decide between 1/2
(every other day starting on the 1st, with potential month-end overlaps) or a more complex two-job approach for strict alternation.
3. Set Day of Week to '?'
Always use ?
for the 'Day of Week' field when using Day of Month
increments to prevent conflicts.
4. Test Thoroughly
Simulate your cron expression over several months, including month-end transitions and leap years, to ensure it behaves as expected.
5. Implement Application-Level Guards (Optional but Recommended)
For critical 'every other day' requirements, add logic within your task to verify the last execution date and prevent consecutive runs, even if the cron expression triggers it.