Membuat rentang tanggal dengan Python


375

Saya ingin membuat daftar tanggal, dimulai dengan hari ini, dan kembali beberapa hari secara acak, katakanlah, dalam contoh saya 100 hari. Apakah ada cara yang lebih baik untuk melakukannya daripada ini?

import datetime

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

Jawaban:


459

Secara marginal lebih baik ...

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

6
Ini tidak berfungsi jika Anda menggunakan data sadar zona waktu dan ada pergeseran DST !!! Maksud saya, ketika Anda menggunakan tanggal mulai dan berakhir dan mengisinya di antara cara ini ...
gabn88

@ s-lott tidak bisakah Anda hanya menggunakan range(0, numdays)dalam kasus ini?
Lukas Juhrich

3
Saya menggunakan metode ini dengan cara yang sedikit berbeda, untuk menghasilkan sepuluh, interval 5 menit, +/- beberapa nilai (30 detik dalam kasus saya). Saya baru saja membayangkan saya akan berbagi: di datetimes = [start + timedelta(seconds=x*60+randint(-30, 30)) for x in range (0, range_end*5, 5)]mana variabelrange_end = 10
MikeyE

248

Pandas sangat bagus untuk deret waktu secara umum, dan memiliki dukungan langsung untuk rentang tanggal.

Sebagai contoh pd.date_range():

import pandas as pd
from datetime import datetime

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

Ini juga memiliki banyak pilihan untuk membuat hidup lebih mudah. Misalnya, jika Anda hanya menginginkan hari kerja, Anda cukup menukarbdate_range .

Lihat dokumentasi rentang tanggal

Selain itu, ia sepenuhnya mendukung zona waktu pytz dan dapat dengan lancar merentang musim semi / musim gugur DST.

EDIT oleh OP:

Jika Anda membutuhkan datetime python yang sebenarnya, yang bertentangan dengan cap waktu 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")

Ini menggunakan parameter "end" untuk mencocokkan dengan pertanyaan awal, tetapi jika Anda ingin tanggal yang menurun:

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

22
Defo Il jawaban terbaik jika panda sudah digunakan. Satu-satunya hal yang akan saya tambahkan adalah Anda akan menggunakan parameter end = jika tanggal Anda mundur sesuai dengan posting asli. pd.date_range (end = pd.datetime.today (), titik = 100) .tolist ()
Thomas Browne

5
Satu masalah dengan metode ini adalah ketika Anda membutuhkan tipe Python Datetime ... Menggunakan Pandas atau metode rentang tanggal numpy akan membuat tipe Timestamp dan Datetime64 ... Anda perlu list(map(pd.Timestamp.to_pydatetime, datelist))mendapatkan tipe datetime kembali ...
Malik Koné

The pandas.datetimekelas ditinggalkan dan akan dihapus dari panda dalam versi masa depan. Impor dari datetimemodul saja.
Trenton McKinney

82

Dapatkan rentang tanggal antara tanggal mulai dan tanggal yang ditentukan (Dioptimalkan untuk kompleksitas waktu & ruang):

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
Saran awal adalah menggunakan () alih-alih [] untuk mendapatkan date_generator. Efisien dalam arti tidak perlu menyimpan seluruh array tanggal dan menghasilkan satu hanya ketika diperlukan.
Sandeep

2
Perhatikan bahwa end_date tidak dibuat.
Thorbjørn Ravn Andersen

8
gunakan (mulai-awal + 1) untuk mendapatkan tanggal akhir.
Sandeep

6
Tidak menjawab pertanyaan OP, tetapi itulah yang saya kejar :)
Thierry J.

2
(end-start+1)tidak berfungsi, Anda tidak dapat menambahkan timedelta dan int. Anda bisa menggunakannya (end-start).days + 1.
Stephen Paulger

44

Anda dapat menulis fungsi generator yang mengembalikan objek tanggal mulai dari hari ini:

import datetime

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

Generator ini mengembalikan tanggal mulai dari hari ini dan mundur satu hari sekaligus. Berikut ini cara mengambil 3 kencan pertama:

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

Keuntungan dari pendekatan ini daripada pemahaman loop atau daftar adalah bahwa Anda dapat kembali sebanyak yang Anda inginkan.

Edit

Versi yang lebih ringkas menggunakan ekspresi generator alih-alih fungsi:

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

Pemakaian:

>>> 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
Generator adalah cara ideal untuk melakukan ini, jika jumlah hari sewenang-wenang.
xssChauhan

32

ya, temukan kembali roda .... cukup cari di forum dan Anda akan mendapatkan sesuatu seperti ini:

from dateutil import rrule
from datetime import datetime

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

22
Membutuhkan labix.org/python-dateutil . Terlihat bagus tetapi tidak sebanding dengan ketergantungan eksternal hanya untuk menyimpan satu baris.
Beni Cherniavsky-Paskin

1
Tidak dapat mempercayai siapa pun hal jawaban lain sangat pythonic. Meskipun aturan adalah nama yang mengerikan, yang ini paling tidak mudah dilihat.
boatcoder

