35

I have two string variables which contain dates in yyyy-mm-dd format as follows :

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

I want to write code that generates all dates in the range date1 to date2. How can this be done in Python?

Joost Verbraeken
  • 883
  • 2
  • 15
  • 30
Joe
  • 351
  • 1
  • 3
  • 3

9 Answers9

56

Pandas is great for time series in general, and has direct support both for date ranges and date parsing (it's automagic).

import pandas as pd
date1 = '2011-05-03'
date2 = '2011-05-10'
mydates = pd.date_range(date1, date2).tolist()

It also has lots of options to make life easier. For example if you only wanted weekdays, you would just swap in bdate_range.

See https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#generating-ranges-of-timestamps

fantabolous
  • 21,470
  • 7
  • 54
  • 51
36

Dates can be compared to each other just like numbers, and you can do date-related math with the datetime.timedelta object. There's no reason to use dateutil here, and there's no reason to hard-code the number of iterations a la 'range(9)'. This really becomes similar to how you'd deal with plain old numbers.

>>> 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
>>> 
jonesy
  • 3,502
  • 17
  • 23
22

Assuming your dates are already as a datetime.date class you can use .fromordinal and .toordinal to create this oneliner.

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

The result is exclusive end_date. Use end_date.toordinal() + 1 for a range inclusive end_date.


You can easily turn code above into a generator by

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

Example

>>> 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']
Elmex80s
  • 3,428
  • 1
  • 15
  • 23
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

Since dateutil is not a standard library, you will have to install it as a separate package. See the documentation for further details regarding the format (especially dayfirst and yearfirst switches).

eumiro
  • 207,213
  • 34
  • 299
  • 261
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

for python 3 use range instead of xrange.

lorenzori
  • 737
  • 7
  • 23
Cédric Julien
  • 78,516
  • 15
  • 127
  • 132
3

I like this one because it is intuitive and it gives an array of date strings.

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)]
)
dstandish
  • 2,328
  • 18
  • 34
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)
badp
  • 11,409
  • 3
  • 61
  • 89
  • 4
    This breaks for days that are not 86,400 epoch seconds long, but I'm no Jon Skeet-y enough to know if this assumption holds true "well enough". – badp Sep 26 '11 at 07:42
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

I'm not too sure whether the parsing of the strings was integral or just the way you started the question. If so, please disregard the answer as oversimplified

sehe
  • 374,641
  • 47
  • 450
  • 633
-4
import pandas as pd
date1 = '2011-05-03'
date2 = '2011-05-10'
pd.date_range(start = date1,end = date2)
  • 2
    Welcome to Stack Overflow! Though we thank you for your answer, it would be better if it provided additional value on top of the other answers. In this case, your answer does not provide additional value, since another user already posted that solution. If a previous answer was helpful to you, you should vote it up instead of repeating the same information. – Toby Speight Feb 26 '20 at 18:32