【发布时间】:2016-07-11 21:17:20
【问题描述】:
我正在开发的系统是这样构建和配置的,用户在设置定期付款时无法选择闰年。这导致幕后的所有日期数学都必须忽略闰年。 (我没选这个,不过是这样写的)
我必须编写一个接受 DateTime 值的方法,并在日期上添加天数,忽略闰年,这实际上意味着忽略 2 月 29 日并假装它不存在。
例如,如果我将 365 天添加到 2016 年 1 月 1 日,则结果应该是 2017 年 1 月 1 日,而不是 2016 年 12 月 31 日。
我使用的是 .NET,所以我可以使用 DateTime.IsLeapYear 和其他辅助方法。
这是一项正在进行的工作,这就是我目前所拥有的。我开始采用更简单的路线,现在我意识到这将需要更复杂的算法。
public static DateTime AddDaysToDateWithLeapYearConsideration(DateTime date, int daysToAdd)
{
// Nothing to do
if (daysToAdd == 0)
{
return date;
}
// NOTE: This is an invalid approach; using DateTime.AddDays will take leap years into account
DateTime dateWithAddedDays = date.AddDays(daysToAdd);
const int FEB_28_DAY_OF_YEAR = 59;
int daysToSubtractForLeapYearConsideration = 0;
// The year is a leap year, which is under the feb 28 day threshold, and we're adding enough days to push it over the feb 28 day threshold
// This will result in .NET taking into account the feb 29th (the leap year day), but we have to subtract that leap year day since the system doesn't take feb 29th into account
if (DateTime.IsLeapYear(date.Year) && date.DayOfYear < FEB_28_DAY_OF_YEAR && (date.DayOfYear + daysToAdd > FEB_28_DAY_OF_YEAR))
{
daysToSubtractForLeapYearConsideration++;
}
// The resulting date (after the days are added or subtracted) is a leap year, whose day is past the feburary 28 day threshold, and it's not the same year as the date (i.e. it spans across "n" years)
if (DateTime.IsLeapYear(dateWithAddedDays.Year) && dateWithAddedDays.DayOfYear > FEB_28_DAY_OF_YEAR && dateWithAddedDays.Year != date.Year)
{
daysToSubtractForLeapYearConsideration++;
}
// We determined if the original date should be leap year considered, as well as the resulting date/year with the days added. Now see if there are any years in between
// that we should consider
bool isThereAYearRangeThatWeNeedToEvaluateLeapYearsFor = Math.Abs(date.Year - dateWithAddedDays.Year) > 0;
if (isThereAYearRangeThatWeNeedToEvaluateLeapYearsFor)
{
for (int leapYearEvalIndex = Math.Min(date.Year, dateWithAddedDays.Year); leapYearEvalIndex <= Math.Max(date.Year, dateWithAddedDays.Year); leapYearEvalIndex++)
{
bool isYearPartOfTheYearsThatWeveAlreadyChecked = leapYearEvalIndex == date.Year || leapYearEvalIndex == dateWithAddedDays.Year;
if (!isYearPartOfTheYearsThatWeveAlreadyChecked && DateTime.IsLeapYear(leapYearEvalIndex))
{
daysToSubtractForLeapYearConsideration++;
}
}
}
DateTime dateResult = date.AddDays(daysToAdd - daysToSubtractForLeapYearConsideration);
// The system does not allow 2/29 days, hence all this crazy date math
if (dateResult.Month == 2 && dateResult.Day == 29)
{
dateResult = dateResult.AddDays(1);
}
return dateResult;
}
逻辑还必须考虑负数(即减法),上面的代码失败了。
上面的代码根本不起作用,但我想证明我正在努力解决这个问题,而不是简单地询问而没有尝试过任何事情。
编辑 我提出了一个非常接近大卫方法的算法。 (我写了它,然后回到 StackOverflow 来检查响应)。
public static DateTime AddDaysToDateWithLeapYearConsideration(DateTime date, int daysToAdd)
{
// Nothing to do
if (daysToAdd == 0)
{
return date;
}
DateTime dateResult = date;
// Are we adding or subtracting
bool areWeAddingDays = daysToAdd > 0;
int daysToAccountForInRegardToLeapYearDates = 0,
absDaysToAdd = Math.Abs(daysToAdd);
for (int i = 1; i <= absDaysToAdd; i++)
{
dateResult = dateResult.AddDays(areWeAddingDays ? 1 : -1);
if (dateResult.Month == 2 && dateResult.Day == 29)
{
daysToAccountForInRegardToLeapYearDates++;
}
}
dateResult = dateResult.AddDays(areWeAddingDays ? daysToAccountForInRegardToLeapYearDates : -daysToAccountForInRegardToLeapYearDates);
return dateResult;
}
【问题讨论】:
-
你可以做
int increment = 1; if(daysToAdd < 0) { increment = -1; daysToAdd = -daysToAdd; } for(int i =0; i < daysToAdd; i++){ date.AddDays(increment); if(date.Month == 2 and date.Day == 29) date.AddDays(increment); } return date; -
志同道合的人