用Python生成给定范围内的所有日期

35

我有两个字符串变量,它们包含如下格式的日期,即 yyyy-mm-dd:

date1 = '2011-05-03'
date2 = '2011-05-10'

我想编写代码以生成在日期date1到date2范围内的所有日期。如何在Python中实现?


2
请返回翻译后的文本:在Python中创建日期范围 - Manuel Salvadores
1
@msa 可能是,但不是那一个。 - badp
可能是在Python中迭代一系列日期的重复问题。 - kasperd
9个回答

56

36

日期可以像数字一样相互比较,您可以使用datetime.timedelta对象进行日期相关的数学运算。在这里没有使用dateutil的理由,也没有硬编码类似于'range(9)'的迭代次数的原因。这与处理普通数字的方式非常相似。

>>> import datetime
>>> date1 = '2011-05-03'
>>> date2 = '2011-05-10'
>>> start = datetime.datetime.strptime(date1, '%Y-%m-%d')
>>> end = datetime.datetime.strptime(date2, '%Y-%m-%d')
>>> step = datetime.timedelta(days=1)
>>> while start <= end:
...     print start.date()
...     start += step
... 
2011-05-03
2011-05-04
2011-05-05
2011-05-06
2011-05-07
2011-05-08
2011-05-09
2011-05-10
>>> 

22

假设你的日期已经是 datetime.date 类型,你可以使用 .fromordinal.toordinal 来创建这个一行代码。

from datetime import date

start_date = date(2011, 5, 3)
end_date = date(2011, 5, 10)

[date.fromordinal(i) for i in range(start_date.toordinal(), end_date.toordinal())]

结果是排除 end_date。对于包含end_date在内的范围,请使用end_date.toordinal() + 1


您可以通过将上述代码转换为生成器来轻松实现。

from datetime import date


def date_range(x, y, inclusive=False):

    inclusive_nr = 1 if inclusive else 0

    if isinstance(x, date) and isinstance(y, date):

        for i in range(x.toordinal(), y.toordinal() + inclusive_nr):
            yield date.fromordinal(i)

    else:
        raise TypeError("Parameters x and y should be dates.")

例子

>>> from datetime import date
>>> end_date = date(2011, 5, 10)
>>> start_date = date(2011, 5, 3)
>>> [str(d) for d in date_range(start_date, end_date, inclusive=True)]
['2011-05-03', '2011-05-04', '2011-05-05', '2011-05-06', '2011-05-07', '2011-05-08', '2011-05-09', '2011-05-10']

21
from dateutil import rrule, parser

date1 = '2011-05-03'
date2 = '2011-05-10'

dates = list(rrule.rrule(rrule.DAILY,
                         dtstart=parser.parse(date1),
                         until=parser.parse(date2)))

print dates

由于dateutil不是标准库,您需要将其作为单独的软件包进行安装。请参阅文档以获取有关格式(特别是dayfirstyearfirst开关)的进一步详细信息。


1
不错。解析器如何知道它是yyyy-mm-dd还是yyyy-dd-mm? - Tim Pietzcker

7
import datetime
real_date1 = datetime.date(*[int(x) for x in date1.split('-')])
real_date2 = datetime.date(*[int(x) for x in date2.split('-')])
date_range =  real_date2 - real_date1
dates = list()
for days in xrange(date_range.days):
    dates.append(real_date1 + datetime.timedelta(days))

print dates

如果使用 Python 3, 请使用 range 而不是 xrange


3

我喜欢这个选择是因为它直观易懂,并且提供了一系列的日期字符串。

import re
import datetime

def datetime_to_str_date(dt):
    return re.sub(r'\T.+$','', dt.isoformat())

start_date = datetime.datetime.strptime('2016-01-01', '%Y-%m-%d')
end_date = datetime.datetime.today()
num_of_days = (end_date - start_date).days

date_list = map(
        datetime_to_str_date, 
        [start_date + datetime.timedelta(days=x) for x in range(0, num_of_days)]
)

3
import time

def dates_between(start, end):
  start_epoch = int(time.mktime(time.strptime(start, "%Y-%m-%d")))
  end_epoch = int(time.mktime(time.strptime(end, "%Y-%m-%d"))) + 1 #include end
  return range(start_epoch, end_epoch, 86400)

4
这个代码在不是86,400个时代秒长的日子里会出问题,但我并不像Jon Skeet一样牛逼,无法确定这个假设是否“足够”正确。 - badp

1
>>> for a in range(9):
...     print(datetime.date(2011, 05, 03) + datetime.timedelta(a))
...
2011-05-03
2011-05-04
2011-05-05
2011-05-06
2011-05-07
2011-05-08
2011-05-09
2011-05-10
2011-05-11

我不太确定字符串的解析是否是关键,还是你提出问题的方式。如果是前者,请忽略这个过于简化的答案。


-4
import pandas as pd
date1 = '2011-05-03'
date2 = '2011-05-10'
pd.date_range(start = date1,end = date2)

2
欢迎来到Stack Overflow!虽然我们感谢您的回答,但如果它能在其他答案的基础上提供额外的价值会更好。在这种情况下,您的答案并没有提供额外的价值,因为另一个用户已经发布了那个解决方案。如果之前的答案对您有帮助,您应该投票支持它,而不是重复相同的信息。 - Toby Speight

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接