如何在每天的给定时间执行代码?

时间:2013-10-29 17:46:41

标签: c# datetime time timezone

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

我知道有很多类似的问题,但是它们似乎都没有提供我可以使用的解决方案。主要的建议似乎是使用Task Scheduler或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,
         ...);

但是,如果我的代码在东部时间以外的时区运行,我认为这会崩溃。我还担心,当时区从美国东部时间切换到美国东部时间时,这种情况在23或25小时内表现不正常,反之亦然。

有没有比我目前正在做的更好的方式来处理调度?如何使这段代码更加健壮以处理在任何时区运行但总是在东部时间同时执行?

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

7 个答案:

答案 0 :(得分:9)

设置Windows计划任务。对于这样一个直接的工作来说,长时间运行的计时器往往是有问题的,而且肯定过于复杂。

答案 1 :(得分:7)

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

在回应您关于日程安排和应用程序性质的说明时,遗憾的是,您强调需要重新考虑您的应用程序。构建一个必须一直运行并且不断添加功能的整体应用程序并不是一个好主意。

相反,为什么不重新考虑应用程序的结构。您可以拥有核心功能,它需要一直运行,作为服务运行。然后,其他组件可以通过例如休息接口与其通信。然后可以停止启动应用程序的用户界面方面,而不必担心影响服务,无论如何都可以继续运行。此外,添加额外的功能,例如在一天的特定时间执行特定tsaks的预定应用程序,变得更加容易。

答案 2 :(得分:3)

正确的解决方案是使用Quartz.net或Windows任务计划程序,但由于你说你不能这样做,让我们考虑一下你可能做的其他事情。

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

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

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

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

  • 现在回顾一下这一切,并意识到你刚刚编写了自己的Quartz.NET。你真的确定你不能只建立一个已存在的吗?为什么重新发明轮子?

答案 3 :(得分:1)

使用Quartz.net

等任务调度框架

答案 4 :(得分: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 ,
       ...);

答案 5 :(得分:0)

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

答案 6 :(得分:0)

由于您无法使用第三方库或将内部公开给其他应用程序,因此公开通信端点(WCF,UDP,命名管道等)是一种选择吗?或者您可以监视另一个应用程序创建的“命令文件”的文件夹? (使用FileSystemWatcher或简单的计时器)

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