How to find difference in years between two dates?

John Machin sjmachin at lexicon.net
Wed Jul 26 07:57:27 EDT 2006


thebjorn wrote:
> For the purpose of finding someone's age I was looking for a way to
> find how the difference in years between two dates, so I could do
> something like:
>
>   age = (date.today() - born).year
>
> but that didn't work (the timedelta class doesn't have a year
> accessor).
>
> I looked in the docs and the cookbook, but I couldn't find anything, so
> I came up with:
>
>   def age(born):
>       now = date.today()
>       birthday = date(now.year, born.month, born.day)

Bad luck if the punter was born on 29 Feb and the current year is not a
leap year.

>       return now.year - born.year - (birthday > now and 1 or 0)

Holy code bloat, Batman! Try this:

    return now.year - born.year - (birthday > now)

>
> i.e. calculate the "raw" years first and subtract one if he hasn't had
> his birthday yet this year... It works, but I'd rather use a standard
> and generic approach if it exists...?
>

It's the irregular-size months that cause the problems. If you can work
out the months difference, then just floor_div by 12 to get the years
difference.

Below is some code from the ancient times when everybody and his dog
each had their own date class :-)

HTH,
John

8<--- methods from a date class

   def months_until(self, to_date):
      """Return number of months between from_date (self) and to_date.
      """
      from_date = self
      signum = 1
      if from_date > to_date:
         from_date, to_date = to_date, from_date
         signum = -1
      d1, m1, y1 = from_date.day, from_date.month, from_date.year
      d2, m2, y2 = to_date.day, to_date.month, to_date.year
      mdiff = (y2 - y1) * 12 + m2 - m1
      if d2 < d1 and (d2 < 28 or d2 != last_day_of_month(y2, m2)):
         # the test d2 < 28 is not necessary; it is an optimisation
         # to avoid calling last_day_of_month unnecessarily
         mdiff = mdiff - 1
      return mdiff * signum

   def years_until(self, to_date):
      """Return number of years between from_date (self) and to_date.
      """
      md = self.months_until(to_date)
      if md >= 0:
         return md // 12
      else:
         # ensure division truncates towards zero
         return -((-md) // 12)

8<--- module-level functions and constants

# days in month
_dim = (None, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)

def last_day_of_month(y, m):
   """Return day (1..31) which is last day of month m in year y
   """
   if m == 2:
      return 28 + _leap(y)
   else:
      if not (1 <= m <= 12):
         raise DateError, "month not in 1..12"
      return _dim[m]

def _leap(y):
   if y % 4: return 0
   if y % 100: return 1
   if y % 400: return 0
   return 1

8<----




More information about the Python-list mailing list