C# 根据一天和一个时间计算下一次运行任务的时间

C# 根据一天和一个时间计算下一次运行任务的时间,c#,.net,C#,.net,我有一个相当具体的问题,根据任务的配置,找出计算程序中“任务”下次运行的最佳方法 从定义配置此“任务”所需的某些内容开始。首先,一个类似于框架的DayOfWeekenum的枚举,我称之为DaysOfWeek,并用FlagsAttribute标记它,以指示它可以是其中的倍数: [Flags] public enum DaysOfWeek { Sunday = 1, Monday = 2, Tuesday = 4, Wednesday = 8, Thursda

我有一个相当具体的问题,根据任务的配置,找出计算程序中“任务”下次运行的最佳方法

从定义配置此“任务”所需的某些内容开始。首先,一个类似于框架的
DayOfWeek
enum的枚举,我称之为
DaysOfWeek
,并用
FlagsAttribute
标记它,以指示它可以是其中的倍数:

[Flags]
public enum DaysOfWeek
{
    Sunday = 1,
    Monday = 2,
    Tuesday = 4,
    Wednesday = 8,
    Thursday = 16,
    Friday = 32,
    Saturday = 64
}
其次,具有适当属性的类,以及我试图实现的方法:

public class WeeklySchedule 
{

    public DaysOfWeek DaysToRun { get; set; }
    public TimeSpan TimeToRun{ get; set; }

    public override DateTime CalculateNextRunTime(DateTime lastRun)
    {
        // Here's what im trying to implement   
    }
}
要求应该非常明显

  • 如果DaysToRun是今天,但TimeToRun今天已离开,请返回下一次/天
  • 如果DaysToRun中未包含今天,则查找下一天/时间运行
很明显,我星期一只是有个大脑放屁,因为我无法想出一个有效的方法来计算这个,除了
ShouldExecuteToday()
方法,然后是
FindNextExecutionDay()
等等(也许这是正确的方法……)

编辑:好的,周末的大脑迷雾正在消散,这是我目前的状况。如果有人能在这方面有所改进,我们将不胜感激:

首先,我将两个枚举映射到我的类的一个静态成员中,我知道我可以按照@DorCohen的例子从一个解析到另一个,但这让我感到恶心

private static Dictionary<DayOfWeek, DaysOfWeek> DayToDaysMap 
 = new Dictionary<DayOfWeek, DaysOfWeek>()
{
{DayOfWeek.Monday, DaysOfWeek.Monday},
{DayOfWeek.Tuesday, DaysOfWeek.Tuesday},
{DayOfWeek.Wednesday, DaysOfWeek.Wednesday},
{DayOfWeek.Thursday, DaysOfWeek.Thursday},
{DayOfWeek.Friday, DaysOfWeek.Friday},
{DayOfWeek.Saturday, DaysOfWeek.Saturday},
{DayOfWeek.Sunday, DaysOfWeek.Sunday},
};
然后,实现变得简单;“我今天可以跑步吗?”如果不行,“提前6天,看看我那天是否可以跑步”。请注意,参数
lastRun
在此实现中未使用,它用于其他实现(例如重复计划)

public覆盖DateTime CalculateNextRunTime(DateTime lastRun)
{
var now=DateTime.now;
如果(应该(现在))
返回新的日期时间(now.Year,now.Month,now.Day,this.TimeOfDay.Hours,
这个。时间。分钟,这个。时间。秒);
对于(变量i=1;i<7;i++)
{
now=now.AddDays(1).Date;
如果(应该(现在))
返回新的日期时间(now.Year,now.Month,now.Day,
this.TimeOfDay.Hours,this.TimeOfDay.Minutes,this.TimeOfDay.Seconds);
}
return DateTime.MinValue;
}

欢迎改进

在这两个else语句中,您需要在第二天返回以运行任务, 您可以通过简单的循环来完成

        DaysOfWeek DaysToRun = DaysOfWeek.Friday | DaysOfWeek.Monday;
        TimeSpan timeToRun = new TimeSpan(12,0,0);

        DateTime now = DateTime.Today;
        DaysOfWeek Day = (DaysOfWeek)Enum.Parse(typeof(DaysOfWeek), now.DayOfWeek.ToString());
        if (DaysToRun.HasFlag(Day))
        {
            if (now.TimeOfDay < timeToRun )
            {
                MessageBox.Show(nowTime.ToString());
            }
            else
            {
                //return next day
            }
        }
        else
        {
            //return next day
        }
