For anyone who cares about 29/2 special handling, here is my suggestion (thx to @Gareth for the incentive):
import calendar
def add_years(d, years):
"""
Return the same calendar date (month and day) in the destination year.
In the case of leap years, if d.date is 29/2/xx and the target year (xx+years) is
not a leap year it will return 28/2.
Conversely, if d.date is 28/2/xx and the target year is a leap year, it will
return 29/2.
"""
sy, ty = d.year, d.year + years
if all([calendar.isleap(sy), calendar.isleap(ty)]):
ret = d.replace(year=d.year+years)
elif all([calendar.isleap(sy), d.month == 2, d.day == 29]):
ret = d.replace(day=d.day-1).replace(year=d.year+years)
elif all([calendar.isleap(ty), d.month == 2, d.day == 28]):
ret = d.replace(year=d.year+years).replace(day=d.day+1)
else:
ret = d.replace(year=d.year + years)
return ret