在Python中创建日期范围


373

我想创建一个日期列表,从今天开始,然后返回任意天数,例如在我的示例中为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

Answers:


458

略胜一筹...

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

6
如果您使用可识别时区的日期时间并且DST发生了变化,则此方法将无效!我的意思是,当您使用开始日期和结束日期并以这种方式填写时…
gabn88

@ s-lott range(0, numdays)在这种情况下不能使用吗?
Lukas Juhrich

3
我以略有不同的方式使用此方法,以生成十个,五分钟的间隔,+ /-某个值(在我的情况下为30秒)。我只是想分享一下:datetimes = [start + timedelta(seconds=x*60+randint(-30, 30)) for x in range (0, range_end*5, 5)]变量range_end = 10
MikeyE

246

Pandas 一般而言,它非常适合时间序列,并且直接支持日期范围。

例如pd.date_range()

import pandas as pd
from datetime import datetime

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

它还具有许多使生活更轻松的选择。例如,如果您只想要工作日,则只需交换bdate_range

请参阅日期范围文档

此外,它完全支持pytz时区,并且可以平滑地跨越春季/秋季DST偏移。

OP编辑:

如果您需要实际的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()

22
如果已经在使用熊猫,请给最好的答案。我唯一要补充的是,如果您的日期按照原始帖子的时间倒退,则将使用end =参数。pd.date_range(end = pd.datetime.today(),周期= 100).tolist()
Thomas Browne

5
这种方法的问题是当您需要Python Datetime类型...使用Pandas或numpy日期范围方法将创建Timestamp和Datetime64类型...您需要list(map(pd.Timestamp.to_pydatetime, datelist))恢复datetime类型...
MalikKoné17

pandas.datetime班已取消,将在未来版本的熊猫被删除。从datetime模块导入。
特伦顿·麦金尼

82

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

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")

6
最初的建议是使用()而不是[]来获取date_generator。从某种意义上讲,这是高效的,无需存储整个日期数组,仅在需要时才生成一个日期。
桑迪普2014年

2
请注意,不会生成end_date。
托尔比约恩Ravn的安徒生

8
使用(end-start + 1)获取结束日期。
Sandeep

6
没有回答OP的问题,但这就是我所追求的:)
Thierry J.

2
(end-start+1)不起作用,您无法添加timedelta和int。您可以使用(end-start).days + 1
Stephen Paulger

44

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

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)]

1
如果天数是任意的,则生成器是执行此操作的理想方法。
xssChauhan

32

是的,重新发明轮子...。只要在论坛上搜索,您将获得类似以下内容:

from dateutil import rrule
from datetime import datetime

list(rrule.rrule(rrule.DAILY,count=100,dtstart=datetime.now()))

22
需要labix.org/python-dateutil。看起来不错,但几乎不值得仅保留一行的外部依赖项。
贝尼·切尔尼亚夫斯基-帕斯金

1
不能相信任何人,其他答案都是非常Python性的。尽管rrule是一个可怕的名字,但至少在眼中这个名字很容易。
boatcoder

7
@ BeniCherniavsky-Paskin:在实施周期(日历)算法时,容易引入细微的错误。dateutil.rrule实现iCalendar RFC-使用具有明确定义的行为的函数要容易得多,而不是使用几乎相同功能几乎没有太大差异的多个实现。dateutil.rrule允许将错误修复限制在一个地方。
jfs

3
@ Mark0978:rrule名称不是任意的;它来自相应的rfc
jfs

1
是的,我不是在为不幸的名称选择而指责dateutil :-)
boatcoder

32

您还可以使用日序使之更简单:

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(),
    )
]

18

我希望从该问题的标题中找到类似的内容range(),这样我就可以指定两个日期并创建一个包含所有日期的列表。这样,如果事先不知道这两个日期之间的天数,则无需计算。

因此,由于存在偏离主题的风险,因此此一线工作即可:

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

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

此答案的全部功劳!


