Quantize a datetime to integer years, months, days, hours, minutes, seconds or microseconds

    Also works with a `datetime.timetuple` or `time.struct_time` or a 1to9-tuple of ints or floats.
    Also works with a sequenece of struct_times, tuples, or datetimes

    >>> quantize_datetime(datetime.datetime(1970,1,2,3,4,5,6), resolution=3)
    datetime.datetime(1970, 1, 2, 0, 0)

    Notice that 6 is the highest resolution value with any utility
    >>> quantize_datetime(datetime.datetime(1970,1,2,3,4,5,6), resolution=7)
    datetime.datetime(1970, 1, 2, 3, 4, 5)
    >>> quantize_datetime(datetime.datetime(1971,2,3,4,5,6,7), 1)
    datetime.datetime(1971, 1, 1, 0, 0)

def quantize_datetime(dt, resolution=None):
    """Quantize a datetime to integer years, months, days, hours, minutes, seconds or microseconds

    Also works with a `datetime.timetuple` or `time.struct_time` or a 1to9-tuple of ints or floats.
    Also works with a sequenece of struct_times, tuples, or datetimes

    >>> quantize_datetime(datetime.datetime(1970,1,2,3,4,5,6), resolution=3)
    datetime.datetime(1970, 1, 2, 0, 0)

    Notice that 6 is the highest resolution value with any utility
    >>> quantize_datetime(datetime.datetime(1970,1,2,3,4,5,6), resolution=7)
    datetime.datetime(1970, 1, 2, 3, 4, 5)
    >>> quantize_datetime(datetime.datetime(1971,2,3,4,5,6,7), 1)
    datetime.datetime(1971, 1, 1, 0, 0)
    """
    # FIXME: this automatically truncates off microseconds just because timtuple() only goes out to sec
    resolution = int(resolution or 6)
    if hasattr(dt, 'timetuple'):
        dt = dt.timetuple()  # strips timezone info

    if isinstance(dt, time.struct_time):
        # strip last 3 fields (tm_wday, tm_yday, tm_isdst)
        dt = list(dt)[:6]
        # struct_time has no microsecond, but accepts float seconds
        dt += [int((dt[5] - int(dt[5])) * 1000000)]
        dt[5] = int(dt[5])
        return datetime.datetime(*(dt[:resolution] + [1] * max(3 - resolution, 0)))

    if isinstance(dt, tuple) and len(dt) <= 9 and all(isinstance(val, (float, int)) for val in dt):
        dt = list(dt) + [0] * (max(6 - len(dt), 0))
        # if the 6th element of the tuple looks like a float set of seconds need to add microseconds
        if len(dt) == 6 and isinstance(dt[5], float):
            dt = list(dt) + [1000000 * (dt[5] - int(dt[5]))]
            dt[5] = int(dt[5])
        dt = tuple(int(val) for val in dt)
        return datetime.datetime(*(dt[:resolution] + [1] * max(resolution - 3, 0)))

    return [quantize_datetime(value) for value in dt]