我有以下代码来做到这一点,但我如何能做得更好?现在我认为它比嵌套循环更好,但是当您在列表理解中使用生成器时,它开始变得像perl一行程序。

day_count = (end_date - start_date).days + 1
for single_date in [d for d in (start_date + timedelta(n) for n in range(day_count)) if d <= end_date]:
    print strftime("%Y-%m-%d", single_date.timetuple())

笔记

我不是用这个来打印的。这只是为了演示。 start_date和end_date变量是datetime。date对象,因为我不需要时间戳。(它们将用于生成报告)。

样例输出

开始日期为2009-05-30,结束日期为2009-06-09:

2009-05-30
2009-05-31
2009-06-01
2009-06-02
2009-06-03
2009-06-04
2009-06-05
2009-06-06
2009-06-07
2009-06-08
2009-06-09

当前回答

如果你打算使用动态timedelta,那么你可以使用:

1. 使用while循环

def datetime_range(start: datetime, end: datetime, delta: timedelta) -> Generator[datetime, None, None]:
    while start <= end:
        yield start
        start += delta

2. 使用for循环

from datetime import datetime, timedelta
from typing import Generator


def datetime_range(start: datetime, end: datetime, delta: timedelta) -> Generator[datetime, None, None]:
    delta_units = int((end - start) / delta)

    for _ in range(delta_units + 1):
        yield start
        start += delta

3.如果你正在使用async/await

async def datetime_range(start: datetime, end: datetime, delta: timedelta) -> AsyncGenerator[datetime, None]:
    delta_units = int((end - start) / delta)

    for _ in range(delta_units + 1):
        yield start
        start += delta

4. 列表理解

def datetime_range(start: datetime, end: datetime, delta: timedelta) -> List[datetime]:
    delta_units = int((end - start) / delta)
    return [start + (delta * index) for index in range(delta_units + 1)]

那么1和2解可以简单地像这样使用

start = datetime(2020, 10, 10, 10, 00)
end = datetime(2022, 10, 10, 18, 00)
delta = timedelta(minutes=30)

result = [time_part for time_part in datetime_range(start, end, delta)]
# or 
for time_part in datetime_range(start, end, delta):
    print(time_part)

3- 3 / 3解决方案可以在异步上下文中使用。因为它运行一个异步生成器对象,该对象只能在异步上下文中使用

start = datetime(2020, 10, 10, 10, 00)
end = datetime(2022, 10, 10, 18, 00)
delta = timedelta(minutes=30)

result = [time_part async for time_part in datetime_range(start, end, delta)]

async for time_part in datetime_range(start, end, delta):
    print(time_part)

这些解决方案的优点是它们都使用了动态的timedelta。这在你不知道你将得到哪个时间增量的情况下非常有用。

其他回答

使用dateutil库:

from datetime import date
from dateutil.rrule import rrule, DAILY

a = date(2009, 5, 30)
b = date(2009, 6, 9)

for dt in rrule(DAILY, dtstart=a, until=b):
    print dt.strftime("%Y-%m-%d")

这个python库有许多更高级的特性,其中一些非常有用,比如相对增量,并且被实现为单个文件(模块),很容易包含到项目中。

import datetime

def daterange(start, stop, step_days=1):
    current = start
    step = datetime.timedelta(step_days)
    if step_days > 0:
        while current < stop:
            yield current
            current += step
    elif step_days < 0:
        while current > stop:
            yield current
            current += step
    else:
        raise ValueError("daterange() step_days argument must not be zero")

if __name__ == "__main__":
    from pprint import pprint as pp
    lo = datetime.date(2008, 12, 27)
    hi = datetime.date(2009, 1, 5)
    pp(list(daterange(lo, hi)))
    pp(list(daterange(hi, lo, -1)))
    pp(list(daterange(lo, hi, 7)))
    pp(list(daterange(hi, lo, -7))) 
    assert not list(daterange(lo, hi, -1))
    assert not list(daterange(hi, lo))
    assert not list(daterange(lo, hi, -7))
    assert not list(daterange(hi, lo, 7)) 
import datetime
from dateutil.rrule import DAILY,rrule

date=datetime.datetime(2019,1,10)

date1=datetime.datetime(2019,2,2)

for i in rrule(DAILY , dtstart=date,until=date1):
     print(i.strftime('%Y%b%d'),sep='\n')

输出:

2019Jan10
2019Jan11
2019Jan12
2019Jan13
2019Jan14
2019Jan15
2019Jan16
2019Jan17
2019Jan18
2019Jan19
2019Jan20
2019Jan21
2019Jan22
2019Jan23
2019Jan24
2019Jan25
2019Jan26
2019Jan27
2019Jan28
2019Jan29
2019Jan30
2019Jan31
2019Feb01
2019Feb02

我也有类似的问题,但我需要每月而不是每天迭代一次。

这就是我的解

import calendar
from datetime import datetime, timedelta

def days_in_month(dt):
    return calendar.monthrange(dt.year, dt.month)[1]

def monthly_range(dt_start, dt_end):
    forward = dt_end >= dt_start
    finish = False
    dt = dt_start

    while not finish:
        yield dt.date()
        if forward:
            days = days_in_month(dt)
            dt = dt + timedelta(days=days)            
            finish = dt > dt_end
        else:
            _tmp_dt = dt.replace(day=1) - timedelta(days=1)
            dt = (_tmp_dt.replace(day=dt.day))
            finish = dt < dt_end

示例# 1

date_start = datetime(2016, 6, 1)
date_end = datetime(2017, 1, 1)

for p in monthly_range(date_start, date_end):
    print(p)

输出

2016-06-01
2016-07-01
2016-08-01
2016-09-01
2016-10-01
2016-11-01
2016-12-01
2017-01-01

例# 2

date_start = datetime(2017, 1, 1)
date_end = datetime(2016, 6, 1)

for p in monthly_range(date_start, date_end):
    print(p)

输出

2017-01-01
2016-12-01
2016-11-01
2016-10-01
2016-09-01
2016-08-01
2016-07-01
2016-06-01

为什么有两个嵌套迭代?对我来说,它只用一次迭代就产生了相同的数据列表:

for single_date in (start_date + timedelta(n) for n in range(day_count)):
    print ...

没有列表被存储,只有一个生成器被迭代。此外,生成器中的“if”似乎是不必要的。

毕竟,线性序列应该只需要一个迭代器,而不是两个。

与John Machin讨论后更新:

也许最优雅的解决方案是使用生成器函数来完全隐藏/抽象日期范围内的迭代:

from datetime import date, timedelta

def daterange(start_date, end_date):
    for n in range(int((end_date - start_date).days)):
        yield start_date + timedelta(n)

start_date = date(2013, 1, 1)
end_date = date(2015, 6, 2)
for single_date in daterange(start_date, end_date):
    print(single_date.strftime("%Y-%m-%d"))

注意:为了与内置的range()函数保持一致,此迭代在到达end_date之前停止。因此,对于包容性迭代使用第二天,就像使用range()一样。