1

我正在尝试向我的程序添加一个功能,该功能将在芝加哥期货市场收盘后每天执行代码。这意味着我想在东部时间 5:35 左右(市场所在的中部时间 4:35)运行代码。随着夏令时的发生,这当然会在 EST (UTC -5) 和 EST (UTC -4) 之间来回切换。

我知道有很多类似的问题,但是它们似乎都没有提供我可以使用的解决方案。主要建议似乎是使用任务计划程序或 Quartz,但是,我无法在我的程序中实现这些。我认为最有希望的解决方案是使用 、 和 的组合TimeZoneInfoDateTimeTimeSpan每天安排一个计时器,该计时器将在正确的时间触发。我目前的解决方案是这样的:

    DateTime now = DateTime.Now;
    DateTime currentDateTime = now.DateTime.Date;
    DateTime expiryDateTime = currentDateTime
        .AddHours(17)
        .AddMinutes(35)
        .AddDays(
            now.DateTime.Hour >= 18 + utcOffset 
            || (now.DateTime.Hour == 17 && now.DateTime.Minute >= 35) ? 1 : 0);
    Timer timer = new Timer(
         ...,
         null,
         expiryDateTime - DateTime.Now,
         ...);

但是,如果我的代码在东部时间以外的时区运行,我认为这将分崩离析。我还担心当时区从 EST 切换到 EDT 时,这在 23 或 25 小时的日子里不会正常运行,反之亦然。

有没有比我目前正在做的更好的方法来处理日程安排?如何使此代码更健壮以处理在任何时区运行但始终在东部时间同时执行?

编辑:如上所述,Task Scheduler 和 Quartz 不是 options。Quartz 退出了,因为我无法包含第三方库。任务计划程序已退出,因为我需要从我的程序中访问大量内部值。启动另一个应用程序并将这些值暴露给该应用程序会增加比我认为的优点更多的复杂性。

4

7 回答 7

9

设置 Windows 计划任务。对于如此直接的工作,长时间运行的计时器往往会出现问题,而且肯定过于复杂。

于 2013-10-29T17:48:50.823 回答
7

您错过了 Windows 任务计划程序的要点。您没有在您的应用程序中实现它。您编写一个应用程序,然后使用任务调度程序在每天的设定时间运行它。

不幸的是,在回应您对调度和应用程序性质的澄清时,您强调需要重新考虑您的应用程序。构建一个必须一直运行并且不断添加功能的单体应用程序并不是一个好主意。

相反,为什么不重新考虑应用程序的结构。您可以将需要始终运行的核心功能作为服务运行。然后其他组件可以通过例如一个restful接口与之对话。然后可以停止和启动应用程序的用户界面方面,而不必担心影响服务,服务可以继续运行。此外,添加额外功能(例如在一天中的特定时间执行特定任务的计划应用程序)变得更加容易。

于 2013-10-29T17:49:12.267 回答
3

正确的解决方案是使用 Quartz.net 或 Windows 任务计划程序,但既然你说你不能这样做,让我们想想你还可以做什么。

  • 您的程序需要一直运行。这意味着它应该是一个后台 Windows 服务应用程序。你不想让它成为一个用户应用程序,因为你不想处理关闭应用程序的用户,即使没有人登录它也应该运行。

  • 您不应该使用长计时器。相反,请使用定期运行的计时器来检查是否到了执行事件的时间。短计时器应该多久运行一次?这是您希望在空闲时消耗多少资源与您的任务可以接受多少潜在延迟之间的权衡。如果它需要在设定时间的几分钟内运行,您可能只想每分钟检查一次,或者每五分钟左右检查一次。

  • 如果可能,您应该只从数据中获取一次“下一个执行时间”。因此,当您定期检查时,您不必每次都访问数据库。但是要小心,如果这个值是不稳定的,那么你需要一种方法来使你的本地缓存值过期。

  • 关于 DST 和时区,您需要区分计划在特定 UTC 时间或特定本地时间运行的任务。例如,如果您有一个每小时任务,您不妨使用 UTC,这样您就不会被 DST 更改所困扰。如果您有日常任务,您可能应该使用当地时间。这可能是计算机的本地时间,也可能是特定时区的本地时间。这由你来管理。此外,您将需要一种策略来处理计划在 spring-forward DST 转换创建的间隙期间运行的任务,或计划在回退转换创建的模糊时间期间运行的任务。另请参阅DST 标签 wiki

  • 现在回顾所有这些,并意识到您刚刚编写了自己的 Quartz.NET 等价物。你真的确定你不能仅仅建立在一个已经存在的之上吗?为什么要重新发明轮子?

