System.Timers.Timer и летнее время
У меня есть несколько таймеров, которые читают HH:MM:SS
из таблицы, чтобы выяснить, когда бежать ежедневно.
Например:
Таймер А должен работать каждый понедельник в 13:00:00.
Таймер B должен работать каждый вторник в 02:00:00
Таймер С должен работать каждый час
Итак, в моем коде я выясняю, какое сейчас время, а затем вычисляю миллисекунды из DateTime.Now()
до следующего случая, когда таймер должен работать. Когда таймер Elapsed
событие завершено, оно пересчитывает, когда оно должно быть запущено следующим. Это создало проблему в эти выходные из-за смены времени.
Есть лучший способ сделать это? Было бы DateTime.UtcNow
быть лучшей альтернативой? Может быть, преобразовать строку времени в базе данных в время UTC, а затем выяснить разницу между DateTime.UtcNow()
вместо DateTime.Now()
?
1 ответ
Вы можете сделать несколько вещей, чтобы решить эту проблему.
Одним из них является использование UTC для всех ваших запланированных заданий. Это дает вам надежную и предсказуемую систему без большого количества сложностей или бремени тестирования. Но работы, которые выполняются в 03:00 по понедельникам летом, будут выполняться в 02:00 зимой. Если все в порядке, стратегия UTC - хорошая.
(Тестирование вариантов использования переключения часовых поясов - это боль, и если вы используете UTC для всего, ваша нагрузка на тестирование уменьшается.)
Другой - серьезно относиться к арифметике свиданий и быть очень осторожным. Класс DateTime в C# отлично справляется с арифметикой дат даже в даты замены.
Итак, допустим, вам нужно запускать процесс один раз в неделю в воскресенье в 01:30. В США это кошмарный час... это не происходит при весенней смене, и это происходит дважды при осенней смене. Но вы все еще хотите, чтобы процесс запускался один раз.
То, что вы делаете, это: сохраняйте значение "следующего запланированного времени". Каждый раз, когда вы заканчиваете выполнение задания, вычисляете значение "следующего запланированного времени" для следующего запуска. Это может работать так:
var today = DateTime.Today.AddDays(7); /* midnight a week from now */
TimeSpan runTime = TimeSpan.Parse("01:30");
var nextRun = today + runTime;
Затем сохраните это значение DateRime nextRun. Позже вы можете выяснить, сколько времени пройдет до следующего запуска. Это хороший способ сделать это. Есть и другие.
var msUntilNextRun = (nextRun.Ticks - DateTime.Now.Ticks) / 10000;
Если значение msUntilNextRun оказывается достаточно маленьким и положительным, вы можете спать, пока не наступит время запуска. Если оно получается маленьким и отрицательным, вы проспали - бегите немедленно (проспать очень часто).
Добавление дней к текущему полуночному значению, добавление времени к нему и вычисление времени ожидания - это способ получить разумное время выполнения даже в дни перехода.