在Python中创建日期范围

591

我想创建一个日期列表,从今天开始,向前推100天。有没有比这更好的方法?

import datetime

a = datetime.datetime.today()
numdays = 100
dateList = []
for x in range (0, numdays):
    dateList.append(a - datetime.timedelta(days = x))
print dateList
23个回答

709

略微改进了一点...

base = datetime.datetime.today()
date_list = [base - datetime.timedelta(days=x) for x in range(numdays)]

如果我想获取前7个日期,不包括今天的日期怎么办?@S.Lott - Mohammad Amir
1
@MohammadAmir date_list = [base - datetime.timedelta(days=x+1) for x in range(7)]日期列表 = [基准日期 - datetime.timedelta(days=x+1) for x in range(7)] - Corvax
4
改正:好多了。 - Jonah

446

Pandas 通常用于时间序列,并直接支持日期范围。

例如pd.date_range()

import pandas as pd
from datetime import datetime

datelist = pd.date_range(datetime.today(), periods=100).tolist()

此外,它还有许多选项可以使生活更加轻松。例如,如果你只想要工作日,你只需要切换到bdate_range

请查看日期范围文档

此外,它完全支持pytz时区,并且可以平稳地跨越春/秋季节变化。

编辑者注:

如果您需要实际的Python日期时间(而不是Pandas时间戳):

import pandas as pd
from datetime import datetime

pd.date_range(end = datetime.today(), periods = 100).to_pydatetime().tolist()

#OR

pd.date_range(start="2018-09-09",end="2020-02-02")

使用“end”参数来匹配原始问题,如果您想要降序排列日期:

pd.date_range(datetime.today(), periods=100).to_pydatetime().tolist()

如果你想将数据范围转换为 DataFrame(因为它是一个 DatetimeIndex),你可以使用 to_frame() 方法:pd.date_range(start='2018-09-09', end='2020-02-02').to_frame(name='dates') - Bilbottom
你有没有想过为什么pd.date_range会被弃用,如果Python datetime中没有等效的方法?FutureWarning: The pandas.datetime class is deprecated and will be removed from pandas in a future version. - Bill
是的,我一直在想为什么没有一个严格的 datetime 解决方案,但我找不到 datetime 中的同样功能。 - bart cubrich
@Bill 我在 pandas 1.5.3 中没有收到关于 pd.date_range 的警告;也许你在其他地方使用了 pd.datetime?请参考 https://dev59.com/rlIH5IYBdhLWcg3wKKBU#60856931 - fantabolous
@fantabolous,是的,我不知道我在那里做什么。我想我有点困惑了。请忽略我的评论。 - Bill

142

获取指定起始日期和结束日期之间的日期范围(针对时间和空间复杂度进行了优化):

import datetime

start = datetime.datetime.strptime("21-06-2014", "%d-%m-%Y")
end = datetime.datetime.strptime("07-07-2014", "%d-%m-%Y")
date_generated = [start + datetime.timedelta(days=x) for x in range(0, (end-start).days)]

for date in date_generated:
    print date.strftime("%d-%m-%Y")

8
最初的建议是使用()而不是[]来获取日期生成器。这样做的效率在于无需存储整个日期数组,只有在需要时才生成一个日期。 - Sandeep
3
请注意,end_date未生成。 - Thorbjørn Ravn Andersen
10
使用公式 (end-start+1) 来获取结束日期。 - Sandeep
9
没有回答原帖提出的问题,但这正是我所需要的 :) - Thierry J.
11
(end-start+1) 不可行,你不能将时间差和整数相加。但是你可以使用 (end-start).days + 1 - Stephen Paulger

51

你可以编写一个生成器函数,从今天开始返回日期对象:

import datetime

def date_generator():
  from_date = datetime.datetime.today()
  while True:
    yield from_date
    from_date = from_date - datetime.timedelta(days=1)

这个生成器返回从今天开始每次向后一天的日期。以下是如何获取前三个日期:

>>> import itertools
>>> dates = itertools.islice(date_generator(), 3)
>>> list(dates)
[datetime.datetime(2009, 6, 14, 19, 12, 21, 703890), datetime.datetime(2009, 6, 13, 19, 12, 21, 703890), datetime.datetime(2009, 6, 12, 19, 12, 21, 703890)]

