Timer-based
If you are searching for a vanilla solution that sticks to the "timer-concept", you can just compute the correct timespan and let the task, once its done, reschedule itself again with the correct computed time for the next execution.
Also see Call method at date/time. Will look something like:
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
public static ScheduledFuture<?> scheduleFor(Runnable runnable, ZonedDateTime when) {
Instant now = Instant.now();
// Use a different resolution if desired
long secondsUntil = ChronoUnit.SECONDS.between(now, when.toInstant());
return scheduler.schedule(runnable, secondsUntil, TimeUnit.of(ChronoUnit.SECONDS));
}
public static ZonedDateTime nextDate() {
return ZonedDateTime.now().plusMonths(1);
}
And then you have your task something like
public class MyTask implements Runnable {
// ...
@Override
public void run() {
// TODO Your stuff ...
// Reschedule
scheduleFor(this, nextDate());
}
}
And initially trigger it like
scheduleFor(task, ZonedDateTime.now().withDayOfMonth(10));
Disadvantages
Obviously this solution, because it is timer-based, has the big disadvantage that it can easily drift and get out of sync. For example if your system time changes or if the execution of the task takes so long that you drift into the next day.
Latter can easily be mitigated by generating the next date at start of the task, instead of the end.
If all of that is not a factor for you, it will likely work as intended.
Otherwise you should probably go for a different, crontab-like approach where the date is checked periodically and soon as it matches, it executes the task. In contrast to a fixed-offset execution as in "execute in ... seconds".