假设我需要找出下一个计划日期是什么时候,我知道该计划是基于 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)
尝试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。无论哪种方式,结果仍然是错误的。试试这个吧。
获取下一个运行时间
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)
这应该适用于所有日期、间隔和时间表。编辑:修正调整值
| 归档时间: |
|
| 查看次数: |
2683 次 |
| 最近记录: |