Zenhack.net

More DST Bugs

08 Apr 2016

A few weeks ago, Iron Blogger broke again. At the time I noticed that there were negative debts on the ledger, but didn’t have time to look into it. Being between contract gigs, I finally found some time today to dig into it. Big surprise; it was another daylight savings time bug. Basically, in order to work out how late a post is, there’s was something like this:

rounds_late = divide_timedelta(duedate(post) - post.counts_for), ROUND_LEN)

The above is somewhat simplified, but the essentials are intact. Basically, find the difference between the two times and divide by the length of a round (which happens to be one week). divide_timedelta exists because there’s no built-in division method for timedelta objects. It’s implementation was:

def divide_timedelta(numerator, denominator):
    """Divide two timedelta objects.

    The timedelta type doesn't have it's own divide operator, which
    is something we need in a few places.

    Both numerator and denominator *must* be whole-second quantities.

    The return value is of type int, not timedelta.
    """
    # total_seconds returns a floating point value, but for our purposes it's
    # always going to be an integer, and we don't want to deal with  precision
    # errors, so we convert before dividing.
    return int(numerator.total_seconds())/int(denominator.total_seconds())

A couple things to note:

  1. The docstring has a mistake; it should actually be whole week (or round) quantities.
  2. We rely on property (1) to allow us to just do integer division, keeping things simple.

Okay, here’s the bug: a week isn’t actually a constant unit of time. Most of the time it’s 24 hours * 7 days, but if it spans a DST boundary, it can be off by an hour in either direction. If it’s an hour shorter, integer division will round down, and we’ll get an off-by-one error. The upshot of this is that after the last party everybody was tagged as owing one-post’s worth of money less than they really did – even if they owed nothing to begin with.

The bug has been fixed now, and I caught a couple of other mistakes in the code while I was digging; they didn’t seem to be causing any immediate symptoms, but were clearly incorrect.

Date and time handling has been a constant source of problems for Iron Blogger, and I’m thinking about ways of making it less error-prone; see Issue #59. In addition to causing hiccups in operation and taking up some of my time, this has necessitated more caution when making improvements than I’d like. Making the date-and-time handling routines more robust will be well worth the effort, I think.

I’m also re-committing myself to the promise I made (and broke) about dedicating time to Iron Blogger. I’ve actually blocked off the time for this on my calendar, so I hope I’ll be more successful in sticking to it.