python-3.xrefactoringintervalstime-tracking

How to correctly calculate time intervals between given sections of time?


For a time tracking application, the day is split into five sections:

0:00 - 6:00, 6:00 - 14:00, 14:00 - 20:00, 20:00 - 23:00, 23:00 - (infinity)

These five "bins" need to be filled according to time spent in either of these. For instance the interval in question starts at 5:00 and ends at 16:00, bin #1 contains 1 hour, bin #2 contains 8 hours, bin #3 contains 2 hours, bin #4 contains 0 hours, bin #5 contains 0 hours. Any amount of time, beyond 23:00 goes into bin #5.

So far I came up with this:

    sections = [ 0, 0, 0, 0, 0 ]
    for tframe in numericdata:
        if tframe[0] < 6.00: # starts before 6:00
            if tframe[1] >= 6.00: # ends after 6:00
                sections[0] += 6.00 - tframe[0]
            else: # ends before 6:00
                sections[0] += tframe[1] - tframe[0]
                continue

            if tframe[1] >= 14.00: # ends after 14:00
                sections[1] += 14.00 - 6.00
            else: # ends between 6:00 and 14:00
                sections[1] += tframe[1] - 6.00
                continue

            if tframe[1] >= 20.00: # ends after 20:00
                sections[2] += 20.00 - 14.00
            else: # ends between 14:00 and 20:00
                sections[2] += tframe[1] - 14.00
                continue

            if tframe[1] >= 23.00: # ends after 23:00
                sections[3] += 23.00 - 20.00
                sections[4] += tframe[1] - 23.00
            else: # ends between 20:00 and 23:00
                sections[3] += tframe[1] - 20.00
                continue

        elif tframe[0] < 14.00: # starts between 6:00 and 14:00
            if tframe[1] >= 14.00: # ends after 14:00
                sections[1] += 14.00 - tframe[0]
            else: # ends before 14:00
                sections[1] += tframe[1] - tframe[0]
                continue

            if tframe[1] >= 20.00: # ends after 20:00
                sections[2] += 20.00 - 14.00
            else: # ends between 14:00 and 20:00
                sections[2] += tframe[1] - 14.00
                continue

            if tframe[1] >= 23.00: # ends after 23:00
                sections[3] += 23.00 - 20.00
                sections[4] += tframe[1] - 23.00
            else: # ends between 20:00 and 23:00
                sections[3] += tframe[1] - 20.00
                continue

        elif tframe[0] < 20.00: # starts between 14:00 and 20:00
            if tframe[1] >= 20.00: # ends after 20:00
                sections[2] += 20.00 - tframe[0]
            else: # ends before 20:00
                sections[2] += tframe[1] - tframe[0]
                continue

            if tframe[1] >= 23.00: # ends after 23:00
                sections[3] += 23.00 - 20.00
                sections[4] += tframe[1] - 23.00
            else: # ends between 20:00 and 23:00
                sections[3] += tframe[1] - 20.00
                continue

        elif tframe[0] < 23.00: # starts between 20:00 and 23:00
            if tframe[1] >= 23.00: # ends after 23:00
                sections[3] += 23.00 - tframe[0]
                sections[4] += tframe[1] - 23.00
            else: # ends before 23:00
                sections[3] += tframe[1] - tframe[0]
                continue

        else: # starts and ends some time after 23:00
            sections[4] += tframe[1] - tframe[0]

numericdata is an array containing intervals as tuples of start and end times. All time values have been converted to hours with fractions, so 13:15 is encoded as 13.25, etc. As an example numericdata may contain [ [ 6.75, 12.5 ], [ 13.5, 18.25 ] ], so two intervals, one from 6:45 to 12:30, and another one from 13:30 to 18:15. The resulting sections array would look like this: [ 0, 6.25, 4.25, 0, 0 ]

I feel like there must be a better way to do this, than what I've come up with. It's entirely hardcoded, and I can't come up with constructing something, that reduces code duplication, and perhaps is a bit more flexible, such as defining the amount of bins, and their lengths rather than hardcoding them like that.

Thanks in advance!


Solution

  • I hope I understood your question correctly. The split hours are defined in list splits, so it's configurable:

    data = [[ 6.75, 12.5 ], [ 13.5, 18.25 ]]
    splits = [0, 6, 14, 20, 23, float('inf')]
    
    def intersection(a, b, c, d):
        if a > d or b < c:
            return 0 # no intersection
        left, right = max(a, c), min(b, d)
        return right - left
    
    out = [sum(v) for v in zip(*[[intersection(*i, *s) for s in zip(splits, splits[1::])] for i in data])]
    
    print(out)
    

    Prints:

    [0, 6.25, 4.25, 0, 0]