1
那不是该问题的其他答案。
Thomas Browne

4
我从来没有假装说这比其他答案更单线,也不是一个更好的解决方案。我展示了一段代码,该代码的功能与您所要求的略有不同,但是我很高兴在这里找到问题的标题。
snake_charmer '16

11

这里有一个稍微不同的答案美国洛特的回答,让两个日期之间的日期列表的建设关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)]

7

我知道一个较晚的答案,但是我只是遇到了同样的问题,并决定在这方面缺少Python的内部范围函数,因此我在我的util模块中覆盖了它。

from __builtin__ import range as _range
from datetime import datetime, timedelta

def range(*args):
    if len(args) != 3:
        return _range(*args)
    start, stop, step = args
    if start < stop:
        cmp = lambda a, b: a < b
        inc = lambda a: a + step
    else:
        cmp = lambda a, b: a > b
        inc = lambda a: a - step
    output = [start]
    while cmp(start, stop):
        start = inc(start)
        output.append(start)

    return output

print range(datetime(2011, 5, 1), datetime(2011, 10, 1), timedelta(days=30))

1
我认为您想要output = []while cmp(...)循环中交换线路。比较range(0,10,1)_range(0,10,1)
sigfpe 2012年

3
我认为,如果您将该函数命名,此答案会更好date_range
布兰登·布拉德利

7

根据我为自己写的答案:

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'一个对象。


7

如果有两个日期,并且您需要范围,请尝试

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)))

5

这是我根据自己的代码创建的要点,这可能会有所帮助。(我知道这个问题太旧了,但是其他人可以使用它)

https://gist.github.com/2287345

(下同)

import datetime
from time import mktime

def convert_date_to_datetime(date_object):
    date_tuple = date_object.timetuple()
    date_timestamp = mktime(date_tuple)
    return datetime.datetime.fromtimestamp(date_timestamp)

def date_range(how_many=7):
    for x in range(0, how_many):
        some_date = datetime.datetime.today() - datetime.timedelta(days=x)
        some_datetime = convert_date_to_datetime(some_date.date())
        yield some_datetime

def pick_two_dates(how_many=7):
    a = b = convert_date_to_datetime(datetime.datetime.now().date())
    for each_date in date_range(how_many):
        b = a
        a = each_date
        if a == b:
            continue
        yield b, a

4

这是一个供bash脚本获取工作日列表的衬板,它是python3。可以轻松地对其进行修改,最后的int是您想要的过去天数。

python -c "import sys,datetime; print('\n'.join([(datetime.datetime.today() - datetime.timedelta(days=x)).strftime(\"%Y/%m/%d\") for x in range(0,int(sys.argv[1])) if (datetime.datetime.today() - datetime.timedelta(days=x)).isoweekday()<6]))" 10

这是提供开始(或更确切地说,结束)日期的一种变体

python -c "import sys,datetime; print('\n'.join([(datetime.datetime.strptime(sys.argv[1],\"%Y/%m/%d\") - datetime.timedelta(days=x)).strftime(\"%Y/%m/%d \") for x in range(0,int(sys.argv[2])) if (datetime.datetime.today() - datetime.timedelta(days=x)).isoweekday()<6]))" 2015/12/30 10

这是任意开始日期和结束日期的变体。并不是说这不是非常有效,但是对于在bash脚本中放入for循环是有好处的:

python -c "import sys,datetime; print('\n'.join([(datetime.datetime.strptime(sys.argv[1],\"%Y/%m/%d\") + datetime.timedelta(days=x)).strftime(\"%Y/%m/%d\") for x in range(0,int((datetime.datetime.strptime(sys.argv[2], \"%Y/%m/%d\") - datetime.datetime.strptime(sys.argv[1], \"%Y/%m/%d\")).days)) if (datetime.datetime.strptime(sys.argv[1], \"%Y/%m/%d\") + datetime.timedelta(days=x)).isoweekday()<6]))" 2015/12/15 2015/12/30

