And this is why you always use your framework's or language's date arithmetics library and never try to hack up a solution on your own. Date calculations alone are hard enough with the basic irregularities of month lengths. Add the leap years and it becomes even harder.
And don't get me started on times, especially once time zones and summertime comes into play.
Likely your particular hacked-together solution will fail at some point. And if it doesn't: was it worth all the effort you put into making it perfect, especially considering that somebody has already done it for your framework.
I think the problem is that it's not obvious that (in Python, where I saw this first):
datetime( now.years + 1, now.month, now.day )
is a hacked-together solution. You have to really design an API very carefully to suggest that this is a bad thing to do (I guess you could make now.years + int yield a type that datetime won't accept as the first argument, but I'm sure I wouldn't think of that before the fact and I consider myself a relatively competent API designer.
Not excusing MSFT here, as they have the resources and experience to get it right, but in general I think that following the rule of "don't DIY" won't solve the problem.
I guess that the fundamental problem is thinking of months and years as numbers (and representing them as such).
If they were purely symbolic constants, then the expression "January + 1" is meaningless and would throw an error.
So, with hindsight, I'd say that any Datetime api which represents days, months and years as numeric quantities (which is, probably, all of them) encourages these kinds of bugs. (Or at least doesn't discourage them).
Can anyone come up with a use case where you need numeric values for these things? (Which doesn't suffer from the same kind of bugs as this?)
And this is why you always use your framework's or language's date arithmetics library and never try to hack up a solution on your own. Date calculations alone are hard enough with the basic irregularities of month lengths. Add the leap years and it becomes even harder.
And don't get me started on times, especially once time zones and summertime comes into play.
Likely your particular hacked-together solution will fail at some point. And if it doesn't: was it worth all the effort you put into making it perfect, especially considering that somebody has already done it for your framework.
NIH at its finest.