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 оказывается достаточно маленьким и положительным, вы можете спать, пока не наступит время запуска. Если оно получается маленьким и отрицательным, вы проспали - бегите немедленно (проспать очень часто).

Добавление дней к текущему полуночному значению, добавление времени к нему и вычисление времени ожидания - это способ получить разумное время выполнения даже в дни перехода.

Другие вопросы по тегам