可能想用注释中的代码更新您的答案。Python的注释混乱不堪,有点需要格式化。
杰克·巴斯曼

3

Matplotlib相关

from matplotlib.dates import drange
import datetime

base = datetime.date.today()
end  = base + datetime.timedelta(days=100)
delta = datetime.timedelta(days=1)
l = drange(base, end, delta)

3

我知道已经回答了,但是出于历史目的,我会给出答案,因为我认为这很简单。

import numpy as np
import datetime as dt
listOfDates=[date for date in np.arange(firstDate,lastDate,dt.timedelta(days=x))]

当然,它不会像代码高尔夫那样赢得胜利,但是我认为它很优雅。


arange与步骤是相当不错的,但listOfDatesnumpy的datetime64而不是python原有的日期时间。
F.Raab

2
但是,您可以np.arange(…).astype(dt.datetime)用来arange返回本机python datetime而不是numpy datetime64。
F.Raab

2

从Sandeep的答案开始,另一个向前或向后计数的示例。

from datetime import date, datetime, timedelta
from typing import Sequence
def range_of_dates(start_of_range: date, end_of_range: date) -> Sequence[date]:

    if start_of_range <= end_of_range:
        return [
            start_of_range + timedelta(days=x)
            for x in range(0, (end_of_range - start_of_range).days + 1)
        ]
    return [
        start_of_range - timedelta(days=x)
        for x in range(0, (start_of_range - end_of_range).days + 1)
    ]

start_of_range = datetime.today().date()
end_of_range = start_of_range + timedelta(days=3)
date_range = range_of_dates(start_of_range, end_of_range)
print(date_range)

[datetime.date(2019, 12, 20), datetime.date(2019, 12, 21), datetime.date(2019, 12, 22), datetime.date(2019, 12, 23)]

start_of_range = datetime.today().date()
end_of_range = start_of_range - timedelta(days=3)
date_range = range_of_dates(start_of_range, end_of_range)
print(date_range)

[datetime.date(2019, 12, 20), datetime.date(2019, 12, 19), datetime.date(2019, 12, 18), datetime.date(2019, 12, 17)]

请注意,开始日期包含在退货中,因此,如果您要总共四个日期,请使用 timedelta(days=3)


1
from datetime import datetime, timedelta
from dateutil import parser
def getDateRange(begin, end):
    """  """
    beginDate = parser.parse(begin)
    endDate =  parser.parse(end)
    delta = endDate-beginDate
    numdays = delta.days + 1
    dayList = [datetime.strftime(beginDate + timedelta(days=x), '%Y%m%d') for x in range(0, numdays)]
    return dayList

1

具有datetime和的每月日期范围生成器dateutil。简单易懂:

import datetime as dt
from dateutil.relativedelta import relativedelta

def month_range(start_date, n_months):
        for m in range(n_months):
            yield start_date + relativedelta(months=+m)

0
import datetime    
def date_generator():
    cur = base = datetime.date.today()
    end  = base + datetime.timedelta(days=100)
    delta = datetime.timedelta(days=1)
    while(end>base):
        base = base+delta
        print base

date_generator()

1
他想回去,不转发..所以end应该是base - datetime.timedelta。而且...为什么这种解决方案比原始解决方案更好?
frarugi87

0

从以上答案中,我为日期生成器创建了此示例

import datetime
date = datetime.datetime.now()
time = date.time()
def date_generator(date, delta):
  counter =0
  date = date - datetime.timedelta(days=delta)
  while counter <= delta:
    yield date
    date = date + datetime.timedelta(days=1)
    counter +=1

for date in date_generator(date, 30):
   if date.date() != datetime.datetime.now().date():
     start_date = datetime.datetime.combine(date, datetime.time())
     end_date = datetime.datetime.combine(date, datetime.time.max)
   else:
     start_date = datetime.datetime.combine(date, datetime.time())
     end_date = datetime.datetime.combine(date, time)
   print('start_date---->',start_date,'end_date---->',end_date)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.