Python 灵活创建日期时间范围的功能

Python function for flexibly creating datetime range

我正在尝试编写一个可以灵活创建连续日期时间对象的函数,类似于标准 range() 函数或 numpy linspace() 函数。 该函数应该能够接受 num 参数或 step 大小参数来确定序列。此外,应该有包含或排除起点和终点的选项。 因此,函数的签名应该是这样的:

import datetime as dt
def datetime_range(start: dt.datetime, stop: dt.datetime, step: dt.timedelta = None, num: int = None, startpoint=True, endpoint=True)

所需的输出应如下所示:

start = dt.datetime.min
stop = start + dt.timedelta(days=1)
num = 4

>>> datetime_range(start, end, num=num, startpoint=True, endpoint=True)
>>> [datetime.datetime(1, 1, 1, 0, 0), datetime.datetime(1, 1, 1, 8, 0), datetime.datetime(1, 1, 1, 16, 0), datetime.datetime(1, 1, 2, 0, 0)]

>>> datetime_range(start, end, num=num, startpoint=False, endpoint=True)
>>> [datetime.datetime(1, 1, 1, 6, 0), datetime.datetime(1, 1, 1, 12, 0), datetime.datetime(1, 1, 1, 18, 0), datetime.datetime(1, 1, 2, 0, 0)]

>>> datetime_range(start, end, num=num, startpoint=True, endpoint=False)
>>> [datetime.datetime(1, 1, 1, 0, 0), datetime.datetime(1, 1, 1, 6, 0), datetime.datetime(1, 1, 1, 12, 0), datetime.datetime(1, 1, 1, 18, 0)]

>>> datetime_range(start, end, num=num, startpoint=False, endpoint=False)
>>> [datetime.datetime(1, 1, 1, 4, 48), datetime.datetime(1, 1, 1, 9, 36), datetime.datetime(1, 1, 1, 14, 24), datetime.datetime(1, 1, 1, 19, 12)]

到目前为止我想出的内容如下。但它没有按预期工作,我卡住了。

def datetime_range(start: dt.datetime, stop: dt.datetime, step: dt.timedelta = None, num: int = None, startpoint=True,endpoint=True):

    assert bool(step) != bool(num), f'only one of step or num must be given'
    delta = stop - start
    if bool(num):
        if endpoint and startpoint:
            div = num - 1
        elif endpoint != startpoint:
            div = num
        else:
            div = num + 1
        step = delta / div
    else:
        if endpoint and startpoint:
            div = delta / step
        elif endpoint != startpoint:
            div = (end - step) / step  # or (delta / step) - 1
        else:
            div = (end - 2 * step) / step  # or (delta/step) -2

    return ((start + (not startpoint) * step) + x * step for x in range(div))

我在 Python 3.8 中工作。也许有人可以给我提示。此外,我怀疑可能有一种比使用 if, elif, else 更优雅的计算 div 的方法。非常感谢任何帮助!

对您的代码的更正:

  • 使用 num 时,返回的项目必须具有长度 num,例如如果 num=4 那么结果列表应该包含 4 个日期时间。所以 .. for x in range(div) 是不正确的,因为 div 可能是 +- num 因此你的代码 returns 只有 3 个项目而预期是 4 个项目的主要原因。
  • 当使用step时,我们不应该担心迭代的总数,因为递增的step是固定的。我们唯一需要做的就是将这些增量计算为 range 的基础,同时考虑是否包含 startpointendpoint
  • 您应该进一步验证输入以确保 stepnum 不为 0 以确保我们前进到 stop 计数而不是停留在由于仅递增 0.
  • 最好在生成器外调整start。这样更直接。
  • 使用bool(num)等条件是没有必要的。我们可以在验证后直接检查num
import datetime as dt


