根据时间表查找下一个循环日期

本文关键字:循环 日期 下一个 查找 时间表 | 更新日期: 2023-09-27 18:16:12

假设我需要找出下一个计划日期是什么时候,当我知道计划是基于8/1/2014的开始日期,它应该每7天运行一次,当前日期是8/10/2014。我应该得到2014年8月14日的日期。我最终想让这段代码每X小时、每X天、每X周工作一次,但现在我只是用几天来测试。我有下面的代码,我用来计算下一个运行时间,但我让它为一个日期工作,然后它失败了另一个。仅供参考,我使用选项来指定当前日期用于测试目的。我做错了什么?

public class ScheduleComputer
{
    public DateTime GetNextRunTime(ScheduleRequest request)
    {
        var daysSinceBase = ((int)((request.CurrentDate - request.BaseDate).TotalDays)) + 1;
        var partialIntervalsSinceBaseDate = daysSinceBase % request.Interval;
        var fullIntervalsSinceBaseDate = daysSinceBase / request.Interval;
        var daysToNextRun = 0;
        if (partialIntervalsSinceBaseDate > 0)
        {
            daysToNextRun = (request.Interval - partialIntervalsSinceBaseDate) + 1;
        }
        var nextRunDate = request.BaseDate.AddDays((fullIntervalsSinceBaseDate * request.Interval) + daysToNextRun - 1);
        return nextRunDate;
    }
}
public class ScheduleRequest
{
    private readonly DateTime _currentDate;
    public ScheduleRequest()
    {
        _currentDate = DateTime.Now;
    }
    public ScheduleRequest(DateTime currentDate)
    {
        _currentDate = currentDate;
    }
    public DateTime CurrentDate
    {
        get { return _currentDate; }
    }
    public DateTime BaseDate { get; set; }
    public Schedule Schedule { get; set; }
    public int Interval { get; set; }
}
public enum Schedule
{
    Hourly,
    Daily,
    Weekly
}

这里是我的单元测试

