根据时间表查找下一个循环日期
本文关键字:循环 日期 下一个 查找 时间表 | 更新日期: 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
}
这应该对所有日期,间隔和时间表正确工作。修改后的调整值