DaysOfWeek DaysToRun=DaysOfWeek.周五| DaysOfWeek.周一;
TimeSpan timeToRun=新的TimeSpan(12,0,0);
DateTime now=DateTime.Today;
DaysOfWeek Day=(DaysOfWeek)Enum.Parse(typeof(DaysOfWeek),now.DaysOfWeek.ToString();
if(DaysToRun.HasFlag(日))
{
如果(now.TimeOfDay
以下是我的重写:

public DateTime CalculateNextRunTime()
{
    var now = DateTime.Now;
    for (var i = 0; i<=7; i++)
    {
        var potentialRunTime = now.AddDays(i);
        if (!DateInDayOfWeek(potentialRunTime))
            continue;
        potentialRunTime = potentialRunTime.Date + TimeToRun;
        if (potentialRunTime < DateTime.Now)
            continue;
        return potentialRunTime;
    }
    return DateTime.MinValue;
}
public DateTime CalculateNextRunTime()
{
var now=DateTime.now;

对于(var i=0;i我使用按位收集工作日或月日

以下是工作日的按位值:

2   Sunday 
4   Monday 
8   Tuesday 
16  Wednesday 
32  Thursday 
64  Friday 
128 Saturday 
如果需要计算星期一、星期四和星期六的下一个运行日期,则需要求和4、32和128,并将结果值作为收集日传递

示例:(4+32+128)=164

您将使用与月日的收款日相同的方法

以下是从第1天到第31天的月份天数的按位值:

2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
65536
131072
262144
524288
1048576
2097152
4194304
8388608
16777216
33554432
67108864
134217728
268435456
536870912
1073741824
2147483648
以下是函数:

static DateTime GetNextRun(int hour, int min, bool isDaily, bool isWeekly, bool isMonthly, bool isLastDayOfMonth, int collectionDay)
        {
            var today = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, hour, min, 0, 0);
            var tomorrow = today.AddDays(1);

            if (isDaily)
            {
                return tomorrow;
            }
            else if (isWeekly)
            {
                if (collectionDay < 2)
                {
                    throw new Exception("The collection Day is invalid.");
                }

                if (collectionDay > 255)
                {
                    throw new Exception("The collection Day is invalid.");
                }

                for (int i = 1; i < 8; i++)
                {
                    var dayOfWeek = (int)today.AddDays(i).DayOfWeek;
                    var power = (int)(Math.Pow(2, dayOfWeek + 1));
                    if ((power & collectionDay) > 0)
                    {
                        return today.AddDays(i);
                    }
                }
            }
            else if (isMonthly)
            {
                var nextDate = tomorrow;

                if (collectionDay < 2 && isLastDayOfMonth)
                {
                    return new DateTime(tomorrow.Year, tomorrow.Month, GetDaysInMonth(tomorrow), hour, min, 0, 0);
                }

                if (collectionDay < 2)
                {
                    throw new Exception("The collection Day is invalid.");
                }

                while (true)
                {
                    var power = (int)(Math.Pow(2, nextDate.Day));
                    if ((power & collectionDay) > 0)
                    {
                        if (isLastDayOfMonth && nextDate.Month != tomorrow.Month)
                        {
                            return new DateTime(tomorrow.Year, tomorrow.Month, GetDaysInMonth(tomorrow), hour, min, 0, 0);
                        }

                        return nextDate;
                    }

                    nextDate = nextDate.AddDays(1);
                }
            }

            return DateTime.MaxValue;
        }

        static int GetDaysInMonth(DateTime d)
        {
            for (int i = 28; i < 33; i++)
            {
                try
                {
                    new DateTime(d.Year, d.Month, i, 1, 1, 0, 0);
                }
                catch (Exception)
                {
                    return (i - 1);
                }
            }

            return 31;
        }
要获取下一个星期一、星期四或星期六”(4+32+128)=164):

要获取一个月的第二天,请执行以下操作:

var d = GetNextRun(16, 13, false, false, true, false, 4);
要获得一个月中接下来的2天、5天或7天“(4+32+128)=164):


您还可以设置isLastDayOfMonth标志,以计算下一次运行的月份最后一天。

您可以查看著名的。在最后的
CalculateNext Runtime
函数中,您将重复大量代码,这些代码看起来可以从0开始滚动到循环中。您可能需要稍微更改一些内容,例如可能会为每天生成潜在的运行时间,然后查看它们是否在未来(如果潜在的日期是今天或将来,这将起到相同的作用)。你也可以在第二天不使用循环的情况下使用一些位移位和对数的东西来计算,但这可能有点太复杂,不值得避免7次迭代循环。;-)@Chris-我也认为第一次迭代可以被循环,但这几乎是问题的关键;它可以安排在今天,但在当天早些时候,在这种情况下,时间不应该返回。请注意循环是如何使用
.Date
将时间返回到午夜的,其中的“今天”部分做不到。@JamieC:我已经注意到了。我现在写下了一个答案,这个答案的循环方式略有不同。我认为这是一个更整洁的代码,尽管它可能比你的代码做了一些不必要的检查。如果你想让可读性,我猜你应该这样做。;-)这是一个好的开始,但有几点。1)
now.TimeOfDay
将为您提供一个
时间跨度
,因此无需创建时间跨度。2)我看不到在下一个可用日期运行此循环。我不同意这是一个“简单循环”,最简单的是,根据我上面的编辑,这是一个循环,但不是那么简单:)我当然更喜欢这段代码的外观。我将插入它并运行它通过我所有的单元测试,以检查它的逻辑与我的逻辑没有什么不同。只要它通过,这就是公认的答案:)宾果!谢谢你的帮助克里斯。没问题。我不能完全把我的fi我想在很多方面都是关于重新思考标准是如何工作的(也就是说,它必须总是在现在之前,这样你才能做sa)
static DateTime GetNextRun(int hour, int min, bool isDaily, bool isWeekly, bool isMonthly, bool isLastDayOfMonth, int collectionDay)
        {
            var today = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, hour, min, 0, 0);
            var tomorrow = today.AddDays(1);

            if (isDaily)
            {
                return tomorrow;
            }
            else if (isWeekly)
            {
                if (collectionDay < 2)
                {
                    throw new Exception("The collection Day is invalid.");
                }

                if (collectionDay > 255)
                {
                    throw new Exception("The collection Day is invalid.");
                }

                for (int i = 1; i < 8; i++)
                {
                    var dayOfWeek = (int)today.AddDays(i).DayOfWeek;
                    var power = (int)(Math.Pow(2, dayOfWeek + 1));
                    if ((power & collectionDay) > 0)
                    {
                        return today.AddDays(i);
                    }
                }
            }
            else if (isMonthly)
            {
                var nextDate = tomorrow;

                if (collectionDay < 2 && isLastDayOfMonth)
                {
                    return new DateTime(tomorrow.Year, tomorrow.Month, GetDaysInMonth(tomorrow), hour, min, 0, 0);
                }

                if (collectionDay < 2)
                {
                    throw new Exception("The collection Day is invalid.");
                }

                while (true)
                {
                    var power = (int)(Math.Pow(2, nextDate.Day));
                    if ((power & collectionDay) > 0)
                    {
                        if (isLastDayOfMonth && nextDate.Month != tomorrow.Month)
                        {
                            return new DateTime(tomorrow.Year, tomorrow.Month, GetDaysInMonth(tomorrow), hour, min, 0, 0);
                        }

                        return nextDate;
                    }

                    nextDate = nextDate.AddDays(1);
                }
            }

            return DateTime.MaxValue;
        }

        static int GetDaysInMonth(DateTime d)
        {
            for (int i = 28; i < 33; i++)
            {
                try
                {
                    new DateTime(d.Year, d.Month, i, 1, 1, 0, 0);
                }
                catch (Exception)
                {
                    return (i - 1);
                }
            }

            return 31;
        }
var d = GetNextRun(16, 13, false, true, false, false, 2);
var d = GetNextRun(16, 13, false, true, false, false, 164);
var d = GetNextRun(16, 13, false, false, true, false, 4);
var d = GetNextRun(16, 13, false, false, true, false, 164);