使用这种方法的好处是,与循环或列表推导式相比,您可以返回任意次数。

编辑

使用生成器表达式而不是函数的更紧凑版本:

date_generator = (datetime.datetime.today() - datetime.timedelta(days=i) for i in itertools.count())

使用方法:

>>> dates = itertools.islice(date_generator, 3)
>>> list(dates)
[datetime.datetime(2009, 6, 15, 1, 32, 37, 286765), datetime.datetime(2009, 6, 14, 1, 32, 37, 286836), datetime.datetime(2009, 6, 13, 1, 32, 37, 286859)]

2
生成器是做这件事的理想方式,如果天数是任意的。 - xssChauhan

44

32
需要使用http://labix.org/python-dateutil。看起来很不错,但为了节省一行代码而增加外部依赖似乎并不值得。 - Beni Cherniavsky-Paskin
9
在实现周期(日历)算术时,很容易引入微妙的错误。"dateutil.rrule" 实现了 iCalendar RFC - 使用具有明确定义行为的函数比使用几乎相同但略有不同的多个实现更容易。"dateutil.rrule" 可以将错误限制在一个地方进行修复。 - jfs
5
@Mark0978说:“rrule”这个名称不是随意取的,它来自于相应的rfc文件 - jfs
1
总的来说可以理解,但对于楼主想要精确跳N天的简单目标,大多数解决方案中使用的 date + timedelta 似乎是一个明确定义的表达方式。我很想了解实际存在的错误。 - Beni Cherniavsky-Paskin
2
@BeniCherniavsky-Paskin,这就是你的错误所在。那个日期并不存在。 - Adam Barnes
显示剩余3条评论

41

您还可以使用天序数来简化操作:

def date_range(start_date, end_date):
    for ordinal in range(start_date.toordinal(), end_date.toordinal()):
        yield datetime.date.fromordinal(ordinal)

或者根据评论中的建议,您可以创建这样的列表:

date_range = [
    datetime.date.fromordinal(ordinal) 
    for ordinal in range(
        start_date.toordinal(),
        end_date.toordinal(),
    )
]

29

从这个问题的标题上,我期望找到类似于range()的东西,它能让我指定两个日期并创建出包含这两个日期之间所有日期的列表。这样一来,如果事先不知道这两个日期之间相差的天数,也可以完成任务。

因此,冒着些微离题的风险,这个一行代码就可以完成任务:

import datetime
start_date = datetime.date(2011, 1, 1)
end_date   = datetime.date(2014, 1, 1)

dates_2011_2013 = [ start_date + datetime.timedelta(n) for n in range(int ((end_date - start_date).days))]

所有功劳归于此回答


3
这不是一行代码,就像其他回答这个问题的许多答案一样。 - Thomas Browne
11
我从未假装这只是比其他回答更简单的解决方案,也没有说这是更好的解决方案。我展示了一段代码,它执行的操作略有不同于你要求的,但鉴于问题的标题,如果能在这里找到这样的答案,我会很高兴的。 - snake_charmer

14
以下是基于S.Lott的回答而得出的稍有不同的答案,它会给出在两个日期 startend之间的日期列表。 在下面的示例中,从2017年初到今天。
start = datetime.datetime(2017,1,1)
end = datetime.datetime.today()
daterange = [start + datetime.timedelta(days=x) for x in range(0, (end-start).days)]

11

如果有两个日期并且需要获取它们的范围,请尝试以下方法:

from dateutil import rrule, parser
date1 = '1995-01-01'
date2 = '1995-02-28'
datesx = list(rrule.rrule(rrule.DAILY, dtstart=parser.parse(date1), until=parser.parse(date2)))

9

根据我为自己写下的答案:

import datetime;
print [(datetime.date.today() - datetime.timedelta(days=x)).strftime('%Y-%m-%d') for x in range(-5, 0)]

输出:

['2017-12-11', '2017-12-10', '2017-12-09', '2017-12-08', '2017-12-07']

区别在于,我得到的是 'date' 对象,而不是 'datetime.datetime' 对象。

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