SystemEvents.TimeChanged
will tell you if the clock has been changed by the user. It doesn't fire on a regular basis with each tick of the clock. So it's not useful for scheduling events, except that you might recalculate timers when it occurs. (I think it also will fire if the system syncs with a time server, but I'm not positive about that.)
If you try to calculate the difference in wall time as Hans suggested, be careful. You can't just use DateTime
. Observe:
// With time zone set for US Pacific time, there should only be 23 hours
// between these two points
DateTime a = new DateTime(2013, 03, 10, 0, 0, 0, DateTimeKind.Local);
DateTime b = new DateTime(2013, 03, 11, 0, 0, 0, DateTimeKind.Local);
TimeSpan t = b - a;
Debug.WriteLine(t.TotalHours); // 24
Even with local kind specified, it does not take DST into account.
If you're going to take this approach, you must use DateTimeOffset
types.
DateTimeOffset a = new DateTimeOffset(2013, 03, 10, 0, 0, 0, TimeSpan.FromHours(-8));
DateTimeOffset b = new DateTimeOffset(2013, 03, 11, 0, 0, 0, TimeSpan.FromHours(-7));
TimeSpan t = b - a;
Debug.WriteLine(t.TotalHours); // 23
You will probably need to gather your inputs with something like this:
DateTime today = DateTime.Today; // today at midnight
DateTime tomorrow = today.AddDays(1); // tomorrow at midnight
TimeZoneInfo tz = TimeZoneInfo.Local;
DateTimeOffset a = new DateTimeOffset(today, tz.GetUtcOffset(today));
DateTimeOffset b = new DateTimeOffset(tomorrow, tz.GetUtcOffset(tomorrow));
TimeSpan t = b - a;
Debug.WriteLine(t.TotalHours); // 23, 24, or 25 depending on DST
However - it is probably not such a great idea to set a single timer to run for such a long period of time. Not only might the clock change by the user or a system time sync, but the application or system might shut down or restart. Also, if you have many of these tasks, you could end up with a lot of resources consumed just to sit idle.
One idea would be to keep a list with the next time to fire events. In your application, you would fire a short-lived polling timer (say once per minute or so), and compare the current time against the values in the list to know if you really need to do anything.
Another idea would be a slight variation of that. Instead of short polling, you would keep your list sorted, and set a time for the delay until the next event, with some maximum delay (perhaps an hour). Again, when the timer fires, you see if there's anything to do, or if you need to set another timer delay. You have to run this when your application starts, and any time a new event is scheduled.
With either of these approaches, you should schedule using either a DateTimeOffset
, or the equivalent UTC DateTime
. Otherwise, you risk firing at the wrong time for DST - or even firing twice during the DST fall-back transition.
If all of that sounds too complicated, then you might try a pre-built solution, such as Quartz.net. In particular, read this section of their FAQ.
Regarding your first and third bullet points, I agree wholeheartedly - but it is never going to happen. Even if it does we will still have to consider all of the many years of history that it did occur. If you haven't watched this video already, you should.