How to find difference in years between two dates?

Discussion in 'Python' started by thebjorn, Jul 26, 2006.

  1. thebjorn

    thebjorn Guest

    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)
    return now.year - born.year - (birthday > now and 1 or 0)

    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...?

    -- bjorn
     
    thebjorn, Jul 26, 2006
    #1
    1. Advertising

  2. thebjorn

    John Machin Guest

    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<----
     
    John Machin, Jul 26, 2006
    #2
    1. Advertising

  3. 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)
    > return now.year - born.year - (birthday > now and 1 or 0)
    >
    > 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...?


    You may want to have a look at mxDatetime, which has a RelativeDateTime
    type that seems to do what you want:
    http://www.egenix.com/files/python/mxDateTime.html


    --
    bruno desthuilliers
    python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
    p in ''.split('@')])"
     
    Bruno Desthuilliers, Jul 26, 2006
    #3
  4. thebjorn

    Roy Smith Guest

    "thebjorn" <> wrote:

    > def age(born):
    > now = date.today()
    > birthday = date(now.year, born.month, born.day)
    > return now.year - born.year - (birthday > now and 1 or 0)


    I don't get that last line. There's two things in particular that are
    puzzling me.

    1) What does "birthday > now" mean? It sounds like you haven't been born
    yet.

    2) I find the "and 1 or 0" part very confusing. I can't remember all the
    minor rules about operator precedence, but I'm sure this works out to some
    clever hack involving boolean short-circuit evaluation to get around the
    lack of a ternary operator in python. If I need to pull out the reference
    manual to decipher what an expression means, it's too complicated. Try
    something like:

    if birthday > now:
    return now.year - born.year - 1
    else:
    return now.year - born.year

    It takes up a little more space, but it's bog easy to understand without
    scratching your head or diving into the manual to refresh your memory of
    obscure language details.
     
    Roy Smith, Jul 26, 2006
    #4
  5. thebjorn

    John Machin Guest

    Bruno Desthuilliers wrote:
    > 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)
    > > return now.year - born.year - (birthday > now and 1 or 0)
    > >
    > > 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...?

    >
    > You may want to have a look at mxDatetime, which has a RelativeDateTime
    > type that seems to do what you want:
    > http://www.egenix.com/files/python/mxDateTime.html
    >


    Which pieces of the following seem to be working to you?

    >>> import mx.DateTime
    >>> f = mx.DateTime.RelativeDateTimeDiff
    >>> d = mx.DateTime.Date
    >>> f(d(2000, 2, 29), d(2001, 2, 28))

    <RelativeDateTime instance for 'YYYY-(-11)-(-28) HH:MM:SS' at 0xaee170>
    >>> f(d(2000, 2, 29), d(2001, 3, 1))

    <RelativeDateTime instance for '(-0001)-MM-(-01) HH:MM:SS' at 0xb06530>
    >>> f(d(2001, 1, 31), d(2001, 2, 28))

    <RelativeDateTime instance for 'YYYY-MM-(-28) HH:MM:SS' at 0xaee170>
    >>> g = lambda x, y: f(y, x)
    >>> g(d(2000, 2, 29), d(2001, 2, 28))

    <RelativeDateTime instance for 'YYYY-(+11)-(+30) HH:MM:SS' at 0xb06580>
    >>> g(d(2000, 2, 29), d(2001, 3, 1))

    <RelativeDateTime instance for '(+0001)-MM-DD HH:MM:SS' at 0xaee170>
    >>> g(d(2001, 1, 31), d(2001, 2, 28))

    <RelativeDateTime instance for 'YYYY-MM-(+28) HH:MM:SS' at 0xb06580>
    >>>


    and going the other way, adding one month to 31 January gives you some
    date in March which is ludicrous.
     
    John Machin, Jul 26, 2006
    #5
  6. John Machin wrote:
    > Bruno Desthuilliers wrote:
    >
    >>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)
    >>> return now.year - born.year - (birthday > now and 1 or 0)
    >>>
    >>>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...?

    >>
    >>You may want to have a look at mxDatetime, which has a RelativeDateTime
    >>type that seems to do what you want:
    >>http://www.egenix.com/files/python/mxDateTime.html
    >>

    >
    >
    > Which pieces of the following seem to be working to you?


    John, it seems you failed to notice the use of "may" and "seems" in my
    post. IIRC, both are supposed to strongly suggest a lack of certitude.


    --
    bruno desthuilliers
    python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
    p in ''.split('@')])"
     
    Bruno Desthuilliers, Jul 26, 2006
    #6
  7. thebjorn

    John Machin Guest

    Bruno Desthuilliers wrote:
    > John Machin wrote:
    > > Bruno Desthuilliers wrote:

    >
    > >
    > > Which pieces of the following seem to be working to you?

    >
    > John, it seems you failed to notice the use of "may" and "seems" in my
    > post. IIRC, both are supposed to strongly suggest a lack of certitude.
    >
    >


    I didn't fail to notice that you were seeming. Re-read my question:
    It's asking you which bits you were seeming.
     
    John Machin, Jul 26, 2006
    #7
  8. thebjorn

    Guest

    Roy Smith:
    > 2) I find the "and 1 or 0" part very confusing. I can't remember all the
    > minor rules about operator precedence, but I'm sure this works out to some
    > clever hack involving boolean short-circuit evaluation to get around the
    > lack of a ternary operator in python. If I need to pull out the reference
    > manual to decipher what an expression means, it's too complicated. Try
    > something like:


    >From the manual, 5.10:
    >(Note that neither and nor or restrict the value and type they return to False and True, but rather return the last evaluated argument. This is sometimes useful, e.g., if s is a string that should be replaced by a default value if it is empty, the expression s or 'foo' yields the desired value. Because not has to invent a value anyway, it does not bother to return a value of the same type as its argument, so e.g., not 'foo' yields False, not ''.)<


    Then are such things something good to remove from Python 3.0 (making
    or and and always return True or False), to simplify the language, and
    make it more clear and reduce the possibility of bugs?

    Bye,
    bearophile
     
    , Jul 26, 2006
    #8
  9. John Machin wrote:
    > Bruno Desthuilliers wrote:
    >
    >>John Machin wrote:
    >>
    >>>Bruno Desthuilliers wrote:

    >>
    >>>Which pieces of the following seem to be working to you?

    >>
    >>John, it seems you failed to notice the use of "may" and "seems" in my
    >>post. IIRC, both are supposed to strongly suggest a lack of certitude.
    >>

    >
    > I didn't fail to notice that you were seeming. Re-read my question:
    > It's asking you which bits you were seeming.


    OP problem:
    age = (date.today() - born).year

    Possible solution:

    import mx.DateTime as dt
    def age(date):
    return dt.Age(dt.today(), date).years
    born = dt.Date(1967, 5, 1)
    assert age(born) == 39



    --
    bruno desthuilliers
    python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
    p in ''.split('@')])"
     
    Bruno Desthuilliers, Jul 26, 2006
    #9
  10. thebjorn

    thebjorn Guest

    Roy Smith wrote:
    > "thebjorn" <> wrote:
    >
    > > def age(born):
    > > now = date.today()
    > > birthday = date(now.year, born.month, born.day)
    > > return now.year - born.year - (birthday > now and 1 or 0)

    >
    > I don't get that last line. There's two things in particular that are
    > puzzling me.
    >
    > 1) What does "birthday > now" mean? It sounds like you haven't been born
    > yet.


    I'm making a (perhaps tenous) semantic distinction between birthdate,
    the date you were born on, and birthday, an annual event that may or
    may not have happened yet this year :)

    > 2) I find the "and 1 or 0" part very confusing. I can't remember all the
    > minor rules about operator precedence, but I'm sure this works out to some
    > clever hack involving boolean short-circuit evaluation to get around the
    > lack of a ternary operator in python.


    You're correct :) the line was originally:

    return now.year - born.year - (1 if birthday > now else 0)

    which gave a nice traceback on the production server that didn't have
    2.5 on it :-( The and/or short-circuit is a fairly well established
    (yet divisive) pydiom, and I was going to say something about people
    really ought to learn a few simple precedence rules, but then I
    realized the parenthesis aren't needed in the above <ehm..> The
    parenthesis are needed in a version someone else mentioned:

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

    but I wouldn't write that, just like I wouldn't write 1 + True..

    > If I need to pull out the reference manual to decipher what an expression means,
    > it's too complicated.


    Nah, that's a little too restrictive I think. I will agree that the
    and/or is more cute than functional, at least in this case. Since it
    could also throw, how about:

    def yeardiff(a, b):
    y = a.year - b.year
    if (a.month, a.day) < (b.month, b.day): # tuple comparison
    y -= 1
    return y

    def age(born): return yeardiff(date.today(), born)

    > if birthday > now:
    > return now.year - born.year - 1
    > else:
    > return now.year - born.year


    I prefer to hoist the common expression out of the branches so they
    don't have an opportunity to get out of sync, but I get your point.

    -- bjorn
     
    thebjorn, Jul 26, 2006
    #10
  11. thebjorn

    thebjorn Guest

    John Machin wrote:
    > thebjorn wrote:

    [...]
    > >
    > > 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.


    Good catch! Thanks!

    [..]
    > Holy code bloat, Batman! Try this:
    >
    > return now.year - born.year - (birthday > now)


    yuck :)

    [...]
    > 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.


    I don't agree that the irregular sized months cause a problem in this
    case. They do cause a problem if you're asking "when is today + one
    month?", i.e. there isn't an unambiguous answer to that question in
    general (e.g. if today was January 31). We're asking a different kind
    of question though: "has it been at least one month since January 31?",
    the answer would be no on Feb 29 and yes on Mar 1.

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

    [...]

    Wow. I'm speechless. (any reason you didn't want to use the calendar
    module?)

    -- bjorn
     
    thebjorn, Jul 26, 2006
    #11
  12. thebjorn

    thebjorn Guest

    Bruno Desthuilliers wrote:
    [...]
    > Possible solution:
    >
    > import mx.DateTime as dt
    > def age(date):
    > return dt.Age(dt.today(), date).years
    > born = dt.Date(1967, 5, 1)
    > assert age(born) == 39


    dealbreaker:

    >>> age(datetime.date(1970,5,2))

    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    File "c:\python24\lib\site-packages\mx\DateTime\DateTime.py", line
    842, in RelativeDateTimeDiff
    diff = date1 - date2
    TypeError: unsupported operand type(s) for -: 'DateTime' and
    'datetime.date'

    I'm getting data from a database, and conversions are out of the
    question for something like this. Otherwise it's a fine library :)

    -- bjorn
     
    thebjorn, Jul 26, 2006
    #12
  13. On 2006-07-26 17:50:43, thebjorn wrote:

    > I don't agree that the irregular sized months cause a problem in this
    > case. They do cause a problem if you're asking "when is today + one
    > month?", i.e. there isn't an unambiguous answer to that question in
    > general (e.g. if today was January 31). We're asking a different kind
    > of question though: "has it been at least one month since January 31?",
    > the answer would be no on Feb 29 and yes on Mar 1.


    It's still ambiguous. That's why pretty much no library offers months as
    units of date differences. They offer days (like DateTimeDelta in
    mxDateTime does).

    For example, the mxDateTime guys say about their RelativeDateTime (which
    allows months as differences):
    """
    Note that dates like Date(1999,1,30) + RelativeDateTime(months=+1) are not
    supported. The package currently interprets these constructions as
    Date(1999,2,1) + 30, thus giving the 1999-03-02 which may not be what you'd
    expect.
    """

    I think your original approach (difference of years, adjusted for whether
    the date of the birthday has already passed in the current year or not) is
    the unambiguous way to get the age. Other methods based on date differences
    in days need careful adjustments for odd cases involving leap years.

    Gerhard
     
    Gerhard Fiedler, Jul 26, 2006
    #13
  14. thebjorn

    John Machin Guest

    thebjorn wrote:
    > John Machin wrote:
    > > thebjorn wrote:

    > [...]
    > > >
    > > > 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.

    >
    > Good catch! Thanks!


    Easy catch -- happens at least one per inning :)

    >
    > [..]
    > > Holy code bloat, Batman! Try this:
    > >
    > > return now.year - born.year - (birthday > now)

    >
    > yuck :)


    But this:
    return now.year - born.year - (birthday > now and 1 or 0)
    is not yuck???

    >
    > [...]
    > > 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.

    >
    > I don't agree that the irregular sized months cause a problem in this
    > case. They do cause a problem if you're asking "when is today + one
    > month?", i.e. there isn't an unambiguous answer to that question in
    > general (e.g. if today was January 31). We're asking a different kind
    > of question though: "has it been at least one month since January 31?",
    > the answer would be no on Feb 29 and yes on Mar 1.


    If a bank were paying you interest on a monthly basis, and you
    deposited money on Jan 31 and pulled it out on the last day of
    February, that would count as one month. This is what I call the "today
    - yesterday == 1" rule. For computing things like duration of employee
    service, you need the "today - yesterday == 2" rule -- on the
    assumption that service counts from start of business yesterday to
    close of business today. So hire date of 1 Feb to fire date of (last
    day of Feb) would count as one month.

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

    > [...]
    >
    > Wow. I'm speechless. (any reason you didn't want to use the calendar
    > module?)


    Sorry, I don't understand. Why are you speechless? What would I want to
    use the calendar module for? Apart from the leap() function and the
    table of days in a month, the calendar module doesn't have any of the
    functionality that one would expect in a general-purpose date class.
     
    John Machin, Jul 27, 2006
    #14
  15. thebjorn wrote:
    > Bruno Desthuilliers wrote:
    > [...]
    >
    >>Possible solution:
    >>
    >>import mx.DateTime as dt
    >>def age(date):
    >> return dt.Age(dt.today(), date).years
    >>born = dt.Date(1967, 5, 1)
    >>assert age(born) == 39

    >
    >
    > dealbreaker:
    >
    >>>>age(datetime.date(1970,5,2))

    >

    (snip traceback)

    What about:
    age(dt.Date(1970,5,2))

    > I'm getting data from a database, and conversions
    > are out of the
    > question for something like this.


    Which conversion ? How do you get the data ? as a datetime object ? as a
    (y,m,d) tuple ? as a "y-m-d" string ? Else ?



    --
    bruno desthuilliers
    python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
    p in ''.split('@')])"
     
    Bruno Desthuilliers, Jul 27, 2006
    #15
  16. thebjorn

    thebjorn Guest

    John Machin wrote:
    > thebjorn wrote:
    > > John Machin wrote:
    > > > thebjorn wrote:

    [...]
    > > > Holy code bloat, Batman! Try this:
    > > >
    > > > return now.year - born.year - (birthday > now)

    > >
    > > yuck :)

    >
    > But this:
    > return now.year - born.year - (birthday > now and 1 or 0) is not yuck???


    Correct.

    > > [...]
    > > > 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.

    > >
    > > I don't agree that the irregular sized months cause a problem in this
    > > case. They do cause a problem if you're asking "when is today + one
    > > month?", i.e. there isn't an unambiguous answer to that question in
    > > general (e.g. if today was January 31). We're asking a different kind
    > > of question though: "has it been at least one month since January 31?",
    > > the answer would be no on Feb 29 and yes on Mar 1.

    >
    > If a bank were paying you interest on a monthly basis, and you
    > deposited money on Jan 31 and pulled it out on the last day of
    > February, that would count as one month. This is what I call the "today
    > - yesterday == 1" rule. For computing things like duration of employee
    > service, you need the "today - yesterday == 2" rule -- on the
    > assumption that service counts from start of business yesterday to
    > close of business today. So hire date of 1 Feb to fire date of (last
    > day of Feb) would count as one month.


    You give a good argument that the concept of a month is fuzzy, I still
    don't believe that it makes the concept of a year fuzzy (or that the
    fuzziness of month needs to be accounted for when computing years).

    > Sorry, I don't understand. Why are you speechless? What would I want to
    > use the calendar module for? Apart from the leap() function and the
    > table of days in a month, the calendar module doesn't have any of the
    > functionality that one would expect in a general-purpose date class.


    Well, I thought replacing a 4 line function with 31 lines, 13 of which
    duplicated functionality in the standard library was overkill... I came
    up with this yesterday which seems sufficient?

    def yeardiff(a, b):
    y = a.year - b.year
    if (a.month, a.day) < (b.month, b.day): # tuple comparison
    y -= 1
    return y

    -- bjorn
     
    thebjorn, Jul 27, 2006
    #16
  17. thebjorn

    John Machin Guest

    thebjorn wrote:
    > John Machin wrote:
    > > thebjorn wrote:
    > > > John Machin wrote:
    > > > > thebjorn wrote:

    > [...]
    > > > > Holy code bloat, Batman! Try this:
    > > > >
    > > > > return now.year - born.year - (birthday > now)
    > > >
    > > > yuck :)

    > >
    > > But this:
    > > return now.year - born.year - (birthday > now and 1 or 0) is not yuck???

    >
    > Correct.
    >
    > > > [...]
    > > > > 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.
    > > >
    > > > I don't agree that the irregular sized months cause a problem in this
    > > > case. They do cause a problem if you're asking "when is today + one
    > > > month?", i.e. there isn't an unambiguous answer to that question in
    > > > general (e.g. if today was January 31). We're asking a different kind
    > > > of question though: "has it been at least one month since January 31?",
    > > > the answer would be no on Feb 29 and yes on Mar 1.

    > >
    > > If a bank were paying you interest on a monthly basis, and you
    > > deposited money on Jan 31 and pulled it out on the last day of
    > > February, that would count as one month. This is what I call the "today
    > > - yesterday == 1" rule. For computing things like duration of employee
    > > service, you need the "today - yesterday == 2" rule -- on the
    > > assumption that service counts from start of business yesterday to
    > > close of business today. So hire date of 1 Feb to fire date of (last
    > > day of Feb) would count as one month.

    >
    > You give a good argument that the concept of a month is fuzzy


    Sorry, I can't imagine where you got "fuzzy" from. Perhaps you mean
    some other word. The concept is capable of being expressed precisely.

    > I still
    > don't believe that it makes the concept of a year fuzzy (or that the
    > fuzziness of month needs to be accounted for when computing years).


    The point is to ensure that (say) 24 months of employment and 2 years
    of employment are determined in a consistent fashion.

    >
    > > Sorry, I don't understand. Why are you speechless? What would I want to
    > > use the calendar module for? Apart from the leap() function and the
    > > table of days in a month, the calendar module doesn't have any of the
    > > functionality that one would expect in a general-purpose date class.

    >
    > Well, I thought replacing a 4 line function with 31 lines, 13 of which
    > duplicated functionality in the standard library was overkill.


    I think you missed the point that the lines I quoted were straight out
    of a self-contained library that existed (in C as well as Python) way
    before the datetime module was a gleam in Fred & the timbot's eyes.
    Even if I had noticed a leap year function in the calendar module, I
    would probably not have used it. The Python version of the module was
    just a stopgap while I fiddled with getting a C extension going. The C
    leap year function doesn't have any of that modulo stuff in it.

    > I came
    > up with this yesterday which seems sufficient?
    >
    > def yeardiff(a, b):
    > y = a.year - b.year
    > if (a.month, a.day) < (b.month, b.day): # tuple comparison
    > y -= 1
    > return y


    At least it doesn't blow up when b is leapyear-02-29. It just gives the
    wrong answer when a is nonleapyear-02-28. E.g. it gives 0 years
    difference from 1992-02-29 to 1993-02-28 instead of 1.
     
    John Machin, Jul 27, 2006
    #17
  18. thebjorn

    thebjorn Guest

    John Machin wrote:
    > thebjorn wrote:

    [...]
    > > You give a good argument that the concept of a month is fuzzy

    >
    > Sorry, I can't imagine where you got "fuzzy" from. Perhaps you mean
    > some other word. The concept is capable of being expressed precisely.


    and the second to last date in January plus a month is..?

    > > > Sorry, I don't understand. Why are you speechless? What would I want to
    > > > use the calendar module for? Apart from the leap() function and the
    > > > table of days in a month, the calendar module doesn't have any of the
    > > > functionality that one would expect in a general-purpose date class.

    > >
    > > Well, I thought replacing a 4 line function with 31 lines, 13 of which
    > > duplicated functionality in the standard library was overkill.

    >
    > I think you missed the point that the lines I quoted were straight out
    > of a self-contained library that existed (in C as well as Python) way
    > before the datetime module was a gleam in Fred & the timbot's eyes.


    I'm guessing you missed that I was looking for a method in the stdlib
    to do this, and failing that an idiomatic solution...

    > Even if I had noticed a leap year function in the calendar module, I
    > would probably not have used it. The Python version of the module was
    > just a stopgap while I fiddled with getting a C extension going. The C
    > leap year function doesn't have any of that modulo stuff in it.


    I'm sure it doesn't. You might want to look at the assembly your C
    compiler produces for a modulo-power-of-2 operation...

    > > I came up with this yesterday which seems sufficient?
    > >
    > > def yeardiff(a, b):
    > > y = a.year - b.year
    > > if (a.month, a.day) < (b.month, b.day): # tuple comparison
    > > y -= 1
    > > return y

    >
    > At least it doesn't blow up when b is leapyear-02-29. It just gives the
    > wrong answer when a is nonleapyear-02-28. E.g. it gives 0 years
    > difference from 1992-02-29 to 1993-02-28 instead of 1.


    I believe you're mistaken (but feel free to correct me), my grandmother
    is born on Feb 29 and a quick call to my dad verified that they
    celebrated it the day after the 28th (unless Mar 1 was a Monday ;-).
    http://timeanddate.com/date/duration.html also seem to agree with my
    current understanding, just as a datapoint if nothing else.

    -- bjorn
     
    thebjorn, Jul 27, 2006
    #18
  19. thebjorn

    thebjorn Guest

    Bruno Desthuilliers wrote:
    > Which conversion ? How do you get the data ? as a datetime object ? as a
    > (y,m,d) tuple ? as a "y-m-d" string ? Else ?


    All input routines, whether they're from a web-form, database, command
    line, or anywhere else, only produce objects from the datetime module
    for calendar data. That way the program logic doesn't have to guess
    which format it's getting... I suppose I could do something like:

    def age(born):
    mx_born = mx.DateTime.Date(born.year, born.month, born.day)
    ...

    -- bjorn
     
    thebjorn, Jul 27, 2006
    #19
  20. thebjorn

    John Machin Guest

    thebjorn wrote:
    > John Machin wrote:
    > > thebjorn wrote:

    > [...]
    > > > You give a good argument that the concept of a month is fuzzy

    > >
    > > Sorry, I can't imagine where you got "fuzzy" from. Perhaps you mean
    > > some other word. The concept is capable of being expressed precisely.

    >
    > and the second to last date in January plus a month is..?


    The last day in February, according to me and also according to the
    website that you mention later, e.g.
    """
    >From date: Thursday, January 30, 1992

    Added 1 month
    Resulting date: Saturday, February 29, 1992
    """

    The precise rule is that if the original day is later than the last day
    of the target month, clip it to that last day.


    >
    > > Even if I had noticed a leap year function in the calendar module, I
    > > would probably not have used it. The Python version of the module was
    > > just a stopgap while I fiddled with getting a C extension going. The C
    > > leap year function doesn't have any of that modulo stuff in it.

    >
    > I'm sure it doesn't. You might want to look at the assembly your C
    > compiler produces for a modulo-power-of-2 operation...


    I don't need to. AFAIR just about every compiler I've used (BDS C on a
    Z80 is a likely exception) has generated "& mask" (with extra mucking
    about if the first operand is signed). Leap year calc involves % 100
    and % 400. Factoring out the powers of two leaves 25. Now tell me how
    I'm managing that without a modulo operation.

    >
    > > > I came up with this yesterday which seems sufficient?
    > > >
    > > > def yeardiff(a, b):
    > > > y = a.year - b.year
    > > > if (a.month, a.day) < (b.month, b.day): # tuple comparison
    > > > y -= 1
    > > > return y

    > >
    > > At least it doesn't blow up when b is leapyear-02-29. It just gives the
    > > wrong answer when a is nonleapyear-02-28. E.g. it gives 0 years
    > > difference from 1992-02-29 to 1993-02-28 instead of 1.

    >
    > I believe you're mistaken (but feel free to correct me), my grandmother
    > is born on Feb 29 and a quick call to my dad verified that they
    > celebrated it the day after the 28th (unless Mar 1 was a Monday ;-).
    > http://timeanddate.com/date/duration.html also seem to agree with my
    > current understanding, just as a datapoint if nothing else.


    hmm ... let's look at more than 1 data point from that website:

    easy cases Jan 1993 to Feb 1993:
    Jan 10 to Feb 9: 30d (excluding last day) 1m 0d (including last day)
    Jan 10 to Feb 10: 1m 0d (excluding last day) 1m 1d (including last
    day)
    Jan 10 to Feb 11: 1m 1d (excluding last day) 1m 2d (including last
    day)
    looks reasonable, consistent, no anomalies; note how 1m 0d (excluding)
    is "predicted" from the right and below.

    Now try from Jan 31 1993:
    Jan 31 to Feb 27: 27d (ex) 28d (in)
    Jan 31 to Feb 28: 28d (ex) 1m 1d (in)
    Jan 31 to Mar 01: 1m 1d (ex) 1m 2d (in)
    So 1 day short of 1m 1d is not 1m 0 d??? I'd call this unreasonable,
    inconsistent, anomalous -- especially when on the same website you do
    1993-01-31 plus 1 month, it gives you 1993-02-28 (as I'd expect).
     
    John Machin, Jul 28, 2006
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. David Lozzi

    Dates dates dates dates... SQL and ASP.NET

    David Lozzi, Sep 29, 2005, in forum: ASP .Net
    Replies:
    1
    Views:
    908
    Rob Schieber
    Sep 30, 2005
  2. Prafull Soni
    Replies:
    1
    Views:
    695
    osmium
    Mar 7, 2005
  3. PW

    Dates! Dates! Dates!

    PW, Aug 7, 2004, in forum: ASP General
    Replies:
    4
    Views:
    234
    Mark Schupp
    Aug 9, 2004
  4. Li Chen
    Replies:
    2
    Views:
    129
    Li Chen
    Mar 4, 2010
  5. Bambero

    counting years between two dates

    Bambero, Nov 19, 2004, in forum: Javascript
    Replies:
    7
    Views:
    205
    Dr John Stockton
    Nov 21, 2004
Loading...

Share This Page