7
@ BeniCherniavsky-Paskin: sangat mudah untuk memperkenalkan bug halus saat menerapkan aritmatika periode (kalender). dateutil.rrulemengimplementasikan iCalendar RFC - lebih mudah untuk menggunakan fungsi dengan perilaku yang terdefinisi dengan baik daripada beberapa implementasi dari fungsi yang hampir sama yang sedikit berbeda. dateutil.rrulememungkinkan untuk membatasi perbaikan bug ke satu tempat.
jfs

3
@ Mark0978: rrulenama tidak sewenang-wenang; ini dari rfc yang sesuai
jfs

1
Ya, saya tidak menyalahkan dateutil untuk pilihan nama yang disayangkan :-)
boatcoder

32

Anda juga dapat menggunakan ordinal hari untuk membuatnya lebih sederhana:

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

Atau seperti yang disarankan dalam komentar Anda dapat membuat daftar seperti ini:

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

18

Dari judul pertanyaan ini saya berharap menemukan sesuatu seperti range(), yang memungkinkan saya menentukan dua tanggal dan membuat daftar dengan semua tanggal di antaranya. Dengan begitu seseorang tidak perlu menghitung jumlah hari antara dua tanggal itu, jika seseorang tidak mengetahuinya sebelumnya.

Jadi dengan risiko menjadi sedikit di luar topik, one-liner ini bekerja:

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

Semua kredit untuk jawaban ini !


1
Itu bukan satu baris lebih dari banyak jawaban lain untuk pertanyaan itu.
Thomas Browne

4
Saya tidak pernah berpura-pura bahwa ini lebih merupakan satu kalimat daripada jawaban yang lain, tidak juga bahwa ini adalah solusi yang lebih baik. Saya menunjukkan sepotong kode yang melakukan sesuatu yang sedikit berbeda dari apa yang Anda minta tetapi saya akan senang menemukan di sini diberi judul pertanyaan.
snake_charmer

11

Berikut jawaban yang sedikit berbeda dari jawaban S.Lott yang memberikan daftar tanggal antara dua tanggal startdan end. Dalam contoh di bawah ini, dari awal 2017 hingga hari ini.

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

Sedikit jawaban yang terlambat saya tahu, tetapi saya hanya memiliki masalah yang sama dan memutuskan bahwa fungsi jangkauan internal Python sedikit kurang dalam hal ini sehingga saya menimpanya dalam modul util saya.

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
Saya pikir Anda ingin output = []dan menukar garis dalam while cmp(...)loop. Bandingkan range(0,10,1)dan _range(0,10,1).
sigfpe

3
Saya pikir jawaban ini akan lebih baik jika Anda menamai fungsinya date_range.
Brandon Bradley

7

Berdasarkan jawaban yang saya tulis untuk diri sendiri ini:

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

Keluaran:

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

Perbedaannya adalah bahwa saya mendapatkan dateobjek, bukan objek datetime.datetime.


7

Jika ada dua tanggal dan Anda perlu rentang coba

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

Inilah inti yang saya buat, dari kode saya sendiri, ini mungkin membantu. (Saya tahu pertanyaannya terlalu lama, tetapi orang lain dapat menggunakannya)

https://gist.github.com/2287345

(hal yang sama di bawah)

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

Berikut adalah satu liner untuk skrip bash untuk mendapatkan daftar hari kerja, ini adalah python 3. Mudah dimodifikasi untuk apa pun, int pada akhirnya adalah jumlah hari di masa lalu yang Anda inginkan.

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

Berikut adalah varian untuk memberikan tanggal mulai (atau lebih tepatnya, akhir)

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

Berikut adalah varian untuk tanggal mulai dan berakhir yang sewenang-wenang. bukan berarti ini tidak terlalu efisien, tetapi bagus untuk meletakkan for for di skrip 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

Mungkin ingin memperbarui pos jawaban Anda dengan kode di komentar Anda. Python hancur dalam komentar, dan agak membutuhkan format.
Jake Bathman

3

Matplotlib terkait

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

Saya tahu ini telah dijawab, tetapi saya akan meletakkan jawaban saya untuk tujuan historis, dan karena saya pikir itu lurus ke depan.

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

Tentu itu tidak akan memenangkan apa pun seperti golf kode, tapi saya pikir itu elegan.


Dengan arangelangkah-langkah ini cukup bagus, tetapi listOfDatesterdiri dari datetime64 numpy bukannya datetime asli python.
F.Raab

2
Namun Anda dapat menggunakan np.arange(…).astype(dt.datetime)untuk membuat arangedatetime python asli kembali bukan datetime numpy64.
F.Raab

2

Contoh lain yang diperhitungkan maju atau mundur, mulai dari jawaban 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)

memberi

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

dan

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)

memberi

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

Perhatikan bahwa tanggal mulai termasuk dalam pengembalian, jadi jika Anda ingin empat tanggal total, gunakan 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

Generator rentang tanggal bulanan dengan datetimedan dateutil. Sederhana dan mudah dimengerti:

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
Dia ingin kembali, tidak maju .. Jadi endseharusnya base - datetime.timedelta. Selain itu ... Mengapa solusi ini lebih baik daripada yang asli?
frarugi87

0

Dari jawaban di atas saya membuat contoh ini untuk generator tanggal

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)
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.