于 2013-10-29T20:24:34.163 回答
1

我感谢所有其他答案的反馈,并想向任何偶然发现这一点的人指出,如果您可以使用 Quartz 或 Windows 任务计划程序,它们确实是更好的选择。但是,我没有那个选项,所以这就是我最终如何实现每次都找到正确的时间并安排计时器运行的方式:

    //This code block is designed to schedule a timer for 5:35pm Eastern Time, regardless of whether is is Daylight Savings (EDT, UTC-4) or not (EST, UTC-5). 
    //  When understanding this code it is important to remember that a DateTime in UTC time represents an ABSOLUTE point in time. Any UTC time can only occur once. 
    //  A date time measured in local time (here we force Eastern Standard) is NOT ABSOLUTE. The same local time can occur twice (2am Nov 3nd 2013 for example)
    //Gets Eastern Timezone to be used in conversions
    TimeZoneInfo easternTimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
    //Grabs the current time (UTC) 
    DateTime utcNowDateTime = DateTime.Now.ToUniversalTime();
    //Gets current time in Eastern Time (could be UTC-4 or -5 depending on time of year)
    DateTime easternNowDateTime = TimeZoneInfo.ConvertTime(utcNowDateTime, easternTimeZoneInfo);
    //Gets todays date
    DateTime easternNowDate = easternNowDateTime.Date;
    //Gets 5:35pm today or tomorrow depending on whether we are past 5:35pm or not. 
    //  Even though there are actually 18 hours from midnight to 5:35pm on Nov 2 2014 and 16 hours from midnight to 5:35pm on March 9 2014, 
    //  this will still end up at 5:35pm on those days because DateTime DOESNOT take into account the movement of the clocks (foreward or backward) when calling 
    //  .AddHours(), etc
    DateTime easternExpiryDateTime = easternNowDate.AddHours(17).AddMinutes(35).AddDays(easternNowDateTime.Hour >= 18 || (easternNowDateTime.Hour == 17 && easternNowDateTime.Minute >= 35) ? 1 : 0);
    //Convert the Easter Time date time to UTC. When subtracting this time from another UTC DateTime you will get the correct TimeSpan for use with a timer
    //  (even on 23 such as March 10th and 25 hour days such as November 3rd 2013)
    DateTime utcExpiryDateTime = easternExpiryDateTime.ToUniversalTime();
    //Set the timer to trigger at the desired point in the future.
    Timer timer = new Timer(
       ...,
       null,
       utcExpiryDateTime - utcNowDateTime ,
       ...);
于 2013-10-29T20:38:30.890 回答
1

使用Quartz.net等任务调度框架

于 2013-10-29T17:49:05.163 回答
0

您可以创建 Windows 服务并将其安装在您的系统上。

于 2013-10-29T17:50:25.993 回答
0

由于您不能使用第三方库,或将您的内部结构暴露给另一个应用程序,是否可以选择暴露通信端点(WCF、UDP、命名管道等)?或者也许您可以监视另一个应用程序创建的“命令文件”文件夹?(使用 FileSystemWatcher 或简单的计时器)

单独的应用程序可以将消息发送到此端点/文件夹,并且 Windows 任务计划程序可以安排此应用程序。

于 2013-10-29T19:16:11.870 回答