1

I'm trying to calculate the date a person turns 18, taking into account leap years.

One might think that just adding 18 years to a date is enough:

birthDay.AddYears(18);

But if the birthday is February 29, 2000, this leads to february 28, 2018. I believe the correct answer would have to be March 1, 2018.

I found a way to do it, but I wonder if there isn't a simpler way.

birthday.AddDays(-1).AddYears(18).AddDays(1);

I would think an answer to this question would already exist on StackOverflow, but all my searches only turn up questions about calculating age on a specific date.

I don't think my question is a duplicate of Behavior of DateTime.AddYears on leap year as that one only explains why AddYears works as it does. It does not contain code to perform the calculation.

I found a quora article stating that the official date you turn 18 is different in New Sealand from the UK.

comecme
  • 6,086
  • 10
  • 39
  • 67
  • 8
    https://stackoverflow.com/questions/9498249/behavior-of-datetime-addyears-on-leap-year – Etienne Jun 07 '17 at 07:00
  • 2
    *Counter example* for your current algorithm: `new DateTime(1998, 3, 1).AddDays(-1).AddYears(18).AddDays(1)`: expected `1 Mar 2016`, actual `29 Feb 2016` – Dmitry Bychenko Jun 07 '17 at 07:01
  • 1
    Have u had a look on [this one](https://stackoverflow.com/questions/9/calculate-age-in-c-sharp) ? Alot of answers are including your problem ! – Felix D. Jun 07 '17 at 07:01
  • 1
    Possible duplicate of [Behavior of DateTime.AddYears on leap year](https://stackoverflow.com/questions/9498249/behavior-of-datetime-addyears-on-leap-year) – Alessandro D'Andria Jun 07 '17 at 07:11

1 Answers1

1

I suggest hiding the logic (rounding up to 1 Mar instead of rounding down to 28 Feb) into an extension method:

public static partial class DateTimeExtensions {
  public static DateTime AddYearsUp(this DateTime value, int amount) {
    // Business as usual: neither days are leap days or both days are leap days
    if (value.Month != 2 || value.Day != 29 || DateTime.IsLeapYear(value.Year + amount))
      return value.AddYears(amount);

    // We want 29 Feb to be turned into 1 Mar (not 28 Feb)
    return new DateTime(value.Year + amount, 3, 1, 
                        value.Hour, value.Minute, value.Second, value.Millisecond);
  }
}

Then use the method implemented:

var result = birthDay.AddYearsUp(18); 

Please, notice, that you current logic

birthday.AddDays(-1).AddYears(18).AddDays(1);

is not correct; the counter example is

// 1 Mar 1998
var birthday = new DateTime(1998, 3, 1);

// Expected:  1 Mar 2016
// Actual:   29 Feb 2016
var result = birthday.AddDays(-1).AddYears(18).AddDays(1);
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • Regarding my code being incorrect: I dit perform some tests, but I dit not include one where the birthday is in a non-leap year, while the resulting date is a leap year. Thanks for that. – comecme Jun 07 '17 at 08:44
  • 1
    @comecme: typical *borders check* procedure: `leap` to `leap`, `leap` to `non-leap`, `non-leap` to `leap` (the case where the counter example was found); if you have archive data to work with (when did *Einstein* turn `18`?) it's reasonable to include `1900` year (which is non-leap one). – Dmitry Bychenko Jun 07 '17 at 09:07
  • I think I would change your last return statement to `return value.AddYears(amount).AddDays(1)`. Is there a specific reason why you are creating a new DateTime like you do? – comecme Jun 07 '17 at 09:12
  • 1
    @comecme: the very reason of `new DateTime()..` is that I want to be on the *safe side*: I can't guarantee, say, **C# 8.5, .Net 7.0** will not change the logic (and start returning `1 Mar`). I put down my actulal intention: in *case of doubt* (`28 Feb` or `1 Mar`?) I want `1 Mar`, not "the system solution + 1 day" – Dmitry Bychenko Jun 07 '17 at 09:17