[TestFixture]
public class ScheduleComputerTests
{
    private ScheduleComputer _scheduleComputer;
    [SetUp]
    public void SetUp()
    {
        _scheduleComputer = new ScheduleComputer();
    }
    [Test]
    public void ThisTestPassesAndItShould()
    {
        var scheduleRequest = new ScheduleRequest(currentDate: DateTime.Parse("8/14/2014"))
        {
            BaseDate = DateTime.Parse("8/1/2014"),
            Schedule = Schedule.Daily,
            Interval = 7
        };
        var result = _scheduleComputer.GetNextRunTime(scheduleRequest);
        Assert.AreEqual(DateTime.Parse("8/14/2014"), result);
    }
    [Test]
    public void ThisTestFailsAndItShouldNot()
    {
        var scheduleRequest = new ScheduleRequest(currentDate: DateTime.Parse("8/2/2014"))
        {
            BaseDate = DateTime.Parse("8/1/2014"),
            Schedule = Schedule.Daily,
            Interval = 7
        };
        var result = _scheduleComputer.GetNextRunTime(scheduleRequest);
        Assert.AreEqual(DateTime.Parse("8/7/2014"), result);
    }

仅供参考,我在这里看到了这篇文章,但我似乎无法根据我的需要量身定制。

——UPDATE 1——

这是我更新的代码。我知道我用变量使它冗长,这样我可以更好地理解逻辑(希望这不会对性能产生太大影响)。我还添加了处理不同时间段(小时、天、周)的逻辑,并添加了扩展方法,使代码更简洁。然而,这段代码似乎在几小时和几天内完美地工作,但在几周内就失败了。有些地方我没有正确地乘或除7。

public class ScheduleComputer
{
    public DateTime GetNextRunTime(ScheduleRequest request)
    {
        var timeBetwenCurrentAndBase = request.CurrentDate - request.BaseDate;
        var totalPeriodsBetwenCurrentAndBase = timeBetwenCurrentAndBase.TotalPeriods(request.Schedule);
        var fractionalIntervals = totalPeriodsBetwenCurrentAndBase % request.Interval;
        var partialIntervalsLeft = request.Interval - fractionalIntervals;
        if (request.Schedule != Schedule.Hourly) partialIntervalsLeft = partialIntervalsLeft - 1;
        var nextRunTime = request.CurrentDate.AddPeriods(partialIntervalsLeft, request.Schedule);
        return nextRunTime;
    }
}
public static class ScheduleComputerExtensions
{
    public static double TotalPeriods(this TimeSpan timeBetwenCurrentAndBase, Schedule schedule)
    {
        switch (schedule)
        {
            case Schedule.Hourly: return timeBetwenCurrentAndBase.TotalHours;
            case Schedule.Daily: return timeBetwenCurrentAndBase.TotalDays;
            case Schedule.Weekly: return timeBetwenCurrentAndBase.TotalDays * 7;
            default: throw new ApplicationException("Invalid Schedule Provided");
        }
    }
    public static DateTime AddPeriods(this DateTime dateTime, double partialIntervalsLeft, Schedule schedule)
    {
        switch (schedule)
        {
            case Schedule.Hourly: return dateTime.AddHours(partialIntervalsLeft);
            case Schedule.Daily: return dateTime.AddDays(partialIntervalsLeft);
            case Schedule.Weekly: return dateTime.AddDays(partialIntervalsLeft * 7);
            default: throw new ApplicationException("Invalid Schedule Provided");
        }
    }
}

根据时间表查找下一个循环日期

试着用这个

替换你的GetNextRunTime
public DateTime GetNextRunTime(ScheduleRequest request)
{
    double days = (request.Interval - ((request.CurrentDate - request.BaseDate).TotalDays % request.Interval));
    return request.CurrentDate.AddDays(days-1);
}

这应该能给你正确的日期。

编辑:让我们把它分解一下,希望能帮助你弄清楚其中的逻辑。

diff = (request.CurrentDate - request.BaseDate).TotalDays给出了BaseDate和CurrentDate之间的天数。注意,天数不包括BaseDate的天数。所以8/7/14和8/1/14之间的差是6天。

daysSinceLast = diff % request.Interval这给出了自上次间隔命中以来的天数,所以如果上次间隔命中是8/1/14,现在是8/7/14,那么结果将是6% 7 = 6;距离上次预定间隔(不包括最后间隔日期)已过去6天。这是计算中最重要的部分;它保留天数,无论在间隔内经过了多少天,因此,例如,如果自BaseDate以来已经过了100天,并且间隔为7:100% 7 = 2,这意味着自上次间隔触发以来已经过了2天,实际上不需要知道它被触发的最后日期。你只需要BaseDate和CurrentDate。您可以使用此逻辑来查找最后一次触发间隔的日期,只需从CurrentDate中减去天数。

daysUntil = request.Interval - daysSinceLast这为您提供了到下一个计划间隔的天数。7 - 6 =距离下一个预定间隔1天

1天在这个场景中是不正确的,并且结果永远不会正确,因为TimeSpan差异的计算不包括BaseDate的日期,所以您需要从天数中减去1直到nextDate = request.CurrentDate.AddDays(daysUntil - 1)

将剩余天数(基准日期减去1)与当前日期相加,得到所需的值。这有帮助吗?

根据你们的测试,我发现问题出在我们双方身上。我的计算错了,当你需要除以7时,你乘以了7。不管怎样,结果仍然是错误的。

  • 完全删除你的扩展类
  • 用下面的代码修改GetNextRunTime
  • 用下面的代码修改你的scheduleequest和Schedule类/enum

GetNextRunTime

public DateTime GetNextRunTime(ScheduleRequest request)
{
    double diffMillis = (request.CurrentDate - request.BaseDate).TotalMilliseconds;
    double modMillis = (diffMillis % request.IntervalMillis);
    double timeLeft = (request.IntervalMillis - modMillis);
    ulong adjust = (request.Schedule == Schedule.Daily) ? (ulong)Schedule.Daily : 0;
    return request.CurrentDate.AddMilliseconds(timeLeft - adjust);
}

ScheduleRequest

public class ScheduleRequest
{
    private readonly DateTime _currentDate;
    public ScheduleRequest()
    {
        _currentDate = DateTime.Now;
    }
    public ScheduleRequest(DateTime currentDate)
    {
        _currentDate = currentDate;
    }
    public DateTime CurrentDate
    {
        get { return _currentDate; }
    }
    public DateTime BaseDate { get; set; }
    public Schedule Schedule { get; set; }
    public double IntervalMillis { get { return (double)this.Schedule * this.Interval; } }
    public int Interval { get; set; }
}

public enum Schedule : ulong
{
    Hourly = 3600000,
    Daily = 86400000,
    Weekly = 604800000
}

这应该对所有日期,间隔和时间表正确工作。修改后的调整值