Tạo một phạm vi ngày trong Python


374

Tôi muốn tạo một danh sách các ngày, bắt đầu từ hôm nay và quay trở lại một số ngày tùy ý, ví dụ, trong ví dụ của tôi 100 ngày. Có cách nào tốt hơn để làm điều này hơn không?

import datetime

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

Câu trả lời:


459

Tốt hơn một chút ...

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

6
Điều này không hoạt động nếu bạn sử dụng thời gian nhận biết múi giờ và có sự thay đổi của DST !!! Ý tôi là, khi bạn sử dụng ngày bắt đầu và ngày kết thúc và điền vào giữa cách này ...
gabn88

@ s-lott bạn không thể sử dụng range(0, numdays)trong trường hợp này?
Lukas Juhrich

3
Tôi đã sử dụng phương pháp này theo một cách hơi khác, để tạo ra các khoảng thời gian mười, 5 phút, +/- một số giá trị (trong 30 giây trong trường hợp của tôi). Tôi chỉ hình dung tôi muốn chia sẻ: datetimes = [start + timedelta(seconds=x*60+randint(-30, 30)) for x in range (0, range_end*5, 5)]nơi biếnrange_end = 10
MikeyE

247

Pandas là tuyệt vời cho chuỗi thời gian nói chung và có hỗ trợ trực tiếp cho phạm vi ngày.

Ví dụ pd.date_range():

import pandas as pd
from datetime import datetime

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

Nó cũng có rất nhiều lựa chọn để làm cho cuộc sống dễ dàng hơn. Ví dụ: nếu bạn chỉ muốn các ngày trong tuần, bạn sẽ chỉ trao đổi bdate_range.

Xem tài liệu về phạm vi ngày

Ngoài ra, nó hỗ trợ đầy đủ các múi giờ pytz và có thể chuyển nhịp DST mùa xuân / mùa thu một cách trơn tru.

EDIT bởi OP:

Nếu bạn cần datetimes python thực tế, trái ngược với dấu thời gian của 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")

Điều này sử dụng tham số "kết thúc" để khớp với câu hỏi ban đầu, nhưng nếu bạn muốn ngày giảm dần:

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

22
Defo câu trả lời tốt nhất nếu gấu trúc đã được sử dụng. Điều duy nhất tôi muốn thêm là bạn sẽ sử dụng tham số end = nếu ngày của bạn sẽ bị ngược lại theo bài đăng gốc. pd.date_range (end = pd.datetime.today (), period = 100) .tolist ()
Thomas Browne

5
Một vấn đề với các phương thức này là khi bạn cần loại Python Datetime ... Sử dụng các phương thức phạm vi ngày của Pandas hoặc numpy sẽ tạo ra các kiểu Timestamp và Datetime64 ... Bạn cần list(map(pd.Timestamp.to_pydatetime, datelist))lấy lại kiểu datetime ...
Malik Koné

Các pandas.datetimelớp học là lỗi thời và sẽ bị xoá khỏi gấu trúc trong một phiên bản tương lai. Nhập từ datetimemô-đun thay thế.
Trenton McKinney

82

Nhận phạm vi ngày giữa ngày bắt đầu và ngày kết thúc được chỉ định (Tối ưu hóa cho độ phức tạp của thời gian & không gian):

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
Đề xuất ban đầu là sử dụng () thay vì [] để lấy date_generator. Hiệu quả theo nghĩa sẽ không cần lưu trữ toàn bộ mảng ngày và chỉ tạo một ngày khi cần.
Sandeep

2
Lưu ý end_date không được tạo.
Thorbjørn Ravn Andersen

8
sử dụng (end-start + 1) để lấy ngày kết thúc.
Sandeep

6
Không trả lời câu hỏi của OP, nhưng đó là những gì tôi đã theo dõi :)
Thierry J.

2
(end-start+1)không hoạt động, bạn không thể thêm timedelta và int. Bạn có thể sử dụng (end-start).days + 1mặc dù.
Stephen Paulger

44

Bạn có thể viết một hàm tạo trả về các đối tượng ngày bắt đầu từ hôm nay:

import datetime

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

Trình tạo này trả về ngày bắt đầu từ hôm nay và quay ngược từng ngày một. Dưới đây là cách thực hiện 3 ngày đầu tiên:

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

Ưu điểm của phương pháp này so với việc hiểu vòng lặp hoặc danh sách là bạn có thể quay lại bao nhiêu lần tùy ý.

Biên tập

Một phiên bản nhỏ gọn hơn sử dụng biểu thức trình tạo thay vì hàm:

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

Sử dụng:

>>> 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
Máy phát điện là cách lý tưởng để làm việc này, nếu số ngày là tùy ý.
xssChauhan

32

yeah, phát minh lại bánh xe .... chỉ cần tìm kiếm diễn đàn và bạn sẽ nhận được một cái gì đó như thế này:

from dateutil import rrule
from datetime import datetime

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

22
Yêu cầu labix.org/python-dateutil . Nhìn đẹp nhưng hầu như không đáng để phụ thuộc bên ngoài chỉ để lưu một dòng.
Beni Cherniavsky-Paskin

1
Không thể tin bất cứ ai những câu trả lời khác là rất pythonic. Trong khi rrule là một cái tên khủng khiếp, thì cái này ít nhất là dễ nhìn.
thuyền viên

