根据时间表查找下一个重复日期

big*_*mac 2 c# math datetime

假设我需要找出下一个计划日期是什么时候,我知道该计划是基于 2014 年 8 月 1 日的开始日期,它应该每 7 天运行一次,当前日期是 2014 年 8 月 10 日。我应该得到 2014 年 8 月 14 日的日期。我最终想让这段代码每隔 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
}
Run Code Online (Sandbox Code Playgroud)

这是我的单元测试

[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);
    }
Run Code Online (Sandbox Code Playgroud)

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

--- 更新 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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

van*_*ane 5

尝试GetNextRunTime用这个替换你的

public DateTime GetNextRunTime(ScheduleRequest request)
{
    double days = (request.Interval - ((request.CurrentDate - request.BaseDate).TotalDays % request.Interval));
    return request.CurrentDate.AddDays(days-1);
}
Run Code Online (Sandbox Code Playgroud)

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

编辑:让我们将其分解,希望能帮助您找出逻辑。

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 的日期,因此您需要从 daysUntil 中减去 1nextDate = request.CurrentDate.AddDays(daysUntil - 1)

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

更新 根据您的测试,我发现问题出在我们双方。我的计算不正确,当你需要除以7时,你却乘以7。无论哪种方式,结果仍然是错误的。试试这个吧。

  • 完全删除你的扩展类
  • 使用以下代码修改您的 GetNextRunTime
  • 使用以下代码修改您的 ScheduleRequest 和 Schedule 类/枚举

获取下一个运行时间

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);
}
Run Code Online (Sandbox Code Playgroud)

日程请求

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; }
}
Run Code Online (Sandbox Code Playgroud)

日程

public enum Schedule : ulong
{
    Hourly = 3600000,
    Daily = 86400000,
    Weekly = 604800000
}
Run Code Online (Sandbox Code Playgroud)

这应该适用于所有日期、间隔和时间表。编辑:修正调整值