根据日期和时间计算下次运行任务的时间

时间:2012-03-05 11:16:52

标签: c# .net

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

从定义配置此“任务”的一些事情开始。首先,枚举看起来很像框架的DayOfWeek枚举,我称之为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},
};

然后这个方法确定它是否应该在一天运行:

private bool ShouldRunOn(DateTime now)
{
    var days = DayToDaysMap[now.DayOfWeek];
    // If the schedule is not set for the specified day, return false
    if (!this.DaysToRun.HasFlag(days))
        return false;

    // Schedule should run on specified day, just determine if it is in the past
    return this.TimeOfDay > now.TimeOfDay;    
}

然后实施变为; “我今天可以跑吗”,如果不是“提前6天,看看我能不能跑那天”。请注意,参数lastRun未在此实现中使用,它用于其他参数(例如重复计划)。

public override DateTime CalculateNextRunTime(DateTime lastRun)
{
    var now = DateTime.Now;
    if (ShouldRunOn(now))
        return new DateTime(now.Year,now.Month,now.Day,this.TimeOfDay.Hours,
              this.TimeOfDay.Minutes,this.TimeOfDay.Seconds);

    for (var i = 1; i < 7; i++)
    {
        now = now.AddDays(1).Date;
        if(ShouldRunOn(now))
            return new DateTime(now.Year, now.Month, now.Day, 
              this.TimeOfDay.Hours, this.TimeOfDay.Minutes, this.TimeOfDay.Seconds);
    }
    return DateTime.MinValue;
}

欢迎改进!

3 个答案:

答案 0 :(得分:2)

这是我的改写:

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;
}

粗略的逻辑是:

从今天开始的每一天: - 检查天是否有效,如果没有跳到第二天 - 创建当天的运行时 - 检查运行时是否在过去,如果是跳到第二天,则返回此运行时。

检查它是否过去对于所有循环之后的所有循环显然是多余的,但是对于所有循环来说它是更整洁的并且我怀疑额外的比较可能是瓶颈。 :)

上面的

DateInDayOfWeek只是一个方法,如果传递的日期与DaysToRun属性中的某一天相匹配,则返回true。我不能使用hasFlags,因为我在编写测试代码时没有使用.NET 4。您可能希望将其保留为单独的方法,以避免它变得混乱。 ; - )

答案 1 :(得分:0)

在其他两个语句中你需要在第二天返回来运行任务, 你可以通过简单的循环来完成它。

        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
        }

答案 2 :(得分:0)

我正在按位收集工作日或工作日。

以下是工作日的按位值:

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;
        }

使用方法:

要获取下一个星期一:

var d = GetNextRun(16, 13, false, true, false, false, 2);

要获取下一个周一或周四或周六“(4 + 32 + 128)= 164”:

var d = GetNextRun(16, 13, false, true, false, false, 164);

要获取一个月的第二天:

var d = GetNextRun(16, 13, false, false, true, false, 4);

要获取每月的下2或5或7天“(4 + 32 + 128)= 164”:

var d = GetNextRun(16, 13, false, false, true, false, 164);

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