Пытаюсь сделать так, чтобы раз в неделю в 8 утра программа отправляла сообщение. Он отправляет его в правильный день, но есть проблема с элементом времени. Если в тот день 7:58 утра, я хочу, чтобы оно было отправлено, оно будет ждать до 8 утра, чтобы отправить его, но если мне вообще придется перезапустить программу после 8 утра, она отправит ее снова с начальной задержкой 0 или отрицательное число, если день, когда его необходимо отправить, уже прошел. Изменение отрицательного числа на 0 приведет к отправке сообщения, что сохранит ошибку, поскольку время отправки прошло.
public class Reminder extends ListenerAdapter{
@Override
public void onReady(ReadyEvent event){
final ZoneId zone = ZoneId.systemDefault();
final ZoneId realzone = ZoneId.of(zone.getId());
final ZonedDateTime zdt = ZonedDateTime.now(realzone);
String day = String.valueOf(zdt.getDayOfWeek());
String targetday = String.valueOf(DayOfWeek.WEDNESDAY);
ZonedDateTime targettime = zdt.withHour(8).withMinute(0);
ZonedDateTime currenttime = ZonedDateTime.now();
long InitialDelay = currenttime.until(targettime, ChronoUnit.MILLIS);
final Set<ZonedDateTime> targetTimes = Set.of(targettime);
JDA bot = event.getJDA(); // <- JDA ...
final long realChannelID = fakeID; //real
final int period = 1000 * 60 * 60 * 24 * 7;
TextChannel textChannel = event.getJDA().getTextChannelById(realChannelID);
System.out.println(InitialDelay);
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
Runnable WeeklyMessageTask = () -> {
System.out.println("starting task");
// for (ZonedDateTime time : targetTimes) {
if (day.equals(targetday) && InitialDelay >= 0) {
System.out.println("Sending weekly message...");
switch (randomint()) {
case 1 -> textChannel2.sendMessage("Tiamat is Titillated... Who is on board for putting an end to that?").queue();
case 2 -> textChannel2.sendMessage("It is Wednesday and the War rages... Who will join the fight?").queue();
case 3 -> textChannel2.sendMessage("Head count for tommorrow everyone?").queue();
}
}
if (!day.equals(targetday) && InitialDelay >= 0)
System.out.println("NOT Sending weekly message...");
System.out.println("ending task");
};
scheduler.scheduleAtFixedRate(WeeklyMessageTask, InitialDelay, period, TimeUnit.MILLISECONDS);
}
public int randomint(){
Random rand = new Random();
int max=3,min=1;
int randomNum = rand.nextInt(max - min + 1) + min;
return randomNum;
}
}
Я попытался изменить оператор if, чтобы проверить, является ли значение отрицательным, чтобы предотвратить отправку дополнительных сообщений, которые не решили проблему. Я также пробовал разные единицы времени, чтобы увидеть, является ли это проблемой (от 7 дней до периода TimeUnit.Milliсекунды). Мне удалось воспроизвести эту проблему за пределами API JDA, поэтому я знаю, что это не проблема API.
Как мне лучше вычислить InitialDelay, чтобы избежать этой проблемы с scheduler.scheduleAtFixedRate()
?
Если он вызывается в 11 часов утра, ваш код в настоящее время отправляет задачу с отрицательной задержкой. Это означает, что запланированное действие выполняется немедленно (в 11 часов утра) и будет выполнено снова через неделю (в 11 часов утра...).
Чтобы надежно уведомлять в 8 утра, вам следует убедиться, что ваша задержка никогда не бывает отрицательной, отложив ее до следующей недели, если время уведомления на этой неделе уже прошло.
Я бы сделал это следующим образом:
public void scheduleNotifications() {
Thread.ofVirtual().start(() -> {
for (;;) {
sleepUntil(nextNotification());
System.out.println("Hi!");
}
});
}
LocalDateTime nextNotification() {
var next = LocalDate.now()
.with(ChronoField.DAY_OF_WEEK, 3) // this week's Wednessday
.atTime(8, 0); // at 8 AM
if (next.isBefore(LocalDateTime.now())) { // if in the past
next = next.plusWeeks(1); // delay until next week
}
return next;
}
void sleepUntil(LocalDateTime time) {
try {
Thread.sleep(Duration.between(LocalDateTime.now(), time));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
Приведенное выше решение неоднократно рассчитывает задержку с учетом различной продолжительности недели (если дневное время заканчивается или начинается, неделя может быть на час длиннее или короче, чем обычно). То есть, если мы заботимся о постоянном времени уведомления в условиях летнего времени, мы не можем использовать ScheduledThreadPoolExecutor.scheduleAtFixedRate
и должны сами управлять повторением. К счастью, виртуальные потоки облегчают эту задачу.
Если вы еще не можете использовать виртуальные потоки, вместо этого вы можете использовать ScheduledExecutor.schedule
:
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoField;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
public class Test {
ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
public void scheduleNotifications() {
scheduleRepeatedly(() -> System.out.println("Hi!"), () -> nextNotification());
}
void scheduleRepeatedly(Runnable action, Supplier<LocalDateTime> timeSupplier) {
var delay = Duration.between(LocalDateTime.now(), timeSupplier.get());
exec.schedule(() -> {
action.run();
scheduleRepeatedly(action, timeSupplier);
}, delay.toMillis(), TimeUnit.MILLISECONDS);
}
LocalDateTime nextNotification() {
var next = LocalDate.now().with(ChronoField.DAY_OF_WEEK, 3).atTime(8, 0);
if (next.isBefore(LocalDateTime.now())) {
next = next.plusWeeks(1);
}
return next;
}
}
public static void main(String[] args) { ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor (1); void ScheduleRepeatedly (Runnable action, Поставщик <LocalDateTime> timeSupplier) {вар задержки = Duration.between (LocalDateTime.now(), timeSupplier.get()); exec.schedule(() -> { action.run(); ScheduleRepeatedly(action, timeSupplier); }, Delay.toMillis(), TimeUnit.MILLISCONDS); } } (методы находятся вне основного).
Если отредактировали, чтобы показать импорт, но там нет ничего необычного. Код компилируется для меня, как опубликовано.
Кажется, моя проблема в том, что я не уверен, как лучше всего реализовать ваше решение в ScheduledExecutor.schedule.
Я получаю пару ошибок при работе с этой настройкой. Ему не нравится действие или время. Поставщик в
Runnable action, Supplier<LocalDateTime> timeSupplier
. действие в action.run(); и действие, timeSupplier также вscheduleRepeatedly(action, timeSupplier);
. Я пропустил импорт? у меня есть импорт java.util.function.*;