问题 闰年上DateTime.AddYears的行为


在DateTime上使用AddYears方法时,有人可以解释.NET中闰年计算背后的数学或简单推理吗?

  • 如果您参加2012年2月29日并增加一年,您将获得2013年2月28日,而不是2013年3月1日(一年后的前一天)。
  • 如果您在2012年1月31日之前添加一年,则会在2013年1月31日(一年后的同一日期)到达。

我想大多数人会认为“一年从29.02.leapX是01.03.leapX + 1”。

例:

// Testing with 29th Feb
var now1 = DateTime.Parse("2012-02-29 15:00:00");

var results1 = new DateTime[]
{
    now1.AddYears(1),
    now1.AddYears(2),
    now1.AddYears(3),
    now1.AddYears(4)
};

foreach(var dt in results1)
{
    Console.WriteLine(dt.ToString("s"));
}

// Output:
// 2013-02-28T15:00:00
// 2014-02-28T15:00:00
// 2015-02-28T15:00:00
// 2016-02-29T15:00:00


// Testing with 31st Jan
var now2 = DateTime.Parse("2012-01-31 13:00:00");

var results2 = new DateTime[]
{
    now2.AddYears(1),
    now2.AddYears(2),
    now2.AddYears(3),
    now2.AddYears(4)
};

foreach(var dt in results2)
{
    Console.WriteLine(dt.ToString("s"));
}

// Output:
// 2013-01-31T13:00:00
// 2014-01-31T13:00:00
// 2015-01-31T13:00:00
// 2016-01-31T13:00:00

2629
2018-02-29 11:27


起源

MSDN 非常清楚:“AddYears方法在考虑闰年的情况下计算结果年份。生成的DateTime对象的月份和时间部分与此实例保持一致。” - Tim Schmelter


答案:


我想大多数人会认为“一年从29.02.leapX是01.03.leapX + 1”。

我不会。我通常会期望截断。它基本上类似于1月30日增加一个月 - 我希望在2月份的最后一天。在这两种情况下,您都会添加“更大的单位”(月份或年份),而“较小的单位”(日期)将被截断以适应年/月组合。

(这是怎么回事 乔达时间 和 野田时间 也行事,顺便说一下。)

正如蒂姆在评论中提到的那样 记录 那样的:

AddYears方法在考虑闰年的情况下计算结果年份。生成的DateTime对象的月份和时间部分与此实例保持一致。

所以  必须留在二月;这一年将根据添加的年数而变化,显然 - 因此必须调整以保持有效。


12
2018-02-29 11:32



谢谢。这对我来说很有意义,但我意识到我一直认为日期和日历是重叠数组,其中29.02的“指数”与下一个非闰年的01.03指数相同。一个非常简化(和错误)的想法。 - Henrik
一年总是12个月,但12个月可以是365天或366天,每个月可以是28天到31天的可变天数。一天总是24小时,所以添加天,小时,等等将是精确的,没有可变性,因为一天的长度不随每个习惯的天数而变化。正如预期的那样,如果您在2000年1月1日之前添加365天,那么您将获得12/31/2000,因为那一年有366天。 AddYears和AddMonths是不同的,因为一年的含义或长度会随着年份的不同而变化,就像一个月的长度在几个月内变化一样。 - Triynko
这意味着给定像3月15日这样的日期,在该值上添加“一个月”在技术上是模棱两可的,因为它涉及3月和4月的#天。由于它是模棱两可的,为了避免“漂移”,必须保持一天不变并增加月份,每12个月增加一年。这样做会导致“越界”数字,因此必须更改日期。改变这一天应该只在最后一步中发生。有趣的是,在“1月31日”调用AddMonths(2)会给出不同的值“3月31日”,而不是在中间值“2月28日” - >“3月28日”连续两次调用AddMonth(1);) - Triynko
换句话说,对于29到31之间的天数,AddMonths方法(或者运算符,如果你愿意)缺少添加的关联属性,因为你可以根据你对添加的分组来获得不同的日期。即DateTime(“Jan 31”)。AddMonth(1).AddMonths(1)给出与DateTime(“Jan 31”)不同的结果.AddMonths(2),以及闰年的不同结果。因此它没有完全合理,但它有效地最小化了每次调用的日期漂移,并且其他可能的方式使得它变得更没意义(例如,使用计算中涉及的所有月份的平均天数)。 - Triynko
@Triynko:我甚至不会说它“完全没有意义” - 只是日历不提供与自然数字相同的计算基础。 - Jon Skeet


根据您的理由,2012年3月1日将在2012年3月2日增加一年。如果您在之前的所有闰年中添加此班次,那么您将发现大量漂移的计算。唯一明智的反应是在非闰年回归28-Feb。


2
2018-02-29 11:32