Поведение DateTime.AddYears в високосный год
Может ли кто-нибудь объяснить математическое или просто рассуждение за вычислениями високосного года в .NET при использовании метода AddYears в DateTime?
- Если вы возьмете 29 февраля 2012 года и добавите год, вы получите 28 февраля 2013 года, а не 1 марта 2013 года (день до года).
- Если вы добавите один год до 31 января 2012 года, вы получите 31 января 2013 года (в тот же день через год).
Я думаю, что большинство людей полагают, что "один год с 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
Ответы
Ответ 1
Я думаю, что большинство людей полагают, что "один год с 29.02.leapX равен 01.03.leapX + 1".
Я бы не стал. Обычно я ожидаю усечения. Это принципиально похоже на добавление одного месяца к 30 января - я ожидаю получить последний день в феврале. В обоих случаях вы добавляете "большую единицу" (месяц или год), а "меньшая единица" (день) усекается, чтобы вписаться в комбинацию год/месяц.
(Вот как Joda Time и Noda Time ведут себя тоже, кстати.)
Как упоминал Тим в комментариях, этот документировал:
Метод AddYears вычисляет полученный год с учетом високосных лет. Месяц и временная часть результирующего объекта DateTime остаются такими же, как этот экземпляр.
Таким образом, месяц должен оставаться в феврале; год изменится в зависимости от того, сколько лет добавляется, очевидно - поэтому день должен корректироваться, чтобы оставаться в силе.
Ответ 2
С вашей точки зрения, 1 марта 2012 года станет 2 марта 2012 года, когда вы добавите год. Если вы добавите этот сдвиг для всех предшествующих високосных лет, вы будете искать ваш расчет в массовом движении. Единственный разумный ответ - вернуть 28 февраля за непиковые годы.
Ответ 3
Это интересно, тем не менее..
например эта функция иногда используется:
private static int Age(DateTime birthDate, DateTime asAtDate)
{
// Calculate age in years
int age = asAtDate.Year - birthDate.Year;
if (asAtDate < birthDate.AddYears(age)) age--;
if (age < 0) age = 0;
return age;
}
Если человек родился 29 февраля 2016 года, эта функция сделает вывод, что он достиг 1 года 28 февраля 2016 года.
Я отметил примеры функций Excel в соответствии с:
=DATEDIF(DATE(2016,2,28),DATE(2017,2,28),"Y")
gives result of 1
=DATEDIF(DATE(2016,2,29),DATE(2017,2,28),"Y")
gives result of 0
=DATEDIF(DATE(2016,2,29),DATE(2017,3,1),"Y")
gives result of 1
=DATEDIF(DATE(2016,3,1),DATE(2017,3,1),"Y")
gives result of 1