当我遇到这个问题时,我认为这将是一个涉及 TimeSpans 和基本 DateTime 算法的微不足道的挑战。除非我错过了一些非常明显的错误......
如何将“13.245”年添加到“3/22/2023 5:25:00 AM”?
我得到的最接近的是这个片段:
long ticks = (long)((365.0M - 4) * (decimal)TimeSpan.TicksPerDay * 13.245M);
DateTime futureDate= new DateTime(2023, 3, 22, 5, 25, 0).AddTicks(ticks);
Console.WriteLine(futureDate.ToString());
这给了我“4/23/2036 4:05:48 PM”的输出,我对此并不完全有信心。另外,请注意我不得不手动处理闰年的方式:
365.0M - 4
你的做法几乎是正确的,但有几个问题需要考虑:
闰年:正如你提到的,你需要考虑闰年,因为他们有额外的一天。一种方法是计算两个日期之间的天数,然后加上与该期间闰年数相对应的一天的分数。
Precision:“13.245”年的值不准确,因为一年不正好是365天。它更接近 365.2425 天。这对于小的时间间隔可能无关紧要,但对于较长的时间间隔,它可能会产生重大影响。
时区:您提供的代码假设输入日期是在当地时区,这可能不是您想要的。如果你想使用 UTC 日期,你应该使用 DateTimeOffset 结构而不是 DateTime。
这是考虑到这些问题的代码的修改版本:
decimal yearsToAdd = 13.245M;
DateTime inputDate = new DateTime(2023, 3, 22, 5, 25, 0);
int inputYear = inputDate.Year;
// Calculate the number of leap years between the input year and the future year
int futureYear = inputYear + (int)yearsToAdd;
int numLeapYears = Enumerable.Range(inputYear, futureYear - inputYear)
.Count(y => DateTime.IsLeapYear(y));
// Calculate the number of days to add, including leap years
TimeSpan daysToAdd = TimeSpan.FromDays(yearsToAdd * 365.2425M + numLeapYears);
// Add the days to the input date
DateTime futureDate = inputDate.Add(daysToAdd);
Console.WriteLine(futureDate.ToString());
此代码使用 LINQ 的 Enumerable.Range 方法计算输入年份和未来年份之间的闰年数。然后计算要添加的总天数,包括与部分年份和闰年对应的一天的分数。最后,它将生成的 TimeSpan 添加到输入日期以获得未来日期。
我会这样做:
private static DateTime AddYears(DateTime date, double years)
{
date = date.AddYears((int)years);
double remainder = years - (int)years;
double yearSeconds = (new DateTime(date.Year + 1, 1, 1) - new DateTime(date.Year, 1, 1)).TotalSeconds;
return date.AddSeconds(yearSeconds * remainder);
}
下降到第二个的好处是可以接受最后加法的两倍,并允许更高的精度。
但是,仍然存在出错的可能性,因为剩余的部分可能会使我们进入一个长度不同的新的一年:闰年或其他因素(如奇数闰秒)可能发生变化。例如,假设开始日期是
2023-12-31 23:23:59
,加法是0.99
。上面的代码假定年份长度基于当前的 365 天年份(添加 0 整年),但几乎所有最终的小数加法都发生在明年,它有 366 天。 你最终会比预期的时间短 16 1/2 小时。
要真正准确,你需要把小数部分加到年底,然后根据新的年份重新计算剩下的部分。
知道 Jon Skeet 正在看这个问题,如果 Noda Time 使这更容易,我不会感到惊讶,但现在我将把最后的小数部分作为 OP 的练习。
我确定还有其他可能的错误,但我觉得这会让你在大多数情况下足够接近。
就我个人而言,我会先添加整个年份 (13),然后添加整个月份 (0.245 * 12 = 2.94 = 2),最后一步是检查下个月有多少天和(例如 30)并添加相应的天(0.94 * 30 = 28.2)。 像这样的东西:
private static DateTime AddYears(DateTime date, double years)
{
date = date.AddYears((int)years);
double remainingYearsToAdd = years - (int)years;
double monthsToAdd = remainingYearsToAdd * 12;
date = date.AddMonths((int)monthsToAdd);
double remainingMonthsToAdd = monthsToAdd - (int)monthsToAdd;
int daysInThisMonth = DateTime.DaysInMonth(date.Year, date.Month);
double elapsedPercentageThisMonth = 1d / daysInThisMonth * (date.Day + (date.Hour + (date.Minute + (date.Second + date.Millisecond / 1000d) / 60d) / 60d) / 24d);
if (elapsedPercentageThisMonth + remainingMonthsToAdd <= 1)
{
return date.AddDays(remainingMonthsToAdd * daysInThisMonth);
}
else
{
remainingMonthsToAdd -= elapsedPercentageThisMonth;
date = new DateTime(date.Year, date.Month+1, 1);
int daysInNextMonth = DateTime.DaysInMonth(date.Year, date.Month);
return date.AddDays(daysInNextMonth * remainingMonthsToAdd);
}
}