SystemEvents.TimeChanged
将告诉您时钟是否已被用户更改。它不会在时钟的每个滴答声中定期触发。所以它对调度事件没有用,除了你可能会在它发生时重新计算计时器。(我认为如果系统与时间服务器同步,它也会触发,但我对此并不乐观。)
如果您尝试按照 Hans 的建议计算挂墙时间的差异,请小心。你不能只使用DateTime
. 观察:
// 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
即使指定了本地种类,它也不会考虑 DST。
如果要采用这种方法,则必须使用DateTimeOffset
类型。
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
您可能需要通过以下方式收集您的输入:
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
但是- 设置一个计时器运行这么长时间可能不是一个好主意。不仅时钟可能由用户更改或系统时间同步,而且应用程序或系统可能会关闭或重新启动。此外,如果您有许多这样的任务,您最终可能会因为闲置而消耗大量资源。
一个想法是保留下次触发事件的列表。在您的应用程序中,您将触发一个短暂的轮询计时器(例如每分钟一次左右),并将当前时间与列表中的值进行比较,以了解您是否真的需要做任何事情。
另一个想法将是一个轻微的变化。代替短时间的轮询,您将保持您的列表排序,并设置延迟到下一个事件的时间,并具有一些最大延迟(可能是一个小时)。同样,当计时器触发时,您会查看是否有任何事情要做,或者是否需要设置另一个计时器延迟。您必须在您的应用程序启动时运行它,并且任何时候安排新事件。
使用这些方法中的任何一种,您都应该使用 aDateTimeOffset
或等效的 UTC来安排时间DateTime
。否则,您可能会在 DST 的错误时间触发 - 甚至在 DST 回退过渡期间触发两次。
如果所有这些听起来都太复杂,那么您可以尝试使用预构建的解决方案,例如Quartz.net。特别是,请阅读他们的常见问题解答的这一部分。
关于您的第一个和第三个要点,我完全同意-但这永远不会发生。即使它确实发生了,我们仍然必须考虑它确实发生的所有多年历史。如果你还没有看过这个视频,你应该。