def datetime_range(start: dt.datetime, stop: dt.datetime, step: dt.timedelta = None, num: int = None, startpoint=True,endpoint=True):
    assert None in (step, num), f'only one of step or num must be given'
    if step is not None:
        assert step.total_seconds() > 0, f"Step shouldn't be 0"
    if num is not None:
        assert num > 0, f"Num shouldn't be 0"

    delta = stop - start

    if num:
        if endpoint and startpoint:
            div = num - 1
        elif endpoint or startpoint:
            div = num
        else:
            div = num + 1
        step = delta / div
    else:
        num = delta // step
        if endpoint and startpoint:
            num += 1
        elif endpoint or startpoint:
            pass
        else:
            num -= 1

    if not startpoint:
        start += step

    return (start + (x * step) for x in range(num))


start = dt.datetime.min
stop = start + dt.timedelta(days=1)
num = 4
step = dt.timedelta(hours=4)

print(f"Using num {num}")
print(list(datetime_range(start, stop, num=num, startpoint=True, endpoint=True)))
print(list(datetime_range(start, stop, num=num, startpoint=False, endpoint=True)))
print(list(datetime_range(start, stop, num=num, startpoint=True, endpoint=False)))
print(list(datetime_range(start, stop, num=num, startpoint=False, endpoint=False)))

print(f"\nUsing step {step}")
print(list(datetime_range(start, stop, step=step, startpoint=True, endpoint=True)))
print(list(datetime_range(start, stop, step=step, startpoint=False, endpoint=True)))
print(list(datetime_range(start, stop, step=step, startpoint=True, endpoint=False)))
print(list(datetime_range(start, stop, step=step, startpoint=False, endpoint=False)))

输出

Using num 4
[datetime.datetime(1, 1, 1, 0, 0), datetime.datetime(1, 1, 1, 8, 0), datetime.datetime(1, 1, 1, 16, 0), datetime.datetime(1, 1, 2, 0, 0)]
[datetime.datetime(1, 1, 1, 6, 0), datetime.datetime(1, 1, 1, 12, 0), datetime.datetime(1, 1, 1, 18, 0), datetime.datetime(1, 1, 2, 0, 0)]
[datetime.datetime(1, 1, 1, 0, 0), datetime.datetime(1, 1, 1, 6, 0), datetime.datetime(1, 1, 1, 12, 0), datetime.datetime(1, 1, 1, 18, 0)]
[datetime.datetime(1, 1, 1, 4, 48), datetime.datetime(1, 1, 1, 9, 36), datetime.datetime(1, 1, 1, 14, 24), datetime.datetime(1, 1, 1, 19, 12)]

Using step 4:00:00
[datetime.datetime(1, 1, 1, 0, 0), datetime.datetime(1, 1, 1, 4, 0), datetime.datetime(1, 1, 1, 8, 0), datetime.datetime(1, 1, 1, 12, 0), datetime.datetime(1, 1, 1, 16, 0), datetime.datetime(1, 1, 1, 20, 0), datetime.datetime(1, 1, 2, 0, 0)]
[datetime.datetime(1, 1, 1, 4, 0), datetime.datetime(1, 1, 1, 8, 0), datetime.datetime(1, 1, 1, 12, 0), datetime.datetime(1, 1, 1, 16, 0), datetime.datetime(1, 1, 1, 20, 0), datetime.datetime(1, 1, 2, 0, 0)]
[datetime.datetime(1, 1, 1, 0, 0), datetime.datetime(1, 1, 1, 4, 0), datetime.datetime(1, 1, 1, 8, 0), datetime.datetime(1, 1, 1, 12, 0), datetime.datetime(1, 1, 1, 16, 0), datetime.datetime(1, 1, 1, 20, 0)]
[datetime.datetime(1, 1, 1, 4, 0), datetime.datetime(1, 1, 1, 8, 0), datetime.datetime(1, 1, 1, 12, 0), datetime.datetime(1, 1, 1, 16, 0), datetime.datetime(1, 1, 1, 20, 0)]