7
@ BeniCéciavsky-Paskin: rất dễ dàng để giới thiệu các lỗi tinh vi trong khi thực hiện số học thời gian (lịch). dateutil.rrulethực hiện iCalWiki RFC - dễ dàng hơn khi sử dụng các hàm với hành vi được xác định rõ thay vì thực hiện nhiều chức năng gần như giống nhau mà hơi khác nhau. dateutil.rrulecho phép hạn chế sửa lỗi ở một nơi duy nhất.
JFS

3
@ Mark0978: rruletên không độc đoán; đó là từ rfc tương ứng
jfs

1
Phải, tôi đã không đổ lỗi cho dateutil vì sự lựa chọn không may của tên :-)
Boatcoder

32

Bạn cũng có thể sử dụng thứ tự ngày để làm cho nó đơn giản hơn:

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

Hoặc như được đề xuất trong các bình luận, bạn có thể tạo một danh sách như thế này:

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

18

Từ tiêu đề của câu hỏi này, tôi đã mong đợi tìm thấy một cái gì đó như thế range(), điều đó sẽ cho phép tôi chỉ định hai ngày và tạo một danh sách với tất cả các ngày ở giữa. Theo cách đó, người ta không cần tính số ngày giữa hai ngày đó, nếu người ta không biết trước.

Vì vậy, với nguy cơ hơi lạc đề, lớp lót này thực hiện công việc:

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

Tất cả các khoản tín dụng cho câu trả lời này !


1
Đó không phải là một câu trả lời nhiều hơn nhiều câu trả lời khác cho câu hỏi.
Thomas Browne

4
Tôi chưa bao giờ giả vờ rằng đây là một câu trả lời nhiều hơn những câu trả lời khác, đây cũng không phải là một giải pháp tốt hơn. Tôi đã cho thấy một đoạn mã làm một cái gì đó hơi khác so với những gì bạn yêu cầu nhưng tôi sẽ rất vui khi tìm thấy ở đây với tiêu đề của câu hỏi.
Snake_charmer

11

Đây là một câu trả lời hơi khác so với câu trả lời của S.Lott đưa ra danh sách các ngày giữa hai ngày startend. Trong ví dụ dưới đây, từ đầu năm 2017 đến hôm nay.

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

Tôi biết một chút câu trả lời muộn, nhưng tôi cũng gặp vấn đề tương tự và quyết định rằng chức năng phạm vi nội bộ của Python hơi thiếu về mặt này nên tôi đã ghi đè nó trong mô-đun tiện dụng của tôi.

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
Tôi nghĩ rằng bạn muốn output = []và trao đổi các dòng trong while cmp(...)vòng lặp. So sánh range(0,10,1)_range(0,10,1).
sigfpe

3
Tôi nghĩ rằng câu trả lời này sẽ tốt hơn nếu bạn đặt tên hàm date_range.
Brandon Bradley

7

Dựa trên câu trả lời tôi đã viết cho chính mình điều này:

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

Đầu ra:

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

Sự khác biệt là tôi có được dateđối tượng '', không phải đối tượng ' datetime.datetime'.


7

Nếu có hai ngày và bạn cần phạm vi thử

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

Đây là ý chính tôi đã tạo, từ mã của riêng tôi, điều này có thể giúp ích. (Tôi biết câu hỏi quá cũ, nhưng những người khác có thể sử dụng nó)

https://gist.github.com/2287345

(điều tương tự dưới đây)

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

Đây là một lớp lót cho các tập lệnh bash để có danh sách các ngày trong tuần, đây là python 3. Dễ dàng sửa đổi cho bất cứ điều gì, int ở cuối là số ngày trong quá khứ bạn muốn.

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

Đây là một biến thể để cung cấp ngày bắt đầu (hay đúng hơn là kết thúc)

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

Đây là một biến thể cho ngày bắt đầu và ngày kết thúc tùy ý. không phải là điều này không hiệu quả khủng khiếp, nhưng tốt cho việc đưa vào một vòng lặp for trong một tập lệnh bash:

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

Có thể muốn cập nhật bài trả lời của bạn với mã trong bình luận của bạn. Python được đọc sai trong các bình luận và kinda cần định dạng.
Jake Bathman

3

Matplotlib liên quan

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

Tôi biết điều này đã được trả lời, nhưng tôi sẽ đưa ra câu trả lời của mình cho các mục đích lịch sử, và vì tôi nghĩ rằng nó là thẳng về phía trước.

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

Chắc chắn nó sẽ không giành được bất cứ thứ gì như golf-code, nhưng tôi nghĩ nó rất thanh lịch.


Các arangebước với khá đẹp, nhưng listOfDatesbao gồm datetime64 numpy thay vì datetimes python.
F.Raab

2
Tuy nhiên, bạn có thể sử dụng np.arange(…).astype(dt.datetime)để arangetrả về datetime python gốc thay vì datetime64 numpy.
F.Raab

2

Một ví dụ khác là đếm tiến hoặc lùi, bắt đầu từ câu trả lời của 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)

cho

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

cho

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

Lưu ý rằng ngày bắt đầu được bao gồm trong phần hoàn trả, vì vậy nếu bạn muốn có bốn tổng số ngày, hãy sử dụng 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

Một trình tạo phạm vi ngày hàng tháng với datetimedateutil. Đơn giản và dễ hiểu:

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
Anh muốn quay lại, không tiến lên .. Vậy là endnên base - datetime.timedelta. Hơn nữa ... Tại sao giải pháp này tốt hơn giải pháp ban đầu?
frarugi87 16/07/2015

0

Từ những câu trả lời trên tôi đã tạo ra ví dụ này cho trình tạo ngày

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.